mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 20:40:43 +00:00
Gitea 令牌可以只给读权限。只读令牌能读仓库、能列发行版, 看起来一切正常,但一推代码就 403、一建发行版也 403。 加了 check_ezhub_scope():开头就用一个**无副作用**的探针确认权限 (对不存在的发行版发 DELETE —— 403 说明缺 write:repository, 404 说明权限过了只是资源不存在),缺权限就立刻停下并给出 明确的修复指引,不让它走到一半才炸。 文档里也写清了这个坑。
88 lines
3.3 KiB
Python
88 lines
3.3 KiB
Python
# -*- coding: utf-8 -*-
|
||
r'''判定 EzHub 令牌到底有没有写权限(无副作用)。
|
||
|
||
方法:对一个**不存在的**资源发写请求。
|
||
* 令牌没有写权限 -> 服务端先拦权限,返回 403
|
||
* 令牌有写权限 -> 通过权限检查,然后发现资源不存在,返回 404
|
||
所以 403 / 404 这个差别就能说明问题,而且不会真的改到任何东西。
|
||
'''
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import sys
|
||
import urllib.error
|
||
import urllib.request
|
||
|
||
TOKEN = sys.argv[1] if len(sys.argv) > 1 else ""
|
||
if not TOKEN:
|
||
print(" 用法: python probe_token.py <令牌>")
|
||
raise SystemExit(1)
|
||
|
||
BASE = "https://git.261826.xyz/api/v1"
|
||
HEAD = {"Authorization": "token " + TOKEN, "User-Agent": "ykxmz-probe"}
|
||
|
||
|
||
def probe(method: str, path: str, payload: dict | None = None,
|
||
label: str = "") -> int:
|
||
data = json.dumps(payload).encode() if payload else None
|
||
headers = dict(HEAD)
|
||
if data:
|
||
headers["Content-Type"] = "application/json"
|
||
req = urllib.request.Request(BASE + path, data=data, method=method,
|
||
headers=headers)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||
print(" %-10s %-46s -> %s" % (method, path, resp.status))
|
||
return resp.status
|
||
except urllib.error.HTTPError as exc:
|
||
body = exc.read().decode("utf-8", "replace")[:110]
|
||
print(" %-10s %-46s -> %s %s"
|
||
% (method, path, exc.code, body))
|
||
return exc.code
|
||
except Exception as exc: # noqa: BLE001
|
||
print(" %-10s %-46s -> 异常 %s" % (method, path, exc))
|
||
return 0
|
||
|
||
|
||
print(" 一、读权限(应该 200)")
|
||
probe("GET", "/user")
|
||
probe("GET", "/repos/xmz/ykxmz")
|
||
|
||
print()
|
||
print(" 二、写权限判定(对不存在的资源写)")
|
||
print(" 403 = 没写权限;404 = 有写权限(只是资源不存在)")
|
||
del_code = probe("DELETE", "/repos/xmz/ykxmz/releases/99999999")
|
||
print()
|
||
patch_code = probe("PATCH", "/repos/xmz/ykxmz/releases/99999999",
|
||
{"body": "probe"})
|
||
print()
|
||
post_code = probe("POST", "/repos/xmz/ykxmz/releases",
|
||
{"tag_name": "", "name": "", "body": ""})
|
||
|
||
print()
|
||
print(" 三、结论")
|
||
if del_code == 404 or patch_code == 404:
|
||
print(" ✓ 令牌**有写权限**(返回 404 说明权限过了,只是资源不存在)")
|
||
print(" -> HTTPS push 的 403 是另一个原因,需要再查")
|
||
elif del_code == 403 or patch_code == 403:
|
||
print(" ✗ 令牌**没有写权限**(403)")
|
||
print(" -> 需要重新生成令牌,勾上 write:repository")
|
||
else:
|
||
print(" ? 结果不明确:DELETE=%s PATCH=%s POST=%s"
|
||
% (del_code, patch_code, post_code))
|
||
|
||
print()
|
||
print(" 四、顺带看一下令牌信息")
|
||
try:
|
||
req = urllib.request.Request(BASE + "/user", headers=HEAD)
|
||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||
for name in ("X-Oauth-Scopes", "X-Oauth-Client-Id"):
|
||
value = resp.headers.get(name)
|
||
if value:
|
||
print(" %s: %s" % (name, value))
|
||
me = json.loads(resp.read().decode())
|
||
print(" 用户: %s (id=%s, admin=%s)"
|
||
% (me.get("login"), me.get("id"), me.get("is_admin")))
|
||
except Exception as exc: # noqa: BLE001
|
||
print(" 读取失败: %s" % exc)
|