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:
@@ -14,6 +14,7 @@ import uuid
|
||||
from dataclasses import replace
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import httpx
|
||||
import jwt
|
||||
import pytest
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
@@ -519,3 +520,41 @@ async def test_a_config_that_cannot_be_read_is_not_dialled(monkeypatch) -> None:
|
||||
watcher.cancel()
|
||||
|
||||
assert not started
|
||||
|
||||
|
||||
def test_a_member_the_portal_vouches_for_is_adopted(
|
||||
enrolled: User, # noqa: ARG001 (fixture installs the enrolment)
|
||||
portal_key: rsa.RSAPrivateKey,
|
||||
db: Session,
|
||||
) -> None:
|
||||
"""A share link admits at the portal; the account appears here on arrival."""
|
||||
theirs = portal_token(portal_key, subject="portal-user-7")
|
||||
|
||||
vouched = Mock(
|
||||
status_code=200,
|
||||
json=Mock(
|
||||
return_value={"user_id": "portal-user-7", "email": "invited@example.com"}
|
||||
),
|
||||
)
|
||||
with patch("fluksio.cloud.enroll.httpx.get", return_value=vouched) as get:
|
||||
adopted = user_from_token(db, theirs)
|
||||
assert get.call_args.args[0].endswith("/instance-members/portal-user-7")
|
||||
assert adopted is not None
|
||||
assert adopted.email == "invited@example.com"
|
||||
# Never a superuser, and never able to sign in with a password.
|
||||
assert adopted.is_superuser is False
|
||||
|
||||
# Once mapped, no further asking: the local row is the answer.
|
||||
with patch("fluksio.cloud.enroll.httpx.get") as unused:
|
||||
assert user_from_token(db, theirs) == adopted
|
||||
unused.assert_not_called()
|
||||
|
||||
# Somebody the portal does not vouch for gets nothing, and neither does a
|
||||
# portal that cannot be reached.
|
||||
stranger = portal_token(portal_key, subject="portal-user-8")
|
||||
with patch("fluksio.cloud.enroll.httpx.get", return_value=Mock(status_code=404)):
|
||||
assert user_from_token(db, stranger) is None
|
||||
with patch(
|
||||
"fluksio.cloud.enroll.httpx.get", side_effect=httpx.ConnectError("no route")
|
||||
):
|
||||
assert user_from_token(db, stranger) is None
|
||||
|
||||
Reference in New Issue
Block a user