Make registration start idempotent
This commit is contained in:
parent
e0399cf235
commit
d6873b84ae
4 changed files with 99 additions and 7 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import io
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import unittest
|
||||
|
|
@ -53,6 +54,8 @@ def invoke(
|
|||
if claims is not None:
|
||||
environ["HTTP_X_VERIFIED_OIDC_CLAIMS"] = json.dumps(claims)
|
||||
environ["HTTP_X_USER_ENGINE_PROXY_SECRET"] = marker
|
||||
if path == "/api/v1/public/registrations" and method == "POST":
|
||||
environ["HTTP_IDEMPOTENCY_KEY"] = hashlib.sha256(payload).hexdigest()
|
||||
environ.update(headers or {})
|
||||
captured = {}
|
||||
|
||||
|
|
@ -255,6 +258,9 @@ class PortalApplicationTests(unittest.TestCase):
|
|||
self.assertIn(b"Create your account", html)
|
||||
cookie = page["headers"]["Set-Cookie"]
|
||||
token = re.search(rb'name="csrf_token" value="([^"]+)"', html).group(1).decode()
|
||||
idempotency_key = re.search(
|
||||
rb'name="idempotency_key" value="([^"]+)"', html
|
||||
).group(1).decode()
|
||||
denied, _ = invoke(self.app, "/register", method="POST", form={
|
||||
"csrf_token": "wrong", "username": "new.person",
|
||||
"email": "new@example.test", "client_id": "coulomb-social",
|
||||
|
|
@ -262,7 +268,8 @@ class PortalApplicationTests(unittest.TestCase):
|
|||
}, cookie=cookie)
|
||||
self.assertEqual("403 Forbidden", denied["status"])
|
||||
started, html = invoke(self.app, "/register", method="POST", form={
|
||||
"csrf_token": token, "username": "new.person",
|
||||
"csrf_token": token, "idempotency_key": idempotency_key,
|
||||
"username": "new.person",
|
||||
"email": "new@example.test", "display_name": "New Person",
|
||||
"client_id": "coulomb-social", "tenant": "tenant:coulomb",
|
||||
}, cookie=cookie)
|
||||
|
|
@ -436,6 +443,38 @@ class PortalApplicationTests(unittest.TestCase):
|
|||
)
|
||||
self.assertEqual("400 Bad Request", result["status"])
|
||||
|
||||
def test_public_registration_start_is_idempotent_and_key_is_payload_bound(self):
|
||||
verifier = FakeRegistrationVerification()
|
||||
self.app.registration_verification = verifier
|
||||
self.app.registration_clients = frozenset({"coulomb-social"})
|
||||
self.app.registration_tenants = frozenset({"tenant:coulomb"})
|
||||
body = {
|
||||
"username": "idem.person", "email": "idem@example.test",
|
||||
"client_id": "coulomb-social", "tenant": "tenant:coulomb",
|
||||
}
|
||||
headers = {"HTTP_IDEMPOTENCY_KEY": "registration-key-123456789"}
|
||||
first, _ = invoke(
|
||||
self.app, "/api/v1/public/registrations", method="POST",
|
||||
body=body, headers=headers,
|
||||
)
|
||||
registration_id = verifier.requested.registration_id
|
||||
replay, _ = invoke(
|
||||
self.app, "/api/v1/public/registrations", method="POST",
|
||||
body=body, headers=headers,
|
||||
)
|
||||
conflicting, _ = invoke(
|
||||
self.app, "/api/v1/public/registrations", method="POST",
|
||||
body={**body, "email": "different@example.test"}, headers=headers,
|
||||
)
|
||||
sessions = self.app.service.store.all_registration_sessions()
|
||||
self.assertEqual("202 Accepted", first["status"])
|
||||
self.assertEqual("202 Accepted", replay["status"])
|
||||
self.assertEqual("409 Conflict", conflicting["status"])
|
||||
self.assertEqual(1, len(sessions))
|
||||
self.assertEqual(1, verifier.request_count)
|
||||
self.assertEqual(registration_id, sessions[0].registration_id)
|
||||
self.assertNotIn("registration-key-123456789", repr(sessions[0]))
|
||||
|
||||
def test_public_registration_rate_limit_uses_peer_not_forwarded_header(self):
|
||||
self.app.registration_verification = FakeRegistrationVerification()
|
||||
self.app.registration_rate_limit = 2
|
||||
|
|
@ -896,8 +935,10 @@ class FakeRegistrationVerification:
|
|||
def __init__(self):
|
||||
self.requested = None
|
||||
self.registration_id = None
|
||||
self.request_count = 0
|
||||
|
||||
def request(self, request):
|
||||
self.request_count += 1
|
||||
self.requested = request
|
||||
return RegistrationVerificationReceipt(request_id="vrq_test")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue