0c6fd37ed88c076730bbf3784e18b16fea612b25
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f19185ed81 |
refactor(auth): land Session -> Principal, add MedewerkerAdapter (RB-13)
ADR-0002 SS3 models Zorgverlener/Medewerker as different Principal variants with different login flows. Actor #2 (apps/behandelportal) landed in WP-61/67 and the union never followed: grep -rn "Principal" returned one hit, a comment. Both apps' auth/domain/session.ts stayed byte-identical (`{ bsn, naam }`), so the backoffice's Behandelaar carried a BSN and logged into the backoffice as a citizen, by DigiD, under a fabricated citizen's name (login.page.ts). The divergence ADR-0002 predicted took an orthogonal side door instead (medewerker.interceptor.ts's X-Medewerker/X-Rollen stamp, which never touches SessionStore) -- which is why ssp/auth and bhp/auth still measured as 100%/84% duplicated after ADR-C-006 shared the route guards. RB-09 (landed the day before) made the backend's IIdentityProvider able to say "no identity" and fail closed; this ticket is its named FE half. Each app's auth/domain/session.ts becomes principal.ts, holding the one Principal variant that app actually has an actor for: ssp keeps `{ kind: 'zorgverlener', bsn, naam }` (G1 still strips the BSN before persisting); behandelportal gets `{ kind: 'medewerker', medewerkerId, naam, rollen }` (no BSN to strip -- G2 shape validation only). A new MedewerkerAdapter replaces DigidAdapter in behandelportal, resolving the existing MEDEWERKER_ID/currentRollen() dev stand-in into a Principal; because there is no credential to check, it returns Principal directly rather than a Result whose error variant could never occur. login.page.ts stops being a BSN/wachtwoord form -- one explainer line and an "Inloggen met SSO" button -- and its dead error-handling branch goes with the Result wrapper that justified it. Measured with tools/baseline-scan.mjs --dup: auth duplication drops from 168/168 (ssp) and 168/200 (bhp) to 32/179 and 32/259 -- under the backlog's <40 target. What remains is the ADR-C-006 route-guard re-export (deliberately identical), generic test/story-file boilerplate, and one shared fragment of the root-singleton-store idiom -- not re-converged identity or login-flow logic. SS3's prediction that the two actors would authenticate differently enough to justify not sharing auth has now actually been tested, not just asserted, and held. Also: renamed Session.bsn to Principal.bsn in two doc comments (libs/shared/src/infrastructure/subject.ts, subject.interceptor.ts) that cited the old type name; regenerated libs/shared/docs/behaviour-spec.mdx (generated file, per its own banner); recorded the resolution in ADR-0002 as a new amendment, replacing its "Known debt" section. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
de349e702e |
test(auth): extract and spec the stored-session parse boundary (RB-10)
SessionStore.restore() — identical in both apps — read localStorage itself
and did the parse plus shape validation in the same module-private function,
invoked from a field initializer, so the storage read happened the instant
the singleton was constructed and no spec could feed it a raw string. The
logic it guards is a trust boundary, not incidental validation: the comment
above it names G1 (never persist the BSN) and G2 (validate the shape before
trusting it), and CLAUDE.md mandates a spec for boundary parse* adapters.
ssp/auth and bhp/auth were jointly the worst-covered frontend modules.
parseStoredSession(raw) moves into each app's auth/domain/session.ts, which
is pure TS and already had a spec, so no new scaffolding was needed;
restore() collapses to one line. Four cases: absent, non-JSON, wrong shape,
and — BIO-017's addition — a stored {"bsn":…,"naam":…} restoring with bsn
'', which makes the G1 guarantee executable rather than merely commented.
Verified red without the fix.
Landed twice, once per app, deliberately. TE-001 and BL-002 both say an
extract-to-shared here would contradict ADR-0002, which models the two
actors as different Principal variants and expects the two auth contexts to
diverge; RB-13 is what differentiates them.
Also specs redactProfile (BIO-017's second half) — a pure exported
PII-redaction function that had none.
behaviour-spec.mdx is regenerated, which also picks up the test names RB-07
added; that commit should have carried them and did not.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f2d4c900b4 |
refactor(auth): share the actor-agnostic route guards (ADR-C-006)
authGuard and capabilityGuard were duplicated byte-for-byte across both
apps, along with their specs — 57 of the 211 duplicated lines BL-002
measured in the two auth contexts, the largest block after session.store.ts.
They are not actor-specific. They ask "is anyone logged in" and "may they do
X", never "who are you or how did you get here". ADR-0002 §3's non-sharing
decision scopes to identity and login flow — Principal, DigiD vs employee
SSO — and a route guard is neither; §Consequences names auth.guard.ts only
as a seam that localises the change, not as something that must be
duplicated.
Moves both to libs/shared/src/application/auth.guard.ts, reading SESSION_PORT
instead of an app-local SessionStore. The port gains one member,
isAuthenticated: Signal<boolean> — free, because both SessionStores already
expose exactly that (session.store.ts:40) and both apps already register
{ provide: SESSION_PORT, useExisting: SessionStore }. The seam existed; it
was just narrower than what it already carried.
Each app keeps a re-export at @auth/auth.guard so app.routes.ts is untouched
— routing asks the auth context for its guards, which is the direction the
boundary should read. The two identical specs collapse into one, plus a case
asserting the guard resolves through the port.
Deliberately NOT merged: session.store.ts, session.ts, digid.adapter.ts,
login-form.component.ts, login.page.ts. Those are identical only because
ADR-C-004 (Session -> Principal) was never executed. Merging them would make
a citizen DigiD/BSN login the backoffice's shared login.
Measured with tools/baseline-scan.mjs: ssp/auth duplicated lines 211 -> 151,
bhp/auth 86.8% -> 82.5%, repo-wide 7.1% -> 6.6%. Both guard clone pairs drop
out of the top-clones list. What remains is exactly the three files
ADR-C-004 should differentiate.
behaviour-spec.mdx regenerated (the spec moved libraries).
Verified: lint, typecheck, dep:check (0 violations, 224 modules), prettier,
ng build --localize for both apps, and 407 tests passing across all four
projects.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
28c0a250e7 |
test(backend): split RuleTests.cs by aggregate, refresh DDD doc (WP-71)
RuleTests.cs held five aggregates' rules as nested classes in one file, misaligned with Domain/<Aggregate>/ and with the Acceptance/Builders/ folder convention WP-70 started. Split into Domain/<Aggregate>RuleTests.cs (pure move — same names, same bodies, same count) plus a new ApplicationRuleTests.cs (the enum invariant moved out of the WebApplicationFactory-booting ApplicationTests.cs, since it's a pure Enum.GetNames check with no business needing a web host) and OrgTemplateRuleTests.cs (RejectDraft had no direct unit test before, only endpoint coverage). libs/shared/docs/layers.mdx still taught the pre-WP-67 shape (six contexts, no apps/libs split, enforcement via ESLint) — updated to the real monorepo structure and to dependency-cruiser as the actual enforcement mechanism. Adds specs for registration.policy.ts's isStatusConsistent (untested; its backend mirror is) and both apps' auth/domain/session.ts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
4133b30e5d |
feat(behandelportal): WP-65a beoordeling detail (read) + fix unreachable medewerker login
CI / changes (pull_request) Successful in 17s
CI / lint (pull_request) Failing after 54s
CI / frontend (pull_request) Successful in 2m38s
CI / storybook-a11y (pull_request) Failing after 3m28s
CI / backend (pull_request) Successful in 2m1s
CI / semgrep (pull_request) Successful in 1m9s
CI / e2e (pull_request) Successful in 2m55s
CI / api-client-drift (pull_request) Successful in 2m1s
New GET /beoordeling/{id} shows one aanvraag's status, linked documents, and a
canBesluiten decision flag, gated by the same CanBeoordelen capability as the
werkvoorraad list. Reads through IZaakSource.ListCases rather than a new seam
method (WP-66 needs one anyway for the real write); owner BSN is masked.
Fixes a real gap found while wiring this up: the behandelportal's login was still
WP-61's copied citizen/BSN DigiD flow, so nothing ever sent X-Medewerker and the
werkvoorraad screen (WP-64) always denied in a real browser. A dev-only
medewerkerInterceptor (mirrors the existing ?role= stand-in as ?rollen=) fixes that.
WP-65's own Risks note authorized splitting read from write across sessions given
its size; this is the read half. The decision-recording mutation is next (65b).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
e7156c5132 |
feat(WP-67): merge behandelportal into this repo as a monorepo
Restructures into apps/ssp + apps/behandelportal (two Angular projects) plus libs/shared + libs/beheer (cross-app libraries), replacing WP-61's separate sibling repo. That split had already produced real drift: a hand-vendored copy of the backend's OpenAPI doc, a shared/ui+layout tree forked and silently diverging (7 files), and beheer + the styles.scss token bridge duplicated byte-for-byte across both repos. - git mv the SSP's src/app/* into apps/ssp/; fold shared/, beheer/, environments/, the Storybook docs/*.mdx, and styles.scss into libs/shared + libs/beheer (all confirmed identical between the two repos before merging). auth stays deliberately duplicated per ADR-0002 (actor-specific, expected to diverge) - amended there. - One generated API client (libs/shared), no more vendored swagger.json. - .dependency-cruiser split into a base factory + one config per app, and Storybook into .storybook-ssp/.storybook-behandelportal - both forced by the @auth/* alias resolving to different directories per app. - SiteHeaderComponent/ShellComponent gained HEADER_NAV_ITEMS/ HEADER_ADMIN_LINKS/DEBUG_PANEL injection tokens so each app supplies its own nav/admin-links/dev-panel instead of one being hardcoded. - CLAUDE.md, ARCHITECTURE.md, dependencies.md, and ADR-0002 updated; WP-67 backlog entry documents the full decision trail. npm run ci green (lint, dep:check x2, 360 tests across ssp/ behandelportal/shared/beheer, both localized builds, backend tests, snippet + api-client drift); both dev servers, both Storybook instances, and docker compose verified working. The old sibling repo (/home/eho/repos/behandelportal) is left untouched, not deleted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |