Use Gitea scope permission mask

This commit is contained in:
mpabi
2026-04-26 21:42:25 +02:00
parent 8c0f95d996
commit e9933d7385
4 changed files with 62 additions and 22 deletions
+2 -1
View File
@@ -168,7 +168,8 @@ logiczny remote tokena.
`token_ref` laczy nazwe tokena z markerem po prawej stronie: `*` oznacza, ze `token_ref` laczy nazwe tokena z markerem po prawej stronie: `*` oznacza, ze
remote i `tokens.json` sa zgodne oraz uprawnienia zostaly wczytane, `R` oznacza remote i `tokens.json` sa zgodne oraz uprawnienia zostaly wczytane, `R` oznacza
token tylko w remote, a `S` token tylko w `tokens.json`. Kolumny `scope`, `org` token tylko w remote, a `S` token tylko w `tokens.json`. Kolumny `scope`, `org`
i `repo` sa maskami uprawnien; bez `*` maja wartosc `?????` albo `????`. i `repo` sa maskami uprawnien; bez `*` maja wartosc `?????????`, `?????`
albo `????`.
Zapis z remote do `tokens.json` oraz odczyt `valid` i uprawnien z API robi Zapis z remote do `tokens.json` oraz odczyt `valid` i uprawnien z API robi
dopiero `tokens update --from remotes`. dopiero `tokens update --from remotes`.
Kolumna `valid` oznacza, czy token zostal zaakceptowany przez API teraz. Kolumna `valid` oznacza, czy token zostal zaakceptowany przez API teraz.
+24 -4
View File
@@ -36,7 +36,7 @@ Aktualny format to `version: 3`. Glownym rekordem jest token. `server`, `user`,
}, },
"user": "u1", "user": "u1",
"valid": "forever", "valid": "forever",
"scope": "+---+", "scope": "-----w---",
"remotes": [ "remotes": [
{ {
"name": "r1", "name": "r1",
@@ -69,7 +69,7 @@ Pola wzbogacane przez API przy `tokens update --from remotes`:
- `user` - login wlasciciela tokena odczytany z API - `user` - login wlasciciela tokena odczytany z API
- `valid` - `forever`, data `expires_at`, `invalid`, `?` albo `!` - `valid` - `forever`, data `expires_at`, `invalid`, `?` albo `!`
- `scope` - maska scope tokena - `scope` - maska scope tokena w kolejnosci `aAimnopru`
- `remotes[].org_perm` - maska praw w organizacji - `remotes[].org_perm` - maska praw w organizacji
- `remotes[].repo_perm` - maska praw w repo - `remotes[].repo_perm` - maska praw w repo
@@ -95,11 +95,31 @@ wartosc `?`, bo launcher nie pokazuje metadanych API dla niesparowanych wpisow.
Naglowki masek: Naglowki masek:
- `scope`: `awrop-` - `scope`: `aAimnopru`
- `org`: `oawrc-` - `org`: `oawrc-`
- `repo`: `oawr--` - `repo`: `oawr--`
Znaki w wartosciach: Kategorie `scope`:
- `a` - activitypub
- `A` - admin
- `i` - issue
- `m` - misc
- `n` - notification
- `o` - organization
- `p` - package
- `r` - repository
- `u` - user
Znaki w `scope`:
- `w` - read/write
- `r` - read
- `-` - no access
- `?` - nie wczytano
- `!` - blad wczytania
Znaki w `org` i `repo`:
- `+` - flaga wlaczona - `+` - flaga wlaczona
- `-` - flaga wylaczona - `-` - flaga wylaczona
+1 -1
View File
@@ -91,7 +91,7 @@
}, },
"scope": { "scope": {
"type": "string", "type": "string",
"pattern": "^[+?!-]{5}$" "pattern": "^[rw?!-]{9}$"
}, },
"expires_at": { "expires_at": {
"type": "string" "type": "string"
+35 -16
View File
@@ -388,6 +388,10 @@ def normalize_remote_record(raw_remote: dict) -> dict | None:
return remote_record return remote_record
def valid_mask(raw_value: str, allowed: str, width: int) -> bool:
return len(raw_value) == width and all(char in allowed for char in raw_value)
def normalize_v3_token(config: WorkspaceConfig, raw_token: dict) -> dict | None: def normalize_v3_token(config: WorkspaceConfig, raw_token: dict) -> dict | None:
if not isinstance(raw_token, dict): if not isinstance(raw_token, dict):
return None return None
@@ -409,10 +413,13 @@ def normalize_v3_token(config: WorkspaceConfig, raw_token: dict) -> dict | None:
"server": server_record, "server": server_record,
"remotes": [], "remotes": [],
} }
for field_name in ["user", "valid", "scope", "expires_at"]: for field_name in ["user", "valid", "expires_at"]:
field_value = raw_token.get(field_name) field_value = raw_token.get(field_name)
if isinstance(field_value, str) and field_value: if isinstance(field_value, str) and field_value:
token_record[field_name] = field_value token_record[field_name] = field_value
scope_value = raw_token.get("scope")
if isinstance(scope_value, str) and valid_mask(scope_value, "rw?!-", 9):
token_record["scope"] = scope_value
raw_remotes = raw_token.get("remotes", []) raw_remotes = raw_token.get("remotes", [])
if isinstance(raw_remotes, dict): if isinstance(raw_remotes, dict):
@@ -1070,13 +1077,13 @@ TOKEN_COLUMNS = [
("token_ref", "token_ref", TOKEN_REF_WIDTH), ("token_ref", "token_ref", TOKEN_REF_WIDTH),
("token", "token", 12), ("token", "token", 12),
("valid", "valid", 19), ("valid", "valid", 19),
("scope_mask", "scope", 6), ("scope_mask", "scope", 9),
("org_mask", "org", 6), ("org_mask", "org", 6),
("repo_mask", "repo", 6), ("repo_mask", "repo", 6),
] ]
TOKEN_SEPARATOR_OVERRIDES = { TOKEN_SEPARATOR_OVERRIDES = {
"scope_mask": "awrop-", "scope_mask": "aAimnopru",
"org_mask": "oawrc-", "org_mask": "oawrc-",
"repo_mask": "oawr--", "repo_mask": "oawr--",
} }
@@ -1250,15 +1257,27 @@ def api_get_json(api_url: str, token_value: str, user_name: str = "", basic: boo
def scope_mask(scopes: list[str]) -> str: def scope_mask(scopes: list[str]) -> str:
scope_set = {scope.lower() for scope in scopes} scope_set = {scope.lower() for scope in scopes}
return "".join( categories = [
[ "activitypub",
"+" if "all" in scope_set else "-", "admin",
"+" if "write:repository" in scope_set else "-", "issue",
"+" if "read:repository" in scope_set else "-", "misc",
"+" if {"read:organization", "write:organization"} & scope_set else "-", "notification",
"+" if "public-only" in scope_set else "-", "organization",
] "package",
) "repository",
"user",
]
values = []
for category in categories:
if f"write:{category}" in scope_set:
values.append("w")
elif f"read:{category}" in scope_set:
values.append("r")
else:
values.append("-")
return "".join(values)
def org_permission_mask(org_permissions: dict) -> str: def org_permission_mask(org_permissions: dict) -> str:
@@ -1296,7 +1315,7 @@ def load_gitea_authz(
result = { result = {
"ok": False, "ok": False,
"valid": "!", "valid": "!",
"scope_mask": "!!!!!", "scope_mask": "!!!!!!!!!",
"org_mask": "!!!!!", "org_mask": "!!!!!",
"repo_mask": "!!!!", "repo_mask": "!!!!",
"user": "", "user": "",
@@ -1326,7 +1345,7 @@ def load_gitea_authz(
scopes = [] scopes = []
result["scope_mask"] = scope_mask([str(scope) for scope in scopes]) result["scope_mask"] = scope_mask([str(scope) for scope in scopes])
except (OSError, ValueError, urlerror.URLError): except (OSError, ValueError, urlerror.URLError):
result["scope_mask"] = "?????" result["scope_mask"] = "?????????"
if user_name and owner: if user_name and owner:
org_url = f"{api_base}/users/{quote(user_name, safe='')}/orgs/{quote(owner, safe='')}/permissions" org_url = f"{api_base}/users/{quote(user_name, safe='')}/orgs/{quote(owner, safe='')}/permissions"
@@ -1394,7 +1413,7 @@ def print_token_item_rows(
) -> None: ) -> None:
endpoint_values = endpoint_values_by_name.get(endpoint, endpoint_column_values(endpoint, None, None)) endpoint_values = endpoint_values_by_name.get(endpoint, endpoint_column_values(endpoint, None, None))
valid_value = store_row.get("valid", "?") if store_row else "?" valid_value = store_row.get("valid", "?") if store_row else "?"
scope_value = store_row.get("scope_mask", "?????") if store_row else "?????" scope_value = store_row.get("scope_mask", "?????????") if store_row else "?????????"
org_value = store_row.get("org_mask", "?????") if store_row else "?????" org_value = store_row.get("org_mask", "?????") if store_row else "?????"
repo_value = store_row.get("repo_mask", "????") if store_row else "????" repo_value = store_row.get("repo_mask", "????") if store_row else "????"
token_rows.append( token_rows.append(
@@ -1410,7 +1429,7 @@ def print_token_item_rows(
"token_ref": format_token_ref(token_id, marker), "token_ref": format_token_ref(token_id, marker),
"token": short_secret(token_value), "token": short_secret(token_value),
"valid": valid_value or "?", "valid": valid_value or "?",
"scope_mask": scope_value or "?????", "scope_mask": scope_value or "?????????",
"org_mask": org_value or "?????", "org_mask": org_value or "?????",
"repo_mask": repo_value or "????", "repo_mask": repo_value or "????",
} }