Prove registration collision safety
This commit is contained in:
parent
b80de5a1f4
commit
a8f190b7b7
2 changed files with 43 additions and 0 deletions
|
|
@ -354,6 +354,42 @@ class PortalApplicationTests(unittest.TestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual("403 Forbidden", replay["status"])
|
self.assertEqual("403 Forbidden", replay["status"])
|
||||||
|
|
||||||
|
def test_public_registration_duplicate_inputs_never_take_over_identity(self):
|
||||||
|
verifier = FakeRegistrationVerification()
|
||||||
|
self.app.registration_verification = verifier
|
||||||
|
self.app.provisioning = FakeProvisioning()
|
||||||
|
self.app.registration_clients = frozenset({"coulomb-social"})
|
||||||
|
self.app.registration_tenants = frozenset({"tenant:coulomb"})
|
||||||
|
self.app.registration_oidc_issuer = "https://kc.example"
|
||||||
|
self.app.registration_password_setup_origins = frozenset({"https://kc.example"})
|
||||||
|
|
||||||
|
def register(username, email):
|
||||||
|
invoke(self.app, "/api/v1/public/registrations", method="POST", body={
|
||||||
|
"username": username, "email": email,
|
||||||
|
"client_id": "coulomb-social", "tenant": "tenant:coulomb",
|
||||||
|
})
|
||||||
|
registration_id = verifier.requested.registration_id
|
||||||
|
verifier.registration_id = registration_id
|
||||||
|
result, _ = invoke(
|
||||||
|
self.app, "/api/v1/public/registrations/verify", method="POST",
|
||||||
|
body={"handle": "x" * 32},
|
||||||
|
)
|
||||||
|
return result, self.app.service.store.registration_session(registration_id)
|
||||||
|
|
||||||
|
first, first_session = register("first.person", "shared@example.test")
|
||||||
|
second, second_session = register("second.person", "shared@example.test")
|
||||||
|
collision, collision_session = register("first.person", "other@example.test")
|
||||||
|
|
||||||
|
self.assertEqual("303 See Other", first["status"])
|
||||||
|
self.assertEqual("303 See Other", second["status"])
|
||||||
|
self.assertNotEqual(first_session.user_id, second_session.user_id)
|
||||||
|
self.assertEqual("409 Conflict", collision["status"])
|
||||||
|
linked = self.app.service.store.find_identity(
|
||||||
|
"https://kc.example", "first.person"
|
||||||
|
)
|
||||||
|
self.assertEqual(first_session.user_id, linked.user_id)
|
||||||
|
self.assertNotEqual(collision_session.user_id, linked.user_id)
|
||||||
|
|
||||||
def test_public_registration_rejects_unregistered_client(self):
|
def test_public_registration_rejects_unregistered_client(self):
|
||||||
self.app.registration_verification = FakeRegistrationVerification()
|
self.app.registration_verification = FakeRegistrationVerification()
|
||||||
self.app.registration_clients = frozenset({"coulomb-social"})
|
self.app.registration_clients = frozenset({"coulomb-social"})
|
||||||
|
|
|
||||||
|
|
@ -197,3 +197,10 @@ verification expiry/replay redaction, peer rate limiting, identity-link
|
||||||
collision, cross-tenant denial, lifecycle deletion, audit/outbox redaction,
|
collision, cross-tenant denial, lifecycle deletion, audit/outbox redaction,
|
||||||
and request correlation. Deployed consumer conformance remains gated on public
|
and request correlation. Deployed consumer conformance remains gated on public
|
||||||
runtime credentials and activation.
|
runtime credentials and activation.
|
||||||
|
|
||||||
|
Duplicate-input conformance now also proves that a verified mailbox reused
|
||||||
|
with a different username creates a distinct local user instead of implicitly
|
||||||
|
linking to an existing account. Reusing an existing provider username fails at
|
||||||
|
the identity-link uniqueness boundary, leaves the original link unchanged, and
|
||||||
|
never transfers that identity to the later registration. The full suite passes
|
||||||
|
131 tests with three environment-dependent skips.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue