fix: retire portal_admin mint-link (dead /portal/enter URL); refresh docstrings; assert revoke route gone

This commit is contained in:
2026-06-16 00:15:09 +00:00
parent f0a13ea2ff
commit 01180d5725
3 changed files with 19 additions and 21 deletions
+14 -18
View File
@@ -12,8 +12,11 @@ only its hash is stored.
python3 backend/portal_admin.py link-project --slug myler --project-number 2567-23 python3 backend/portal_admin.py link-project --slug myler --project-number 2567-23
python3 backend/portal_admin.py link-project --slug myler --project-name "RKM Hall" python3 backend/portal_admin.py link-project --slug myler --project-name "RKM Hall"
# mint a magic access link (FULL URL PRINTED ONCE — copy it now) # mint-link is RETIRED — per-client magic URLs (/portal/enter) no longer exist.
python3 backend/portal_admin.py mint-link --slug myler [--label "Dave's link"] # Client access is now per-PROJECT + password: open the project's page in
# Terra-View → "Portal access" to enable it, generate a password, and copy
# the /portal/p/<token> link. (create-client / link-project / list / revoke
# still operate on the underlying Client/token rows.)
# list clients, their projects, and active links # list clients, their projects, and active links
python3 backend/portal_admin.py list python3 backend/portal_admin.py list
@@ -27,7 +30,6 @@ The printed URL base comes from PORTAL_BASE_URL (default http://localhost:8001).
import os import os
import sys import sys
import uuid import uuid
import secrets
import argparse import argparse
from datetime import datetime from datetime import datetime
@@ -37,7 +39,6 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from backend.database import SessionLocal from backend.database import SessionLocal
from backend.models import Client, ClientAccessToken, Project from backend.models import Client, ClientAccessToken, Project
from backend.portal_auth import hash_token
PORTAL_BASE_URL = os.getenv("PORTAL_BASE_URL", "http://localhost:8001").rstrip("/") PORTAL_BASE_URL = os.getenv("PORTAL_BASE_URL", "http://localhost:8001").rstrip("/")
@@ -87,20 +88,15 @@ def link_project(args):
def mint_link(args): def mint_link(args):
db = SessionLocal() # Retired: the per-client magic URL (/portal/enter/...) was removed when the
try: # portal moved to per-project + password access. Minting a token here would
c = _get_client(db, args.slug) # only produce a dead link.
raw = secrets.token_urlsafe(32) sys.exit(
tok = ClientAccessToken(id=str(uuid.uuid4()), client_id=c.id, "mint-link is retired: per-client magic URLs (/portal/enter/...) no longer exist.\n"
token_hash=hash_token(raw), label=args.label) "Client access is now per-project + password. In Terra-View, open the project's page →\n"
db.add(tok) "'Portal access' to enable the portal, generate a password, and copy the /portal/p/<token>\n"
db.commit() "link to send the client."
print(f"✓ Minted access link for '{c.name}'" )
f"{f' ({args.label})' if args.label else ''} — token id {tok.id}")
print("\n COPY THIS NOW (shown only once):\n")
print(f" {PORTAL_BASE_URL}/portal/enter/{raw}\n")
finally:
db.close()
def revoke(args): def revoke(args):
+4 -3
View File
@@ -1,9 +1,10 @@
""" """
Client portal — read-only, scoped client view (see docs/CLIENT_PORTAL.md). Client portal — read-only, scoped client view (see docs/CLIENT_PORTAL.md).
M1: a client opens a magic URL (/portal/enter/{token}) which mints a signed A client opens a per-project secure link (/portal/p/{link_token}), enters the
session cookie, then sees their locations (overview) and per-location read-only shared password, and gets a signed session cookie scoped to that project; they
live data sourced from SLMM's cache. Every data route re-checks ownership. then see that project's locations (overview) and per-location read-only live
data sourced from SLMM's cache. Every data route re-checks ownership.
""" """
import os import os
+1
View File
@@ -10,6 +10,7 @@ def test_portal_link_endpoints_are_gone(client, db_session):
p = make_project(db_session) p = make_project(db_session)
assert client.post(f"/projects/{p.id}/portal-link").status_code == 404 assert client.post(f"/projects/{p.id}/portal-link").status_code == 404
assert client.get(f"/projects/{p.id}/portal-links").status_code == 404 assert client.get(f"/projects/{p.id}/portal-links").status_code == 404
assert client.post(f"/projects/{p.id}/portal-link/sometoken/revoke").status_code == 404
def test_preview_still_mints_a_session(client, db_session): def test_preview_still_mints_a_session(client, db_session):