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>
33 lines
1.4 KiB
TypeScript
33 lines
1.4 KiB
TypeScript
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('*'));
|
|
}
|