Adopt a member the portal vouches for

A share link admits somebody at the portal, so this instance first hears of
them when they arrive rather than when a superuser types their code in.
An unmapped portal identity is now checked once against the portal's own
list of who may reach this instance and given an ordinary local account
only if the portal vouches for it.

Asking rather than believing the token is the point: a token stays signed
and valid until it expires, so trusting its claims would let one rebuild
the account somebody deleted here and deleting a user would stop being the
whole of the revocation.

The account-making itself moved out of the route, since both ways in build
the same thing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GPMNwB2mGBP5j7dXRopcPH
This commit is contained in:
2026-09-02 17:50:29 +02:00
co-authored by Claude Opus 5
parent 058f16ec1d
commit 65272a135f
6 changed files with 181 additions and 42 deletions
+16 -5
View File
@@ -11,6 +11,7 @@ from pydantic import ValidationError
from sqlmodel import Session, select
from fluksio.cloud import config as cloud_config
from fluksio.cloud import enroll
from fluksio.core import security
from fluksio.core.config import settings
from fluksio.core.db import engine
@@ -233,15 +234,25 @@ def _user_for(session: Session, token_data: TokenPayload) -> User | None:
"""The local account a payload names, by id or by portal identity.
A token the portal minted names a person on the portal, not a user here, so
the mapping a superuser made when they admitted them is what turns one into
the other. No mapping, no user — the caller answers that the same way it
answers a token naming a deleted account, which is what makes deleting the
local user the whole of the revocation.
a mapping is what turns one into the other. There are two ways one comes to
exist: a superuser typed their code in, which makes the account up front,
or the owner shared a link from the portal, which admits them there and
leaves this instance to find out when they first arrive. So an unmapped
identity is checked once against the portal's own list of who may reach
this instance, and adopted only if the portal vouches for it.
Deleting the local user stays the whole of the revocation: the portal is
asked rather than the token believed, so a still-valid token cannot rebuild
the account it named, and a person dropped at the portal is not adopted
again.
"""
if token_data.portal_sub:
return session.exec(
user = session.exec(
select(User).where(User.portal_sub == token_data.portal_sub)
).first()
if user is None:
return enroll.adopt_member(session, token_data.portal_sub)
return user
if not token_data.sub:
return None
try: