diff --git a/README.md b/README.md index d7b7f1b..269f52f 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,8 @@ logiczny remote tokena. `token_ref` laczy nazwe tokena z markerem po prawej stronie: `*` oznacza, ze 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` -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 dopiero `tokens update --from remotes`. Kolumna `valid` oznacza, czy token zostal zaakceptowany przez API teraz. diff --git a/doc/tokens.md b/doc/tokens.md index 580c577..6711b0a 100644 --- a/doc/tokens.md +++ b/doc/tokens.md @@ -36,7 +36,7 @@ Aktualny format to `version: 3`. Glownym rekordem jest token. `server`, `user`, }, "user": "u1", "valid": "forever", - "scope": "+---+", + "scope": "-----w---", "remotes": [ { "name": "r1", @@ -69,7 +69,7 @@ Pola wzbogacane przez API przy `tokens update --from remotes`: - `user` - login wlasciciela tokena odczytany z API - `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[].repo_perm` - maska praw w repo @@ -95,11 +95,31 @@ wartosc `?`, bo launcher nie pokazuje metadanych API dla niesparowanych wpisow. Naglowki masek: -- `scope`: `awrop-` +- `scope`: `aAimnopru` - `org`: `oawrc-` - `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 wylaczona diff --git a/doc/tokens.schema.json b/doc/tokens.schema.json index 7ef8226..5c71bea 100644 --- a/doc/tokens.schema.json +++ b/doc/tokens.schema.json @@ -91,7 +91,7 @@ }, "scope": { "type": "string", - "pattern": "^[+?!-]{5}$" + "pattern": "^[rw?!-]{9}$" }, "expires_at": { "type": "string" diff --git a/rvctl.py b/rvctl.py index 74a0ec7..3d19acd 100644 --- a/rvctl.py +++ b/rvctl.py @@ -388,6 +388,10 @@ def normalize_remote_record(raw_remote: dict) -> dict | None: 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: if not isinstance(raw_token, dict): return None @@ -409,10 +413,13 @@ def normalize_v3_token(config: WorkspaceConfig, raw_token: dict) -> dict | None: "server": server_record, "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) if isinstance(field_value, str) and 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", []) if isinstance(raw_remotes, dict): @@ -1070,13 +1077,13 @@ TOKEN_COLUMNS = [ ("token_ref", "token_ref", TOKEN_REF_WIDTH), ("token", "token", 12), ("valid", "valid", 19), - ("scope_mask", "scope", 6), + ("scope_mask", "scope", 9), ("org_mask", "org", 6), ("repo_mask", "repo", 6), ] TOKEN_SEPARATOR_OVERRIDES = { - "scope_mask": "awrop-", + "scope_mask": "aAimnopru", "org_mask": "oawrc-", "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: scope_set = {scope.lower() for scope in scopes} - return "".join( - [ - "+" if "all" in scope_set else "-", - "+" if "write:repository" in scope_set else "-", - "+" if "read:repository" in scope_set else "-", - "+" if {"read:organization", "write:organization"} & scope_set else "-", - "+" if "public-only" in scope_set else "-", - ] - ) + categories = [ + "activitypub", + "admin", + "issue", + "misc", + "notification", + "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: @@ -1296,7 +1315,7 @@ def load_gitea_authz( result = { "ok": False, "valid": "!", - "scope_mask": "!!!!!", + "scope_mask": "!!!!!!!!!", "org_mask": "!!!!!", "repo_mask": "!!!!", "user": "", @@ -1326,7 +1345,7 @@ def load_gitea_authz( scopes = [] result["scope_mask"] = scope_mask([str(scope) for scope in scopes]) except (OSError, ValueError, urlerror.URLError): - result["scope_mask"] = "?????" + result["scope_mask"] = "?????????" if user_name and owner: 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: endpoint_values = endpoint_values_by_name.get(endpoint, endpoint_column_values(endpoint, None, None)) 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 "?????" repo_value = store_row.get("repo_mask", "????") if store_row else "????" token_rows.append( @@ -1410,7 +1429,7 @@ def print_token_item_rows( "token_ref": format_token_ref(token_id, marker), "token": short_secret(token_value), "valid": valid_value or "?", - "scope_mask": scope_value or "?????", + "scope_mask": scope_value or "?????????", "org_mask": org_value or "?????", "repo_mask": repo_value or "????", }