From e44ee3ab2d8be860a719cd6be0fe03e1a34382d1 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 7 Jan 2026 20:02:03 -0300 Subject: [PATCH] comms: fix guest registration via MAS admin API --- .../guest-register-configmap.yaml | 89 ++++++++++--------- .../guest-register-deployment.yaml | 8 +- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/services/communication/guest-register-configmap.yaml b/services/communication/guest-register-configmap.yaml index b051a10..804c7d7 100644 --- a/services/communication/guest-register-configmap.yaml +++ b/services/communication/guest-register-configmap.yaml @@ -14,11 +14,14 @@ data: from urllib import error, parse, request MAS_BASE = os.environ.get("MAS_BASE", "http://matrix-authentication-service:8080").rstrip("/") + MAS_ADMIN_API_BASE = os.environ.get("MAS_ADMIN_API_BASE", "http://matrix-authentication-service:8081/api/admin/v1").rstrip("/") + SYNAPSE_BASE = os.environ.get("SYNAPSE_BASE", "http://othrys-synapse-matrix-synapse:8008").rstrip("/") SERVER_NAME = os.environ.get("MATRIX_SERVER_NAME", "live.bstein.dev") MAS_ADMIN_CLIENT_ID = os.environ["MAS_ADMIN_CLIENT_ID"] MAS_ADMIN_CLIENT_SECRET_FILE = os.environ.get("MAS_ADMIN_CLIENT_SECRET_FILE", "/etc/mas/admin-client/client_secret") MAS_ADMIN_SCOPE = os.environ.get("MAS_ADMIN_SCOPE", "urn:mas:admin") + SESSION_TTL_SEC = int(os.environ.get("SESSION_TTL_SEC", "43200")) RATE_WINDOW_SEC = int(os.environ.get("RATE_WINDOW_SEC", "60")) RATE_MAX = int(os.environ.get("RATE_MAX", "30")) @@ -93,51 +96,52 @@ data: _admin_token_at = now return _admin_token - def _gql(admin_token, query, variables): - status, payload = _json( - "POST", - f"{MAS_BASE}/graphql", - headers={"Authorization": f"Bearer {admin_token}"}, - body={"query": query, "variables": variables}, - timeout=20, - ) - if status != 200: - raise RuntimeError("gql_http_failed") - if payload.get("errors"): - raise RuntimeError("gql_error") - return payload.get("data") or {} - def _generate_localpart(): return "guest-" + secrets.token_hex(6) def _generate_displayname(): return f"{random.choice(ADJ)}-{random.choice(NOUN)}" - def _add_user(admin_token, username): - data = _gql( - admin_token, - "mutation($input:AddUserInput!){addUser(input:$input){status user{id username}}}", - {"input": {"username": username, "skipHomeserverCheck": True}}, - ) - res = data.get("addUser") or {} - status = res.get("status") - user = res.get("user") or {} - return status, user.get("id"), user.get("username") - - def _set_display_name(admin_token, user_id, displayname): - _gql( - admin_token, - "mutation($input:SetDisplayNameInput!){setDisplayName(input:$input){status}}", - {"input": {"userId": user_id, "displayName": displayname}}, + def _admin_api(admin_token, method, path, body=None): + return _json( + method, + f"{MAS_ADMIN_API_BASE}{path}", + headers={"Authorization": f"Bearer {admin_token}"}, + body=body, + timeout=20, ) - def _create_oauth2_session(admin_token, user_id, scope): - data = _gql( + def _create_user(admin_token, username): + status, payload = _admin_api(admin_token, "POST", "/users", {"username": username}) + if status != 201: + return status, None + user = payload.get("data") or {} + return status, user.get("id") + + def _create_session(admin_token, user_id, scope): + status, payload = _admin_api( admin_token, - "mutation($input:CreateOAuth2SessionInput!){createOauth2Session(input:$input){accessToken}}", - {"input": {"userId": user_id, "scope": scope, "permanent": False}}, + "POST", + "/personal-sessions", + { + "actor_user_id": user_id, + "human_name": "guest session", + "scope": scope, + "expires_in": SESSION_TTL_SEC, + }, + ) + if status != 201: + return None + return (payload.get("data", {}).get("attributes", {}) or {}).get("access_token") + + def _set_display_name(access_token, user_id, displayname): + _json( + "PUT", + f"{SYNAPSE_BASE}/_matrix/client/v3/profile/{parse.quote(user_id, safe='')}/displayname", + headers={"Authorization": f"Bearer {access_token}"}, + body={"displayname": displayname}, + timeout=20, ) - return (data.get("createOauth2Session") or {}).get("accessToken") def _rate_check(ip, now): win, cnt = _rate.get(ip, (now, 0)) @@ -216,21 +220,20 @@ data: mas_user_id = None for _ in range(5): localpart = _generate_localpart() - status, mas_user_id, _ = _add_user(admin_token, localpart) - if status == "ADDED": + status, mas_user_id = _create_user(admin_token, localpart) + if status == 201 and mas_user_id: break mas_user_id = None if not mas_user_id or not localpart: raise RuntimeError("add_user_failed") - try: - _set_display_name(admin_token, mas_user_id, displayname) - except Exception: - pass - - access_token = _create_oauth2_session(admin_token, mas_user_id, "urn:matrix:client:api:*") + access_token = _create_session(admin_token, mas_user_id, "urn:matrix:client:api:*") if not access_token: raise RuntimeError("session_failed") + try: + _set_display_name(access_token, f"@{localpart}:{SERVER_NAME}", displayname) + except Exception: + pass except Exception: return self._send_json(502, {"errcode": "M_UNKNOWN", "error": "guest_provision_failed"}) diff --git a/services/communication/guest-register-deployment.yaml b/services/communication/guest-register-deployment.yaml index 41833b2..00e430c 100644 --- a/services/communication/guest-register-deployment.yaml +++ b/services/communication/guest-register-deployment.yaml @@ -13,7 +13,7 @@ spec: template: metadata: annotations: - checksum/config: guest-register-proxy-4 + checksum/config: guest-register-proxy-5 labels: app.kubernetes.io/name: matrix-guest-register spec: @@ -43,6 +43,12 @@ spec: value: 01KDXMVQBQ5JNY6SEJPZW6Z8BM - name: MAS_ADMIN_CLIENT_SECRET_FILE value: /etc/mas/admin-client/client_secret + - name: MAS_ADMIN_API_BASE + value: http://matrix-authentication-service:8081/api/admin/v1 + - name: SYNAPSE_BASE + value: http://othrys-synapse-matrix-synapse:8008 + - name: SESSION_TTL_SEC + value: "43200" - name: MATRIX_SERVER_NAME value: live.bstein.dev - name: RATE_WINDOW_SEC