diff --git a/src/user_engine/service.py b/src/user_engine/service.py index d9193a0..68a2d0f 100644 --- a/src/user_engine/service.py +++ b/src/user_engine/service.py @@ -355,6 +355,7 @@ class UserEngineService: actor, action="profile.self.update", resource_type="user-engine:user", resource_id=user.user_id, tenant=actor.tenant, correlation_id=correlation_id, target_user_id=user.user_id, + context={"self": True}, ) now = utc_now() updated = replace( diff --git a/tests/test_isolated_mvp.py b/tests/test_isolated_mvp.py index 907dea2..1c72600 100644 --- a/tests/test_isolated_mvp.py +++ b/tests/test_isolated_mvp.py @@ -7,6 +7,7 @@ from user_engine.adapters.local import ( from user_engine.domain import ( AccountStatus, AttributeDefinition, + AuthorizationDecision, AuthorizationEffect, Catalog, CatalogLifecycle, @@ -47,6 +48,44 @@ class IsolatedMvpTests(unittest.TestCase): self.assertEqual(2, len(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): service, _, authorization = _service() session = service.me(human_actor_claims(), correlation_id="corr-me")