284 lines
13 KiB
Markdown
284 lines
13 KiB
Markdown
---
|
|
id: USER-WP-0020
|
|
type: workplan
|
|
title: "Production self-service and user administration portal"
|
|
domain: communication
|
|
repo: user-engine
|
|
status: finished
|
|
owner: codex
|
|
topic_slug: netkingdom
|
|
created: "2026-07-27"
|
|
updated: "2026-07-30"
|
|
depends_on:
|
|
- USER-WP-0014
|
|
- USER-WP-0015
|
|
- USER-WP-0019
|
|
state_hub_workstream_id: "35b9c315-e721-4e0b-9710-bad04f8d2519"
|
|
---
|
|
|
|
# USER-WP-0020 - Production self-service and user administration portal
|
|
|
|
Turn the transport-neutral UI contracts from `USER-WP-0014` into a real,
|
|
secure browser application and HTTP API backed by user-engine. The first
|
|
production acceptance case is onboarding
|
|
`bernd.worsch@binky-hedgehog.com` as tenant administrator for
|
|
`tenant:friendly:binky`.
|
|
|
|
user-engine remains authoritative for user-domain records, profiles,
|
|
memberships, invitations, registration sessions, and onboarding state.
|
|
KeyCape/NetKingdom remains authoritative for login, credentials, MFA,
|
|
authentication sessions, and OIDC tokens. flex-auth remains authoritative for
|
|
authorization decisions.
|
|
|
|
## T01 - Ratify the portal boundary and threat model
|
|
|
|
```task
|
|
id: USER-WP-0020-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "9886ac8d-7456-4de0-b019-351dfd74ec20"
|
|
```
|
|
|
|
Define browser/API trust boundaries, public and authenticated routes, role
|
|
projections, CSRF/session/XSS controls, rate limits, account-enumeration
|
|
resistance, invitation abuse controls, audit correlation, privacy retention,
|
|
and recovery behavior. Define an explicit `IdentityProvisioningPort`; the
|
|
domain must not import LLDAP, privacyIDEA, KeyCape, email, or flex-auth clients.
|
|
|
|
Keep enterprise federation, SCIM, corporate directory synchronization, and
|
|
SAML administration out of scope while preserving provider-neutral identity
|
|
links, membership envelopes, provisioning adapters, and external ownership
|
|
metadata for that later stage.
|
|
|
|
## T02 - Implement a production HTTP API
|
|
|
|
```task
|
|
id: USER-WP-0020-T02
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "fdb0c322-3efe-4077-bfba-1648787ef411"
|
|
```
|
|
|
|
Expose versioned APIs for registration start/resume/complete, invitation
|
|
claim, current-user profile, tenant membership, onboarding, tenant-admin user
|
|
management, and platform-admin tenant/user management. Consume verified
|
|
KeyCape claims and require flex-auth decisions for protected writes. Add
|
|
idempotency keys, optimistic concurrency, pagination, structured errors,
|
|
OpenAPI, health/readiness, redacted audit, and transactional outbox behavior.
|
|
|
|
2026-07-28 implementation evidence: the deployed versioned API can create a
|
|
tenant-scoped user and membership, establishes the lifecycle account in the
|
|
requested tenant even for authorized cross-tenant platform operations, and
|
|
invokes a provider-neutral provisioning port with roles, correlation, and a
|
|
required idempotency key. Remaining API breadth and OpenAPI/outbox work keep
|
|
this task in progress.
|
|
|
|
2026-07-29 increment: `IdentityProvisioningPort` now includes provider-neutral
|
|
read-only drift inspection and active reconciliation. The HTTP adapter carries
|
|
the same desired identity envelope used for provisioning, returns only
|
|
machine-readable differences and changes, and retries one transient dependency
|
|
failure under the required idempotency key. NetKingdom's live LLDAP adapter
|
|
proved drift detection, convergence, and replay-safe cleanup. Remaining API
|
|
breadth and OpenAPI/outbox delivery keep this task in progress.
|
|
|
|
## T03 - Implement self-service login and registration UX
|
|
|
|
```task
|
|
id: USER-WP-0020-T03
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "45ed1485-003d-4e5f-99fb-91b1b430f3fa"
|
|
```
|
|
|
|
Build an accessible responsive browser flow for sign in, registration,
|
|
invitation acceptance, email verification status, password-setup handoff,
|
|
MFA-enrollment handoff, consent, profile completion, tenant selection, and
|
|
resumable onboarding. Authentication and factor screens must redirect or
|
|
handoff to NetKingdom-owned surfaces; the portal must never handle provider
|
|
admin credentials or store password/MFA secrets.
|
|
|
|
2026-07-28 increment: the deployed portal presents an HTTPS-only interstitial
|
|
to NetKingdom's 15-minute, single-use password setup surface after provisioning
|
|
and lets an authorized administrator renew that handoff for an already-linked
|
|
identity. The portal never receives the password. Adapter and browser
|
|
regressions are included in the 105-test passing suite. Invitation,
|
|
verification, consent/profile, tenant selection, MFA enrollment, and full
|
|
accessibility breadth remain.
|
|
|
|
## T04 - Implement tenant-admin and platform-admin UX
|
|
|
|
```task
|
|
id: USER-WP-0020-T04
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "16555b68-17ef-4902-bd30-f9a0cfe10f9e"
|
|
```
|
|
|
|
Tenant admins can invite, list, inspect, suspend, reactivate, and remove only
|
|
users and memberships in tenants they administer, assign approved tenant
|
|
roles, resend/expire invitations, and inspect redacted lifecycle/audit state.
|
|
Platform admins can create/manage tenants, bootstrap the first tenant admin,
|
|
inspect cross-tenant lifecycle gaps, and perform explicitly authorized
|
|
recovery actions. Prove ordinary users and tenant admins cannot reach
|
|
platform or other-tenant data.
|
|
|
|
2026-07-28 implementation evidence: the deployed admin surface lists
|
|
tenant-scoped members, creates the user and approved role as a separate
|
|
auditable step, provisions the login through the provider-neutral adapter,
|
|
persists the external subject link, and suspends/reactivates both LLDAP login
|
|
and the tenant account. Browser mutations require an OIDC session and
|
|
per-session CSRF token. Platform tenant creation, invitation/recovery breadth,
|
|
removal, and full cross-tenant conformance remain.
|
|
|
|
## T05 - Add production persistence and operability
|
|
|
|
```task
|
|
id: USER-WP-0020-T05
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "65ff5c96-134a-4ec2-ad92-511f0e5f6f04"
|
|
```
|
|
|
|
Package the service and web assets, use the provider-backed Postgres adapter,
|
|
run migrations safely, expose metrics and structured logs, deliver secrets
|
|
through approved runtime references, implement backups/restore evidence, and
|
|
document rollback and zero-downtime upgrade expectations. Browser state must
|
|
never become authoritative.
|
|
|
|
2026-07-29 increment: the runtime now exposes protected Prometheus readiness
|
|
and aggregate record metrics, rejecting requests without the trusted workload
|
|
marker. NetKingdom deployed a daily atomic Postgres custom-format backup with
|
|
catalog/checksum verification and retention, then restored it into an isolated
|
|
ephemeral PostgreSQL instance and verified four public tables plus the schema
|
|
ledger. A reversible live rollout gate proved rollback to the retained prior
|
|
image and restoration of `user-engine:portal-2bcda7f`. The remaining
|
|
production gaps are off-node backup custody and approved registry publication,
|
|
so this task remains in progress.
|
|
|
|
2026-07-30 follow-up: the portal, provisioner, and prior rollback image are
|
|
published through the workload-scoped Forgejo credential and anonymously
|
|
verified by digest. The live portal and provisioner now use registry digests,
|
|
and rollback/roll-forward no longer depends on containerd retaining local
|
|
tags. An age-encrypted user-engine Postgres dump was uploaded through the
|
|
established activity-core offsite lane with plaintext cleanup.
|
|
|
|
2026-07-30 completion: the approved ops-mason plan moved the portal edge
|
|
marker and provisioner service token into one exact OpenBao workload path,
|
|
delivered through separate bounded AppRoles and namespace-restricted
|
|
ExternalSecret stores. A coordinated rotation converged all three derived
|
|
Secrets before restarting the portal and provisioner, and both Deployments
|
|
returned Ready. Exact-path access succeeds, sibling-path access is denied,
|
|
the derived client/server tokens match, and a post-rotation disposable
|
|
identity reconciliation passed. No secret value entered recorded evidence.
|
|
|
|
## T06 - Security, accessibility, and workflow conformance
|
|
|
|
```task
|
|
id: USER-WP-0020-T06
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "39dc383c-7213-495d-8e12-58e614706afe"
|
|
```
|
|
|
|
Automate positive and negative flows for self-registration, invite-only
|
|
registration where configured, duplicate identities, expired/replayed
|
|
invitations, cross-tenant access, role escalation, CSRF, session expiry,
|
|
provider outage, partial provisioning compensation, audit redaction, WCAG
|
|
keyboard/screen-reader behavior, and mobile/desktop layouts.
|
|
|
|
2026-07-28 evidence: all 101 unit tests pass (three integration skips), mounted
|
|
secret normalization has regression coverage, and a disposable live identity
|
|
proved first create, replay/resume, tenant group projection, and cleanup through
|
|
the restricted deployment path. The complete browser, authorization, failure,
|
|
accessibility, and restore matrix remains.
|
|
|
|
The admin increment adds explicit regression coverage for missing/wrong CSRF,
|
|
required API idempotency, provider-link persistence, and suspension calls.
|
|
|
|
2026-07-29 evidence: the provider-neutral adapter suite now covers drift
|
|
inspection, convergence results, transient retry, and replay-safe identity
|
|
cleanup; the full user-engine suite passes 107 tests with three provider
|
|
integration skips. A live disposable identity was suspended, reported as
|
|
drifted, reconciled to `in_sync`, and removed without leaving directory state.
|
|
|
|
## T07 - Binky production acceptance
|
|
|
|
```task
|
|
id: USER-WP-0020-T07
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "b8d1c048-24d2-416c-908f-e500d63c6865"
|
|
```
|
|
|
|
Through the deployed portal—not a one-shot script—create or claim Binky,
|
|
onboard `bernd.worsch@binky-hedgehog.com`, complete KeyCape login and MFA,
|
|
assign only the Binky tenant-admin membership, and prove denial for
|
|
`tenant:platform`, `tenant:coulomb`, and unrelated tenants. Confirm the same
|
|
admin can invite and lifecycle-manage a disposable Binky test user.
|
|
|
|
2026-07-28: the deployed versioned portal API created the Binky tenant-admin
|
|
user and membership, invoked the provider-neutral provisioning adapter, and
|
|
persisted the LLDAP subject link with `password_setup_required`. A live
|
|
newline-bearing proxy Secret exposed an HTTP transport bug; commit `0ef2ae5`
|
|
normalizes required runtime values, adds regression coverage, passes all 105
|
|
tests (three integration skips), and is deployed on railiance01. The remaining
|
|
reusable first-password gate is now deployed: an authorized portal action
|
|
creates or renews a 15-minute, single-use NetKingdom URL. Live disposable-user
|
|
conformance proved revocation, validation without consumption, OPAQUE password
|
|
registration, directory login, replay denial, and cleanup. Remaining Binky
|
|
acceptance is the human-selected password, MFA enrollment, and tenant
|
|
claim/denial evidence; no one-shot operator password was used.
|
|
|
|
2026-07-29 human acceptance: `bernd.worsch` completed the portal-issued
|
|
password setup, privacyIDEA TOTP enrollment, and a fresh KeyCape
|
|
password-plus-OTP login. Two UX follow-ups were captured from the live journey:
|
|
accept/display the email alias instead of requiring the directory uid wherever
|
|
the provider permits, and make authenticator labels identify the account.
|
|
privacyIDEA's future enrollment label is now `Coulomb: {user}@{realm}` at the
|
|
otpauth issuer/account level; existing wallet entries require a local rename.
|
|
|
|
2026-07-29 follow-up: the first-factor edge now accepts either stable directory
|
|
uid or full email using Authelia's documented LLDAP uid/mail filter. The
|
|
resolved subject remains the uid, so this improves login comprehension without
|
|
changing identity keys.
|
|
|
|
Live `/api/v1/me` acceptance confirmed the canonical KeyCape issuer,
|
|
`tenant:friendly:binky`, Binky-only users/admins groups, roles `user` and
|
|
`tenant-admin`, and AAL2 `pwd` + `otp` assurance with `mfa: true`. Attempts to
|
|
open `tenant:platform` and `tenant:coulomb` administration were both denied.
|
|
|
|
## T08 - Closure and next-stage handoff
|
|
|
|
```task
|
|
id: USER-WP-0020-T08
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "ecbdeef3-a0a9-40d6-9722-5cc650d78a49"
|
|
```
|
|
|
|
Close after the portal is production-operable and the Binky acceptance case
|
|
passes. Record enterprise federation follow-up triggers: customer demand for
|
|
SAML/OIDC federation, SCIM, directory synchronization, just-in-time
|
|
provisioning, or customer-owned identity lifecycle. Do not pre-implement
|
|
those integrations here.
|
|
|
|
## Milestone closure (2026-07-30)
|
|
|
|
The production Binky MVP is complete and live. Its API covers verified current
|
|
user, registration start/completion, tenant-scoped paginated membership
|
|
listing, user creation, provider-neutral provisioning, and suspend/reactivate
|
|
lifecycle with structured errors, correlation, idempotency gates, durable
|
|
transactions, audit, and outbox records. Its browser journey covers KeyCape
|
|
sign-in, provider-owned password setup and MFA, tenant administration, and
|
|
CSRF-protected lifecycle management.
|
|
|
|
Acceptance evidence includes AAL2 login, platform/unrelated-tenant denial,
|
|
provider drift and reconciliation, replay-safe deletion, secret rotation,
|
|
database restore, and deployment rollback/roll-forward. Broader invitation and
|
|
platform recovery APIs, expanded onboarding/administration UX, automated
|
|
outbox delivery, and the full accessibility/provider-outage matrix transfer to
|
|
`USER-WP-0021`; they are not silently treated as implemented here.
|
|
|
|
Enterprise federation remains trigger-driven. Create a separate implementation
|
|
plan only when a tenant requires SAML/OIDC federation, SCIM, directory
|
|
synchronization, just-in-time provisioning, or customer-owned lifecycle.
|