From 5e37e9c012851e516addd648683f25136cc5a2b0 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 14 Aug 2026 16:16:59 +0200 Subject: [PATCH] Authorize self identity links by stored ownership --- src/user_engine/service.py | 5 +++++ tests/test_isolated_mvp.py | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/user_engine/service.py b/src/user_engine/service.py index 763cb89..9395ea0 100644 --- a/src/user_engine/service.py +++ b/src/user_engine/service.py @@ -1948,6 +1948,7 @@ class UserEngineService: return existing correlation_id = correlation_id or new_id("corr") + actor_identity = self.store.find_identity(*actor.identity_key) decision = self._authorize( actor, action="identity.link", @@ -1956,6 +1957,10 @@ class UserEngineService: tenant=actor.tenant, correlation_id=correlation_id, target_user_id=user_id, + context={ + "self": actor_identity is not None + and actor_identity.user_id == user_id, + }, ) identity = ExternalIdentity( identity_id=new_id("idn"), diff --git a/tests/test_isolated_mvp.py b/tests/test_isolated_mvp.py index 9a932c2..eeb7e52 100644 --- a/tests/test_isolated_mvp.py +++ b/tests/test_isolated_mvp.py @@ -42,7 +42,7 @@ class IsolatedMvpTests(unittest.TestCase): self.assertEqual(len(service.outbox_events()), 1) def test_account_lifecycle_and_identity_linking(self): - service, _, _ = _service() + service, _, authorization = _service() session = service.me(human_actor_claims(), correlation_id="corr-me") disabled = service.set_account_status( @@ -67,6 +67,11 @@ class IsolatedMvpTests(unittest.TestCase): self.assertEqual(disabled.status, AccountStatus.DISABLED) self.assertEqual(linked.user_id, session.user.user_id) self.assertEqual(linked_session.user.user_id, session.user.user_id) + link_request = next( + request for request in authorization.requests + if request.action == "identity.link" + ) + self.assertTrue(link_request.context["self"]) self.assertIn( "identity.linked", [event.event_type for event in service.outbox_events()],