diff --git a/docs/project/backlog/README.md b/docs/project/backlog/README.md index 3f0e0d7..c7b2ac9 100644 --- a/docs/project/backlog/README.md +++ b/docs/project/backlog/README.md @@ -43,54 +43,54 @@ WP-19's own file), so it's a separate manual/CI step, not chained into the other Gates land before the work they cover; each lint rule lands in the same WP as the fixes for its existing violations, so every WP ends green. -| WP | Title | Phase | Status | -| ---------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------- | ------- | -| [WP-01](WP-01-axe-ci-gate.md) | Axe-on-every-story CI gate | 0 · gates | done | -| [WP-02](WP-02-check-tokens.md) | Harden `check:tokens` + fix what it catches | 0 · gates | done | -| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done | -| [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | done | -| [WP-05](WP-05-parse-boundaries.md) | Parse-don't-validate closure + MDX | 1 · FP/DDD | done | -| [WP-06](WP-06-typed-async.md) | Generic async template contexts — kill `$any()` | 1 · FP/DDD | done | -| [WP-07](WP-07-brief-idioms.md) | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | done | -| [WP-08](WP-08-store-idiom.md) | One store idiom + machine naming + TEA MDX | 1 · FP/DDD | done | -| [WP-09](WP-09-pure-logic.md) | Pure-logic closure: dates + missing command specs | 1 · FP/DDD | done | -| [WP-10](WP-10-button-fidelity.md) | CIBG button fidelity | 2 · CIBG | done | -| [WP-11](WP-11-markup-fidelity.md) | CIBG markup fidelity: application-link + absent-class triage | 2 · CIBG | done | -| [WP-12](WP-12-datablock.md) | CIBG Datablock for application data | 2 · CIBG | done | -| [WP-13](WP-13-cibg-gap-register.md) | CIBG-gap register + hygiene + MDX | 2 · CIBG | done | -| [WP-14](WP-14-storybook-taxonomy.md) | Storybook taxonomy reorg + Layers MDX | 3 · Storybook | done | -| [WP-15](WP-15-missing-stories.md) | Missing stories: shell + brief components | 3 · Storybook | done | -| [WP-16](WP-16-component-a11y.md) | Component a11y: description wiring + alert role | 4 · a11y | done | -| [WP-17](WP-17-app-a11y.md) | App-level a11y: route focus, template lint, WCAG checklist | 4 · a11y | done | -| [WP-18](WP-18-abac-capability-spine.md) | ABAC capability spine (Principal + capabilities, phase P1) | 5 · productie-volwassenheid | done | -| [WP-19](WP-19-e2e-smoke.md) | Playwright e2e smoke | 5 · productie-volwassenheid | done | -| [WP-20](WP-20-second-locale.md) | Second locale proof | 5 · productie-volwassenheid | done | -| [WP-21](WP-21-resilience-seams.md) | Resilience seams (correlation-id, idempotency, retry) | 5 · productie-volwassenheid | done | -| [WP-22](WP-22-durable-persistence.md) | Durable persistence (optional tier) | 5 · productie-volwassenheid | done | -| [WP-23](WP-23-org-template-backend.md) | Org-template backend + admin role | 6 · Brief v2 | done | -| [WP-24](WP-24-letter-canvas.md) | Letter canvas (edit on the letter) | 6 · Brief v2 | done | -| [WP-25](WP-25-letter-preview-html.md) | Server-rendered letter preview (HTML; PDF deferred) | 6 · Brief v2 | done | -| [WP-26](WP-26-org-template-editor.md) | Admin org-template editor | 6 · Brief v2 | done | -| [WP-27](WP-27-brief-ux-layer.md) | Brief UX layer (undo/redo, standaardbrief, diff) | 6 · Brief v2 | done | -| [WP-28](WP-28-brief-v2-demo-polish.md) | Brief v2 demo polish (scenarios, e2e, docs) | 6 · Brief v2 | todo | -| [WP-29](WP-29-stamdata-beheer-editor.md) | Stamdata beheer editor (low-code, PR-emitting) | follow-on · ADR-0004 | done | -| [WP-30](WP-30-ci-perf-followups.md) | CI performance follow-ups (node_modules cache, runner image, path filters) | follow-on · CI/infra | todo | -| [WP-31](WP-31-shared-store-helpers.md) | Shared store helpers (ActionState/SaveState, history, debounced-save, RemoteData) | 7 · refinements | done | -| [WP-32](WP-32-stamdata-undo.md) | Undo/redo in the stamdata editor | 7 · refinements | done | -| [WP-33](WP-33-dev-switchers.md) | In-app dev switchers (scenario + role) | 7 · refinements | done | -| [WP-34](WP-34-adres-phone-brp-readonly.md) | Adres: phone field + BRP address read-only | 7 · refinements | done | -| [WP-35](WP-35-one-concept-per-type.md) | One Concept per case type (server-enforced) | 7 · refinements | done | -| [WP-36](WP-36-admin-cases.md) | Admin cases page + admin delete | 7 · refinements | done | -| [WP-37](WP-37-dev-switcher-reset.md) | Dev-switcher reset fix (scenario/role URL param) | 8 · platform/DX/showcase | done | -| [WP-38](WP-38-dependency-graph-boundaries.md) | Dependency graph + declarative boundaries (visualize + enforce) | 8 · platform/DX/showcase | done | -| [WP-39](WP-39-showcase-snippets-animations.md) | Showcase: linked code snippets + teaching animations | 8 · platform/DX/showcase | done | -| [WP-40](WP-40-pii-kernel.md) | PII kernel: branded `Bsn` VO (elfproef) + masked-value atom | 8 · platform/DX/showcase | done | -| [WP-41](WP-41-persisted-authz-audit.md) | Persisted, queryable authz/PII-reveal audit (no PII) | 8 · platform/DX/showcase | done | -| [WP-42](WP-42-privacy-security-showcase.md) | Privacy & security showcase page (mask + no-PII log) | 8 · platform/DX/showcase | partial | -| [WP-43](WP-43-scaffold-generators.md) | Runnable generators: value-object / form-machine / bff-endpoint / ui-component | 8 · platform/DX/showcase | todo | -| [WP-44](WP-44-context-generator.md) | Runnable generator: `gen:context` | 8 · platform/DX/showcase | todo | -| [WP-45](WP-45-create-ssp-generator.md) | `create-ssp` bootstrap generator (mechanise new-ssp) | 8 · platform/DX/showcase | todo | -| [WP-46](WP-46-vitest-coverage.md) | Vitest coverage (report + report-only thresholds) | 8 · platform/DX/showcase | done | +| WP | Title | Phase | Status | +| ---------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------- | ------ | +| [WP-01](WP-01-axe-ci-gate.md) | Axe-on-every-story CI gate | 0 · gates | done | +| [WP-02](WP-02-check-tokens.md) | Harden `check:tokens` + fix what it catches | 0 · gates | done | +| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done | +| [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | done | +| [WP-05](WP-05-parse-boundaries.md) | Parse-don't-validate closure + MDX | 1 · FP/DDD | done | +| [WP-06](WP-06-typed-async.md) | Generic async template contexts — kill `$any()` | 1 · FP/DDD | done | +| [WP-07](WP-07-brief-idioms.md) | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | done | +| [WP-08](WP-08-store-idiom.md) | One store idiom + machine naming + TEA MDX | 1 · FP/DDD | done | +| [WP-09](WP-09-pure-logic.md) | Pure-logic closure: dates + missing command specs | 1 · FP/DDD | done | +| [WP-10](WP-10-button-fidelity.md) | CIBG button fidelity | 2 · CIBG | done | +| [WP-11](WP-11-markup-fidelity.md) | CIBG markup fidelity: application-link + absent-class triage | 2 · CIBG | done | +| [WP-12](WP-12-datablock.md) | CIBG Datablock for application data | 2 · CIBG | done | +| [WP-13](WP-13-cibg-gap-register.md) | CIBG-gap register + hygiene + MDX | 2 · CIBG | done | +| [WP-14](WP-14-storybook-taxonomy.md) | Storybook taxonomy reorg + Layers MDX | 3 · Storybook | done | +| [WP-15](WP-15-missing-stories.md) | Missing stories: shell + brief components | 3 · Storybook | done | +| [WP-16](WP-16-component-a11y.md) | Component a11y: description wiring + alert role | 4 · a11y | done | +| [WP-17](WP-17-app-a11y.md) | App-level a11y: route focus, template lint, WCAG checklist | 4 · a11y | done | +| [WP-18](WP-18-abac-capability-spine.md) | ABAC capability spine (Principal + capabilities, phase P1) | 5 · productie-volwassenheid | done | +| [WP-19](WP-19-e2e-smoke.md) | Playwright e2e smoke | 5 · productie-volwassenheid | done | +| [WP-20](WP-20-second-locale.md) | Second locale proof | 5 · productie-volwassenheid | done | +| [WP-21](WP-21-resilience-seams.md) | Resilience seams (correlation-id, idempotency, retry) | 5 · productie-volwassenheid | done | +| [WP-22](WP-22-durable-persistence.md) | Durable persistence (optional tier) | 5 · productie-volwassenheid | done | +| [WP-23](WP-23-org-template-backend.md) | Org-template backend + admin role | 6 · Brief v2 | done | +| [WP-24](WP-24-letter-canvas.md) | Letter canvas (edit on the letter) | 6 · Brief v2 | done | +| [WP-25](WP-25-letter-preview-html.md) | Server-rendered letter preview (HTML; PDF deferred) | 6 · Brief v2 | done | +| [WP-26](WP-26-org-template-editor.md) | Admin org-template editor | 6 · Brief v2 | done | +| [WP-27](WP-27-brief-ux-layer.md) | Brief UX layer (undo/redo, standaardbrief, diff) | 6 · Brief v2 | done | +| [WP-28](WP-28-brief-v2-demo-polish.md) | Brief v2 demo polish (scenarios, e2e, docs) | 6 · Brief v2 | todo | +| [WP-29](WP-29-stamdata-beheer-editor.md) | Stamdata beheer editor (low-code, PR-emitting) | follow-on · ADR-0004 | done | +| [WP-30](WP-30-ci-perf-followups.md) | CI performance follow-ups (node_modules cache, runner image, path filters) | follow-on · CI/infra | todo | +| [WP-31](WP-31-shared-store-helpers.md) | Shared store helpers (ActionState/SaveState, history, debounced-save, RemoteData) | 7 · refinements | done | +| [WP-32](WP-32-stamdata-undo.md) | Undo/redo in the stamdata editor | 7 · refinements | done | +| [WP-33](WP-33-dev-switchers.md) | In-app dev switchers (scenario + role) | 7 · refinements | done | +| [WP-34](WP-34-adres-phone-brp-readonly.md) | Adres: phone field + BRP address read-only | 7 · refinements | done | +| [WP-35](WP-35-one-concept-per-type.md) | One Concept per case type (server-enforced) | 7 · refinements | done | +| [WP-36](WP-36-admin-cases.md) | Admin cases page + admin delete | 7 · refinements | done | +| [WP-37](WP-37-dev-switcher-reset.md) | Dev-switcher reset fix (scenario/role URL param) | 8 · platform/DX/showcase | done | +| [WP-38](WP-38-dependency-graph-boundaries.md) | Dependency graph + declarative boundaries (visualize + enforce) | 8 · platform/DX/showcase | done | +| [WP-39](WP-39-showcase-snippets-animations.md) | Showcase: linked code snippets + teaching animations | 8 · platform/DX/showcase | done | +| [WP-40](WP-40-pii-kernel.md) | PII kernel: branded `Bsn` VO (elfproef) + masked-value atom | 8 · platform/DX/showcase | done | +| [WP-41](WP-41-persisted-authz-audit.md) | Persisted, queryable authz/PII-reveal audit (no PII) | 8 · platform/DX/showcase | done | +| [WP-42](WP-42-privacy-security-showcase.md) | Privacy & security showcase page (mask + no-PII log) | 8 · platform/DX/showcase | done | +| [WP-43](WP-43-scaffold-generators.md) | Runnable generators: value-object / form-machine / bff-endpoint / ui-component | 8 · platform/DX/showcase | todo | +| [WP-44](WP-44-context-generator.md) | Runnable generator: `gen:context` | 8 · platform/DX/showcase | todo | +| [WP-45](WP-45-create-ssp-generator.md) | `create-ssp` bootstrap generator (mechanise new-ssp) | 8 · platform/DX/showcase | todo | +| [WP-46](WP-46-vitest-coverage.md) | Vitest coverage (report + report-only thresholds) | 8 · platform/DX/showcase | done | Sequencing dependencies (stated in the WPs too): 01 before 10–15 (axe covers story churn); 03/04 before 05–09 (boundaries stop new violations during refactors); 06 before 07 (typed diff --git a/docs/project/backlog/WP-42-privacy-security-showcase.md b/docs/project/backlog/WP-42-privacy-security-showcase.md index a7451f9..57cc99a 100644 --- a/docs/project/backlog/WP-42-privacy-security-showcase.md +++ b/docs/project/backlog/WP-42-privacy-security-showcase.md @@ -1,6 +1,15 @@ # WP-42 — Privacy & security showcase page -Status: partial — mask/parse showcase done; audit half pending WP-41 +Status: done (optional Foundations MDX writeup left as a nice-to-have) + +## Audit view (added after WP-41) + +`/beheer/audit` — an admin page (`beheer/ui/audit.page.ts`) reading the WP-41 `GET /admin/audit` +trail through a `beheer` adapter/store (domain `AuditEntry` + trust-boundary parse), rendered as a +read-only table (time/action/resource/decision/role/correlation-id), capability-gated on +`cases:manage`. Added to `ADMIN_LINKS` (so it shows in the header nav + dashboard "Beheer" section) +and to the `role.interceptor` ROLE_AWARE list (else it silently 403s). This closes the audit half. + Phase: 8 — platform/DX/showcase Priority: P2 Depends on: WP-40, WP-41 diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 10818ff..a105472 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -84,6 +84,13 @@ export const routes: Routes = [ loadComponent: () => import('@registratie/ui/admin-cases.page').then((m) => m.AdminCasesPage), }, + { + path: 'beheer/audit', + // Admin-only authz/PII-reveal audit trail (WP-41/42). capabilityGuard denies-by-default + // unless GET /me resolved `cases:manage` (reused for audit read). Backend re-enforces. + canActivate: [capabilityGuard('cases:manage')], + loadComponent: () => import('@beheer/ui/audit.page').then((m) => m.AuditPage), + }, { path: 'concepts', loadComponent: () => import('./showcase/concepts.page').then((m) => m.ConceptsPage), diff --git a/src/app/beheer/application/audit.store.ts b/src/app/beheer/application/audit.store.ts new file mode 100644 index 0000000..96724b1 --- /dev/null +++ b/src/app/beheer/application/audit.store.ts @@ -0,0 +1,32 @@ +import { Injectable, inject, signal } from '@angular/core'; +import { RemoteData } from '@shared/application/remote-data'; +import { AuditEntry } from '@beheer/domain/audit-entry'; +import { AuditAdapter, parseAuditEntries } from '@beheer/infrastructure/audit.adapter'; + +type Err = Error | undefined; + +/** + * Admin view of the persisted authz/PII-reveal audit trail (WP-41/42). One root singleton + * owning the list as a RemoteData signal, parsed at the trust boundary. Read-only. + */ +@Injectable({ providedIn: 'root' }) +export class AuditStore { + private adapter = inject(AuditAdapter); + + private state = signal>({ tag: 'Loading' }); + readonly entries = this.state.asReadonly(); + + async load() { + if (this.state().tag !== 'Success') this.state.set({ tag: 'Loading' }); + try { + const parsed = parseAuditEntries(await this.adapter.list()); + this.state.set( + parsed.ok + ? { tag: 'Success', value: parsed.value } + : { tag: 'Failure', error: new Error(parsed.error) }, + ); + } catch (e) { + this.state.set({ tag: 'Failure', error: e as Error }); + } + } +} diff --git a/src/app/beheer/domain/audit-entry.ts b/src/app/beheer/domain/audit-entry.ts new file mode 100644 index 0000000..cbd2810 --- /dev/null +++ b/src/app/beheer/domain/audit-entry.ts @@ -0,0 +1,10 @@ +/** One authz/PII-reveal audit row as the FE sees it (WP-41 backend → WP-42 view). Pure + type; data-minimised (no PII) by construction on the server. */ +export interface AuditEntry { + at: string; // ISO timestamp + action: string; + resource: string; + decision: 'allow' | 'deny'; + role: string; + correlationId: string; +} diff --git a/src/app/beheer/infrastructure/audit.adapter.ts b/src/app/beheer/infrastructure/audit.adapter.ts new file mode 100644 index 0000000..ec472c7 --- /dev/null +++ b/src/app/beheer/infrastructure/audit.adapter.ts @@ -0,0 +1,45 @@ +import { Injectable, inject } from '@angular/core'; +import { Result, ok, err } from '@shared/kernel/fp'; +import { ApiClient } from '@shared/infrastructure/api-client'; +import type { AuthzAuditDto } from '@shared/infrastructure/api-client'; +import { AuditEntry } from '@beheer/domain/audit-entry'; + +/** + * Infrastructure adapter for the admin authz/PII-reveal audit trail (`GET /admin/audit`, + * WP-41). The single place the ApiClient lives for audit; the store parses at the boundary. + */ +@Injectable({ providedIn: 'root' }) +export class AuditAdapter { + private client = inject(ApiClient); + + list(): Promise { + return this.client.audit(); + } +} + +/** Trust-boundary parse of the audit rows. */ +export function parseAuditEntries(json: unknown): Result { + if (!Array.isArray(json)) return err('audit: not an array'); + const out: AuditEntry[] = []; + for (const item of json) { + if (typeof item !== 'object' || item === null) return err('audit: row not an object'); + const d = item as AuthzAuditDto; + if ( + typeof d.at !== 'string' || + typeof d.action !== 'string' || + typeof d.resource !== 'string' || + typeof d.role !== 'string' || + typeof d.correlationId !== 'string' + ) + return err('audit: missing fields'); + out.push({ + at: d.at, + action: d.action, + resource: d.resource, + decision: d.decision === 'allow' ? 'allow' : 'deny', + role: d.role, + correlationId: d.correlationId, + }); + } + return ok(out); +} diff --git a/src/app/beheer/ui/audit.page.ts b/src/app/beheer/ui/audit.page.ts new file mode 100644 index 0000000..d88f301 --- /dev/null +++ b/src/app/beheer/ui/audit.page.ts @@ -0,0 +1,128 @@ +import { Component, computed, effect, inject } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component'; +import { AlertComponent } from '@shared/ui/alert/alert.component'; +import { ButtonComponent } from '@shared/ui/button/button.component'; +import { ASYNC } from '@shared/ui/async/async.component'; +import { AccessStore } from '@shared/application/access.store'; +import { AuditStore } from '@beheer/application/audit.store'; + +/** + * Admin page: the persisted authz/PII-reveal audit trail (WP-41/42) — data-minimised, no PII. + * Deny-by-default capability gate (`cases:manage`, reused for admin audit read). Read-only table. + */ +@Component({ + selector: 'app-audit-page', + imports: [PageShellComponent, AlertComponent, ButtonComponent, DatePipe, ...ASYNC], + styles: [ + ` + .scroll { + overflow-x: auto; + } + table { + width: 100%; + border-collapse: collapse; + font-size: var(--rhc-text-font-size-sm); + } + th, + td { + text-align: left; + padding: var(--rhc-space-max-sm) var(--rhc-space-max-md); + border-block-end: var(--rhc-border-width-sm) solid var(--rhc-color-cool-grey-200); + white-space: nowrap; + } + th { + font-weight: var(--rhc-text-font-weight-semi-bold); + } + .deny { + color: var(--rhc-color-rood-600, #a30000); + font-weight: var(--rhc-text-font-weight-semi-bold); + } + `, + ], + template: ` + + @if (!access.ready()) { + + } @else if (!canRead()) { + {{ deniedText }} + } @else { + + + {{ failedText }} + {{ retryText }} + + + @if (entries().length === 0) { + {{ emptyText }} + } @else { +
+ + + + + + + + + + + + + @for (e of entries(); track e.at + e.action + e.correlationId) { + + + + + + + + + } + +
{{ colTijd }}{{ colActie }}{{ colResource }}{{ colBesluit }}{{ colRol }}{{ colCid }}
{{ e.at | date: 'short' }}{{ e.action }}{{ e.resource }}{{ e.decision }}{{ e.role }}{{ e.correlationId }}
+
+ } +
+
+ } +
+ `, +}) +export class AuditPage { + protected store = inject(AuditStore); + protected access = inject(AccessStore); + + protected canRead = computed(() => this.access.can('cases:manage')); + protected entries = computed(() => { + const rd = this.store.entries(); + return rd.tag === 'Success' ? rd.value : []; + }); + + protected heading = $localize`:@@audit.heading:Auditlog`; + protected intro = $localize`:@@audit.intro:Toegangs- en inzagebeslissingen (autorisatie en het tonen van afgeschermde gegevens). Vastgelegd zonder persoonsgegevens.`; + protected deniedText = $localize`:@@audit.denied:U hebt geen rechten om de auditlog te bekijken.`; + protected failedText = $localize`:@@audit.failed:De auditlog kon niet worden geladen.`; + protected emptyText = $localize`:@@audit.empty:Nog geen auditregels.`; + protected retryText = $localize`:@@audit.retry:Opnieuw proberen`; + protected colTijd = $localize`:@@audit.col.tijd:Tijd`; + protected colActie = $localize`:@@audit.col.actie:Actie`; + protected colResource = $localize`:@@audit.col.resource:Resource`; + protected colBesluit = $localize`:@@audit.col.besluit:Besluit`; + protected colRol = $localize`:@@audit.col.rol:Rol`; + protected colCid = $localize`:@@audit.col.cid:Correlatie-id`; + + private loadRequested = false; + constructor() { + effect(() => { + if (this.canRead() && !this.loadRequested) { + this.loadRequested = true; + void this.store.load(); + } + }); + } + + protected reload() { + void this.store.load(); + } +} diff --git a/src/app/shared/infrastructure/role.interceptor.ts b/src/app/shared/infrastructure/role.interceptor.ts index 400149e..5b9750b 100644 --- a/src/app/shared/infrastructure/role.interceptor.ts +++ b/src/app/shared/infrastructure/role.interceptor.ts @@ -13,6 +13,7 @@ const ROLE_AWARE = [ '/api/v1/brief', '/api/v1/admin/org-template', '/api/v1/admin/cases', + '/api/v1/admin/audit', '/api/v1/stamdata', '/api/v1/me', ]; diff --git a/src/app/shared/layout/admin-links.ts b/src/app/shared/layout/admin-links.ts index 5ede2e2..294c859 100644 --- a/src/app/shared/layout/admin-links.ts +++ b/src/app/shared/layout/admin-links.ts @@ -31,4 +31,10 @@ export const ADMIN_LINKS: readonly AdminLink[] = [ to: '/beheer/zaken', cap: 'cases:manage', }, + { + label: $localize`:@@header.nav.audit:Auditlog`, + description: $localize`:@@admin.link.audit.desc:Toegangs- en inzagebeslissingen bekijken`, + to: '/beheer/audit', + cap: 'cases:manage', + }, ]; diff --git a/src/locale/messages.en.xlf b/src/locale/messages.en.xlf index 2ca24d7..ce73484 100644 --- a/src/locale/messages.en.xlf +++ b/src/locale/messages.en.xlf @@ -3658,6 +3658,62 @@ Aanvragen Cases + + Auditlog + Audit log + + + Toegangs- en inzagebeslissingen bekijken + View access and disclosure decisions + + + Auditlog + Audit log + + + Toegangs- en inzagebeslissingen (autorisatie en het tonen van afgeschermde gegevens). Vastgelegd zonder persoonsgegevens. + Access and disclosure decisions (authorization and revealing masked data). Recorded without personal data. + + + U hebt geen rechten om de auditlog te bekijken. + You do not have permission to view the audit log. + + + De auditlog kon niet worden geladen. + The audit log could not be loaded. + + + Nog geen auditregels. + No audit entries yet. + + + Opnieuw proberen + Try again + + + Tijd + Time + + + Actie + Action + + + Resource + Resource + + + Besluit + Decision + + + Rol + Role + + + Correlatie-id + Correlation id + Beheer Administration diff --git a/src/locale/messages.xlf b/src/locale/messages.xlf index a9df589..e27a134 100644 --- a/src/locale/messages.xlf +++ b/src/locale/messages.xlf @@ -94,6 +94,90 @@ 13 + + Auditlog + + src/app/beheer/ui/audit.page.ts + 102 + + + + Toegangs- en inzagebeslissingen (autorisatie en het tonen van afgeschermde gegevens). Vastgelegd zonder persoonsgegevens. + + src/app/beheer/ui/audit.page.ts + 103 + + + + U hebt geen rechten om de auditlog te bekijken. + + src/app/beheer/ui/audit.page.ts + 104 + + + + De auditlog kon niet worden geladen. + + src/app/beheer/ui/audit.page.ts + 105 + + + + Nog geen auditregels. + + src/app/beheer/ui/audit.page.ts + 106 + + + + Opnieuw proberen + + src/app/beheer/ui/audit.page.ts + 107 + + + + Tijd + + src/app/beheer/ui/audit.page.ts + 108 + + + + Actie + + src/app/beheer/ui/audit.page.ts + 109 + + + + Resource + + src/app/beheer/ui/audit.page.ts + 110 + + + + Besluit + + src/app/beheer/ui/audit.page.ts + 111 + + + + Rol + + src/app/beheer/ui/audit.page.ts + 112 + + + + Correlatie-id + + src/app/beheer/ui/audit.page.ts + 113 + + toegevoegd @@ -2611,14 +2695,14 @@ Voer een geldig BSN van 9 cijfers in. src/app/shared/kernel/bsn.ts - 17 + 18 Dit is geen geldig BSN (klopt niet met de elfproef). src/app/shared/kernel/bsn.ts - 21 + 23 @@ -2663,6 +2747,20 @@ 30 + + Auditlog + + src/app/shared/layout/admin-links.ts + 35 + + + + Toegangs- en inzagebeslissingen bekijken + + src/app/shared/layout/admin-links.ts + 36 + + Mijn overzicht