diff options
Diffstat (limited to 'packages/meshbay-common/src/meshbay_common/adminop.py')
| -rw-r--r-- | packages/meshbay-common/src/meshbay_common/adminop.py | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/packages/meshbay-common/src/meshbay_common/adminop.py b/packages/meshbay-common/src/meshbay_common/adminop.py index c679718..4379519 100644 --- a/packages/meshbay-common/src/meshbay_common/adminop.py +++ b/packages/meshbay-common/src/meshbay_common/adminop.py @@ -30,6 +30,9 @@ fields it received, the node from the state it stored. They are compared by producing the same bytes, never by trusting a value off the wire. """ +import hashlib +import json + ADMIN_TRANSCRIPT_PREFIX = b"meshbay:admin:v1" # Operations that require node-operator authority. @@ -135,6 +138,46 @@ OP_GROUP_DETACH = "group_detach" ADMIN_CHALLENGE_TTL = 120 # seconds +def structured_subject(fields: dict) -> str: + """ + The subject of an operation whose effect is more than one value. + + Every value the executor acts on is in here, because the signature covers the + subject and nothing else of the request: a root's path alone left whether + every member may write there unsigned. Canonical JSON — sorted keys, no + whitespace, UTF-8 — so `null`, `""` and a value stay distinct, and the + browser's `adminSubject` (static/crypto.js) produces the same bytes. + """ + return json.dumps(fields, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + + +def secret_digest(value: str | None) -> str | None: + """A secret named in a subject without being written there: `None` (leave it + unchanged) and `""` (clear it) as themselves, anything else as its SHA-256.""" + if not value: + return value + return "sha256:" + hashlib.sha256(value.encode()).hexdigest() + + +def root_add_subject(path: str, name: str, kind: str, writable: bool, + removable: bool) -> str: + return structured_subject({"path": path, "name": name, "kind": kind, + "writable": writable, "removable": removable}) + + +def group_attach_subject(name: str, shared_dir: str, writable: bool) -> str: + return structured_subject({"name": name, "shared_dir": shared_dir, + "writable": writable}) + + +def invite_create_subject(user_id: str, username: str) -> str: + return structured_subject({"user_id": user_id, "username": username}) + + +def tmdb_config_subject(token: str | None, language: str | None) -> str: + return structured_subject({"token": secret_digest(token), "language": language}) + + def admin_transcript( op: str, node_pk_b64: str, |