fix: retire portal_admin mint-link (dead /portal/enter URL); refresh docstrings; assert revoke route gone
This commit is contained in:
+14
-18
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user