Implement family dataspace onboarding

This commit is contained in:
2026-06-05 18:51:47 +02:00
parent af6d82038e
commit 531c2193a4
13 changed files with 1400 additions and 1 deletions

View File

@@ -11,6 +11,9 @@ HTTP or RPC adapters should preserve these operation names:
`tenant_diagnostics`
- `register_application`, `publish_catalog`
- `set_profile_value`, `effective_profile`, `projection`, `identity_context`
- `onboard_family_dataspace`, `invite_family_member`,
`resend_family_invitation`, `revoke_family_invitation`,
`accept_family_invitation`
- `audit_records`, `outbox_events`
## Identity Context Contract
@@ -36,6 +39,24 @@ policy, control, access-review, exception, and lifecycle task references belong
to adapter contracts and remain non-owned unless a later workplan assigns
source-of-truth responsibility to user-engine.
## Family Dataspace Onboarding Contract
`onboard_family_dataspace` is a convenience facade for personal-family
identity-domain setup. It composes existing user, account, tenant-account,
membership, application, catalog, profile, audit, outbox, projection, and
identity-context operations.
The facade represents a family as a NetKingdom tenant plus a `family` scope. It
does not provision the tenant, issue SSO tokens, own credentials, or implement
the protected dataspace runtime. Family roles are scoped membership facts such
as `owner`, `adult`, `child`, `guest`, and `delegated-caretaker`; authorization
systems decide how those facts affect access.
Invitation acceptance requires already-verified claims. user-engine stores
local invitation lifecycle, links the verified external identity, activates
account state, and returns both `identity_context` and a
`CLAIMS_ENRICHMENT` projection for SSO adapters.
## Error Taxonomy
- `ValidationError`: caller supplied an invalid shape, state transition, or

View File

