diff --git a/backend/portal_admin.py b/backend/portal_admin.py index 2d8b346..e4ac227 100644 --- a/backend/portal_admin.py +++ b/backend/portal_admin.py @@ -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-name "RKM Hall" - # mint a magic access link (FULL URL PRINTED ONCE — copy it now) - python3 backend/portal_admin.py mint-link --slug myler [--label "Dave's link"] + # mint-link is RETIRED — per-client magic URLs (/portal/enter) no longer exist. + # 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/ link. (create-client / link-project / list / revoke + # still operate on the underlying Client/token rows.) # list clients, their projects, and active links 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 sys import uuid -import secrets import argparse 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.models import Client, ClientAccessToken, Project -from backend.portal_auth import hash_token PORTAL_BASE_URL = os.getenv("PORTAL_BASE_URL", "http://localhost:8001").rstrip("/") @@ -87,20 +88,15 @@ def link_project(args): def mint_link(args): - db = SessionLocal() - try: - c = _get_client(db, args.slug) - raw = secrets.token_urlsafe(32) - tok = ClientAccessToken(id=str(uuid.uuid4()), client_id=c.id, - token_hash=hash_token(raw), label=args.label) - db.add(tok) - db.commit() - 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() + # Retired: the per-client magic URL (/portal/enter/...) was removed when the + # portal moved to per-project + password access. Minting a token here would + # only produce a dead link. + sys.exit( + "mint-link is retired: per-client magic URLs (/portal/enter/...) no longer exist.\n" + "Client access is now per-project + password. In Terra-View, open the project's page →\n" + "'Portal access' to enable the portal, generate a password, and copy the /portal/p/\n" + "link to send the client." + ) def revoke(args): diff --git a/backend/routers/portal.py b/backend/routers/portal.py index 7d941b1..b7ae6b2 100644 --- a/backend/routers/portal.py +++ b/backend/routers/portal.py @@ -1,9 +1,10 @@ """ 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 -session cookie, then sees their locations (overview) and per-location read-only -live data sourced from SLMM's cache. Every data route re-checks ownership. +A client opens a per-project secure link (/portal/p/{link_token}), enters the +shared password, and gets a signed session cookie scoped to that project; they +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 diff --git a/tests/test_retired_routes.py b/tests/test_retired_routes.py index 079caf5..ebd4d31 100644 --- a/tests/test_retired_routes.py +++ b/tests/test_retired_routes.py @@ -10,6 +10,7 @@ def test_portal_link_endpoints_are_gone(client, db_session): p = make_project(db_session) 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.post(f"/projects/{p.id}/portal-link/sometoken/revoke").status_code == 404 def test_preview_still_mints_a_session(client, db_session):