Allow a signed-in person to save their own profile.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 51s
Account journey acceptance / journeys (push) Successful in 11s

Assistant: grok
Assistant-Session: 01a0d25d-d358-7e13-b84a-d007fbb7e34f
This commit is contained in:
tegwick 2026-09-27 02:18:51 +02:00
parent 14aee356cf
commit b488e19f06
2 changed files with 40 additions and 0 deletions

View file

@ -355,6 +355,7 @@ class UserEngineService:
actor, action="profile.self.update", resource_type="user-engine:user", actor, action="profile.self.update", resource_type="user-engine:user",
resource_id=user.user_id, tenant=actor.tenant, resource_id=user.user_id, tenant=actor.tenant,
correlation_id=correlation_id, target_user_id=user.user_id, correlation_id=correlation_id, target_user_id=user.user_id,
context={"self": True},
) )
now = utc_now() now = utc_now()
updated = replace( updated = replace(

View file

@ -7,6 +7,7 @@ from user_engine.adapters.local import (
from user_engine.domain import ( from user_engine.domain import (
AccountStatus, AccountStatus,
AttributeDefinition, AttributeDefinition,
AuthorizationDecision,
AuthorizationEffect, AuthorizationEffect,
Catalog, Catalog,
CatalogLifecycle, CatalogLifecycle,
@ -47,6 +48,44 @@ class IsolatedMvpTests(unittest.TestCase):
self.assertEqual(2, len(me_requests)) self.assertEqual(2, len(me_requests))
self.assertTrue(all(request.context["self"] for request in me_requests)) self.assertTrue(all(request.context["self"] for request in me_requests))
def test_self_profile_save_is_authorized_as_the_signed_in_person(self):
class SelfRequired:
def check(self, request):
if (
request.action == "profile.self.update"
and request.context.get("self") is not True
):
return AuthorizationDecision.for_standalone(
AuthorizationEffect.DENY,
reason="no_matching_role_or_context",
)
return AuthorizationDecision.for_standalone(
AuthorizationEffect.ALLOW, reason="local"
)
def batch_check(self, requests):
return tuple(self.check(request) for request in requests)
service = UserEngineService(
store=InMemoryUserEngineStore(),
identity_adapter=FixtureIdentityClaimsAdapter(),
authorization=SelfRequired(),
)
session = service.me(human_actor_claims(), correlation_id="corr-me")
updated = service.update_self_service_profile(
session.actor,
display_name="Chosen name",
consent_accepted=True,
consent_version="portal-terms-v1",
correlation_id="corr-profile",
)
self.assertEqual("Chosen name", updated.display_name)
self.assertEqual("portal-terms-v1", updated.consent_version)
self.assertIsNotNone(updated.consented_at)
self.assertIsNotNone(updated.profile_completed_at)
def test_account_lifecycle_and_identity_linking(self): def test_account_lifecycle_and_identity_linking(self):
service, _, authorization = _service() service, _, authorization = _service()
session = service.me(human_actor_claims(), correlation_id="corr-me") session = service.me(human_actor_claims(), correlation_id="corr-me")