Portal members: a request that loses the adoption race reads back the winner's user instead of being refused
Docs / docs (push) Successful in 23s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m16s
Playwright Tests / test-playwright (2, 2) (push) Failing after 19s
pre-commit / pre-commit (push) Failing after 2m17s
Test Backend / test-backend (3.12) (push) Successful in 4m51s
Test Backend / test-backend (3.14) (push) Successful in 4m32s
Compose Smoke Test / test-compose (push) Failing after 26s
Playwright Tests / merge-reports (push) Failing after 2m17s
Docs / docs (push) Successful in 23s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m16s
Playwright Tests / test-playwright (2, 2) (push) Failing after 19s
pre-commit / pre-commit (push) Failing after 2m17s
Test Backend / test-backend (3.12) (push) Successful in 4m51s
Test Backend / test-backend (3.14) (push) Successful in 4m32s
Compose Smoke Test / test-compose (push) Failing after 26s
Playwright Tests / merge-reports (push) Failing after 2m17s
This commit is contained in:
@@ -216,11 +216,12 @@ def adopt_member(session: Session, portal_sub: str) -> User | None:
|
||||
return None
|
||||
try:
|
||||
return create_remote_user(session, portal_sub, str(member["email"]))
|
||||
except EnrollError:
|
||||
return None
|
||||
except IntegrityError:
|
||||
except (EnrollError, IntegrityError):
|
||||
# A first page load fires several requests and the websocket at once,
|
||||
# all of them missing the row and all of them creating it. Whoever
|
||||
# loses reads back what the winner wrote.
|
||||
# loses, at the unique index or at the checks before it (the winner
|
||||
# may commit while this one asks the portal), reads back what the
|
||||
# winner wrote. An email another local account holds finds nothing to
|
||||
# read back and stays refused.
|
||||
session.rollback()
|
||||
return session.exec(select(User).where(User.portal_sub == portal_sub)).first()
|
||||
|
||||
@@ -644,6 +644,39 @@ def test_a_member_the_portal_vouches_for_is_adopted(
|
||||
assert user_from_token(db, stranger) is None
|
||||
|
||||
|
||||
def test_a_request_that_loses_the_adoption_race_gets_the_winners_user(
|
||||
enrolled: User, # noqa: ARG001 (fixture installs the enrolment)
|
||||
portal_key: rsa.RSAPrivateKey,
|
||||
db: Session,
|
||||
) -> None:
|
||||
"""A first page load adopts from several requests; none of them is refused."""
|
||||
from fluksio.cloud.enroll import create_remote_user
|
||||
|
||||
theirs = portal_token(portal_key, subject="portal-user-race")
|
||||
|
||||
def vouch_after_the_winner_commits(*_: object, **__: object) -> Mock:
|
||||
# Another request finishes adopting while this one asks the portal.
|
||||
with Session(db.get_bind()) as other:
|
||||
create_remote_user(other, "portal-user-race", "race@example.com")
|
||||
return Mock(
|
||||
status_code=200,
|
||||
json=Mock(
|
||||
return_value={
|
||||
"user_id": "portal-user-race",
|
||||
"email": "race@example.com",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
with patch(
|
||||
"fluksio.cloud.enroll.httpx.get", side_effect=vouch_after_the_winner_commits
|
||||
):
|
||||
adopted = user_from_token(db, theirs)
|
||||
|
||||
assert adopted is not None
|
||||
assert adopted.portal_sub == "portal-user-race"
|
||||
|
||||
|
||||
def test_the_tunnel_opens_the_live_socket_with_a_ticket(
|
||||
client: TestClient, superuser_token_headers: dict[str, str]
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user