List and revoke portal members under Admin → Users

GET /cloud/members asks the portal who it admits to this instance and
marks those with a local user. DELETE /cloud/members/{portal_sub} takes a
membership back. Admin → Users lists the members who have not arrived
yet, with Revoke, which also surfaces a membership left standing when a
local user was deleted while the portal could not be told.
This commit is contained in:
2026-10-15 10:31:25 +02:00
parent 8b47b32d3f
commit 1d66b30d0c
8 changed files with 369 additions and 2 deletions
+55
View File
@@ -717,3 +717,58 @@ def test_a_member_removed_at_the_portal_loses_their_user(db: Session) -> None:
assert db.get(User, boss_id) is not None
db.delete(boss)
db.commit()
def test_admin_lists_and_revokes_portal_members(
client: TestClient,
enrolled: User, # noqa: ARG001 (fixture installs the enrolment)
superuser_token_headers: dict[str, str],
normal_user_token_headers: dict[str, str],
db: Session,
) -> None:
"""Who the portal admits, and which of them have arrived here."""
arrived = create_random_user(db)
arrived.portal_sub = str(uuid.uuid4())
db.add(arrived)
db.commit()
waiting = str(uuid.uuid4())
listed = Mock(
status_code=200,
json=Mock(
return_value={
"data": [
{"user_id": arrived.portal_sub, "email": arrived.email},
{"user_id": waiting, "email": "invited@example.com"},
]
}
),
)
with patch("fluksio.api.routes.cloud.httpx.get", return_value=listed) as get:
members = client.get(
f"{settings.API_V1_STR}/cloud/members", headers=superuser_token_headers
)
assert members.status_code == 200, members.text
assert get.call_args.args[0].endswith("/instance-members/")
assert {row["user_id"]: row["local_user_id"] for row in members.json()} == {
arrived.portal_sub: str(arrived.id),
waiting: None,
}
with patch(
"fluksio.api.routes.cloud.httpx.delete", return_value=Mock(status_code=200)
) as delete:
revoked = client.delete(
f"{settings.API_V1_STR}/cloud/members/{waiting}",
headers=superuser_token_headers,
)
assert revoked.status_code == 200, revoked.text
assert delete.call_args.args[0].endswith(f"/instance-members/{waiting}")
assert (
client.get(
f"{settings.API_V1_STR}/cloud/members", headers=normal_user_token_headers
).status_code
== 403
)
db.delete(arrived)
db.commit()