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:
@@ -0,0 +1,32 @@
|
||||
import { Component, computed, input, output } from '@angular/core';
|
||||
import { ButtonComponent } from '@shared/ui/button/button.component';
|
||||
|
||||
/**
|
||||
* Atom: a possibly-masked sensitive value (BSN, BIG-nummer, …) with an optional, audited
|
||||
* reveal affordance (WP-40). The value arrives masked from the server (data-minimisation)
|
||||
* and is swapped for the full value on reveal; the reveal button shows only when the value
|
||||
* is still masked AND the caller says the principal may reveal it. Centralises the
|
||||
* masked-detection that consumers used to sniff inline. The atom only emits `reveal`; the
|
||||
* caller owns the step-up gesture + the audited fetch (see behandel-scherm).
|
||||
*
|
||||
* ponytail: masked-detection is the mask character (`*`) — a POC heuristic. A server-sent
|
||||
* `masked` boolean would remove the sniff; wire it here without touching consumers.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-masked-value',
|
||||
imports: [ButtonComponent],
|
||||
template: `
|
||||
<span class="value">{{ value() }}</span>
|
||||
@if (canReveal() && masked()) {
|
||||
<app-button variant="subtle" (click)="reveal.emit()">{{ revealLabel() }}</app-button>
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class MaskedValueComponent {
|
||||
value = input.required<string>();
|
||||
canReveal = input(false);
|
||||
revealLabel = input($localize`:@@maskedValue.reveal:Tonen`);
|
||||
reveal = output<void>();
|
||||
|
||||
protected masked = computed(() => this.value().includes('*'));
|
||||
}
|
||||
Reference in New Issue
Block a user