feat(privacy): WP-40 — PII kernel (Bsn value object + masked-value atom)

Branded Bsn value object with the elfproef (11-test) checksum in shared/kernel/bsn.ts,
wired into the DigiD login boundary so login does real BSN validation (hint + e2e BSNs
updated to a valid 123456782). Consolidate the pure maskers into shared/kernel/pii.ts
(maskBsn/maskTail/REDACTED); debug-state keeps redactProfile (needs the registratie
BigProfile — boundary). New <app-masked-value> atom (+story) centralises the masked
`.includes('*')` detection + reveal affordance; behandel-scherm refactored onto it.
Session.bsn stays string (persistence boundary drops it for privacy). +specs for bsn/pii.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-23 15:24:59 +02:00
co-authored by Claude Opus 4.8
parent 19f2e9b734
commit 62cb34b60f
18 changed files with 246 additions and 83 deletions
+7 -7
View File
@@ -1,16 +1,16 @@
import { Injectable } from '@angular/core';
import { Result, ok, err } from '@shared/kernel/fp';
import { Result, ok } from '@shared/kernel/fp';
import { parseBsn } from '@shared/kernel/bsn';
import { Session } from '../domain/session';
/** Infrastructure: talks to the (mock) DigiD identity provider. */
@Injectable({ providedIn: 'root' })
export class DigidAdapter {
// ponytail: fake DigiD — any 9-digit BSN authenticates to a fixed identity.
// Swap for a real OIDC redirect flow when there's a backend.
// ponytail: fake DigiD — any elfproef-valid BSN authenticates to a fixed identity.
// Real BSN validation (parseBsn, WP-40) is the trust boundary; swap the fixed identity
// for a real OIDC redirect flow when there's an IdP.
async authenticate(bsn: string): Promise<Result<string, Session>> {
const t = bsn.trim();
if (!/^\d{9}$/.test(t))
return err($localize`:@@validation.bsn:Voer een geldig BSN van 9 cijfers in.`);
return ok({ bsn: t, naam: 'Dr. A. (Anna) de Vries' });
const r = parseBsn(bsn);
return r.ok ? ok({ bsn: r.value, naam: 'Dr. A. (Anna) de Vries' }) : r;
}
}