@@ -66,3 +66,40 @@ operation. Outbox consumers should treat `event_id` as the delivery id and
for event in service.outbox_events():
print(event.event_type, event.aggregate_id, event.correlation_id)
```
## Onboard A Family Dataspace
```python
from user_engine.domain import FamilyDataspaceRequest, FamilyMemberSpec, FamilyRole
owner = service.me(owner_claims, correlation_id="corr-owner")
onboarding = service.onboard_family_dataspace(
owner.actor,
FamilyDataspaceRequest(
tenant="tenant:worsch-family",
family_scope_id="family:worsch",
family_display_name="Worsch Family",
application_id="app.personal-dataspace",
oidc_client_id="personal-dataspace-client",
protected_system_id="dataspace.personal.worsch",
member_specs=(
FamilyMemberSpec(
primary_email="child@example.test",
display_name="Child Member",
role=FamilyRole.CHILD,
),
),
),
correlation_id="corr-family-onboard",
)
accepted = service.accept_family_invitation(
child_claims,
onboarding.invitations[0].invitation.invitation_id,
correlation_id="corr-child-accept",
)
```
`accepted.identity_context` is the canon-facing context for the SSO adapter.
`accepted.claims_projection` is the application-visible profile projection for
the personal dataspace.

View File

@@ -0,0 +1,120 @@
# Family Dataspace Onboarding
Status: implemented MVP facade
Date: 2026-06-05
Related workplan: USER-WP-0008
## Purpose
Family dataspace onboarding is the first concrete convenience use case for
`user-engine` as a NetKingdom identity-domain integration layer. It lets a
consumer represent a family as a tenant-scoped identity context, invite family
members, bind a personal dataspace application, and produce SSO-ready identity
context without making callers sequence low-level user, profile, membership,
application, audit, and projection operations themselves.
## Model
| Use-case concept | user-engine representation | Source of truth |
| --- | --- | --- |
| Family | NetKingdom tenant plus `family` membership scope | NetKingdom tenant/organization infrastructure |
| Family owner | `User`, `Account`, active `TenantAccount`, `family:owner` membership | user-engine for local facts |
| Family member | invited `User`, `Account`, `TenantAccount`, `FamilyInvitation` | user-engine for local lifecycle |
| SSO identity | linked `ExternalIdentity` from verified `(issuer, subject)` | NetKingdom IAM for authentication |
| Family role | scoped `Membership.kind` such as `owner`, `adult`, `child`, `guest` | user-engine fact, authorization consumes it |
| Personal dataspace | registered `Application` with `ApplicationBinding` | user-engine binding, external runtime owns app |
| SSO claims input | `identity_context` plus `CLAIMS_ENRICHMENT` projection | user-engine read model, NetKingdom IAM consumes it |
## Public Flow
1. Resolve the owner through `me(...)` or pass an already-normalized actor.
2. Call `onboard_family_dataspace(...)` with a `FamilyDataspaceRequest`.
3. user-engine ensures the owner exists, registers the dataspace application,
publishes a minimal dataspace catalog, assigns owner membership, creates
pending member invitations, and returns identity context plus a
claims-enrichment projection for SSO.
4. Invited members accept through `accept_family_invitation(...)` using
verified NetKingdom claims. user-engine links the external identity,
activates account state, records audit/outbox events, and returns SSO-ready
context for the member.
5. Pending invitations can be resent or revoked through
`resend_family_invitation(...)` and `revoke_family_invitation(...)`.
## Example
```python
from user_engine.domain import FamilyDataspaceRequest, FamilyMemberSpec, FamilyRole
owner = service.me(owner_claims, correlation_id="corr-owner")
onboarding = service.onboard_family_dataspace(
owner.actor,
FamilyDataspaceRequest(
tenant="tenant:worsch-family",
family_scope_id="family:worsch",
family_display_name="Worsch Family",
application_id="app.personal-dataspace",
oidc_client_id="personal-dataspace-client",
protected_system_id="dataspace.personal.worsch",
member_specs=(
FamilyMemberSpec(
primary_email="child@example.test",
display_name="Child Member",
role=FamilyRole.CHILD,
),
),
),
correlation_id="corr-family-onboard",
)
member = service.accept_family_invitation(
member_claims,
onboarding.invitations[0].invitation.invitation_id,
correlation_id="corr-member-accept",
)
```
`onboarding.identity_context` and `member.identity_context` contain the
canon-facing actor, user, account, authenticated subject, authorization
principal, tenant, family group, membership, grant-like, and evidence
references. `claims_projection` contains application-visible profile values
such as the family display name and member display name.
## Boundary
user-engine does not issue tokens, manage credentials, run MFA, provision the
family tenant, or implement the personal dataspace runtime. Those remain
NetKingdom IAM, tenant, security, and application responsibilities.
Family roles are exported as scoped membership facts. The authorization port
decides whether those facts allow an action.
Invitation tokens and proofing are deliberately adapter-owned. The MVP
invitation record tracks local lifecycle state and assumes NetKingdom IAM has
already verified claims before acceptance.
## Audit And Events
The facade emits high-level events in addition to the lower-level events from
the operations it composes:
- `family_dataspace.onboarded`
- `family_member.invited`
- `family_invitation.resent`
- `family_invitation.revoked`
- `family_invitation.accepted`
Lower-level events such as `user.created`, `tenant_account.status_changed`,
`membership.added`, `identity.linked`, `application.registered`,
`catalog.published`, and `profile.value_set` remain visible for replay and
traceability.
## Current MVP Limits
- Invitations are stored in the current store boundary and need durable-store
backing before production use.
- Invitation delivery, one-time token material, and proofing are external
adapter responsibilities.
- Membership revocation and historical role lifecycle are not yet fully
modeled beyond invitation revoke and account status changes.
- The default dataspace catalog is intentionally minimal and should evolve with
real dataspace claims requirements.

View File

@@ -15,6 +15,7 @@ projection, audit, and event behavior testable without a UI.
| sensitive_redaction | Sensitive values are redacted in runtime and claims-enrichment projections. |
| audit_event_replay | Mutations carry audit records, outbox events, and correlation ids. |
| identity_canon_context | Actor, user, account, authenticated subject, authorization principal, tenant, membership, grant-like facts, and evidence references stay distinguishable. |
| family_dataspace_onboarding | A family tenant can register a personal dataspace, invite members, accept SSO identities, project claims context, and deny cross-family access. |
## Fixture Actors