Logging, audit, securitylogging en technische foutafhandeling
19.1 Doel van dit hoofdstuk
Dit hoofdstuk beschrijft hoe OefenHub technische logging, domeinhistorie, auditinformatie, securitygerelateerde logging, foutcorrelatie en veilige foutafhandeling technisch inricht.
De uitwerking sluit aan op de middel-zware modulaire monoliet uit het Technisch Ontwerp. Logging en audit worden niet als één generieke centrale module behandeld, maar worden verdeeld over de laag of module die eigenaar is van de brongebeurtenis. Security wordt evenmin als apart project gemodelleerd in de basisarchitectuur; securityconfiguratie en securitylogging worden expliciet uitgewerkt binnen Web, Infrastructure, Identity, Authorization, Scheduling en de relevante domeinmodules.
Dit hoofdstuk gaat over technische inrichting. Nieuwe functionele rechten, bewaartermijnen, gebruikerslabels of schermgedrag ontstaan hier niet zelfstandig. Wanneer tijdens implementatie blijkt dat functionele eisen ontbreken of wijzigen, moet dat worden teruggelegd in Functioneel Ontwerp, Software Requirements Specification, database-informatie of schermdocumentatie.
19.2 Afbakening
| Onderwerp | In dit hoofdstuk | Niet in dit hoofdstuk |
|---|---|---|
| Technische logging | Logstructuur, logniveaus, correlation, foutregistratie, privacygrenzen | Concrete hostingprovider of commerciële observability-tool als bindende keuze |
| Audit en domeinhistorie | Technische verwerking en eigenaarschap | Nieuwe auditfunctionaliteit of nieuwe gebruikersrechten |
| Securitylogging | Access denied, verdachte patronen, pipeline- en autorisatie-events | Een apart OefenHub.Security project |
| Foutafhandeling | Exception boundaries, veilige foutrespons, foutpagina's, logging | Nieuwe UX-teksten buiten bestaande scherm- en popupdocumentatie |
| Jobs en retries | Correlation, foutregistratie en herleidbaarheid vanuit jobs | Volledige TickerQ-inrichting; zie Technisch Ontwerp: background jobs, TickerQ en periodieke verwerking |
| Beheer en operatie | Herleidbaarheid, monitoringinput en failed-state analyse | Backup/restore en operationele procedures; zie Technisch Ontwerp: beheerbeleid, monitoring, backup, restore en operatie |
| Privacy en retentie | Loggingbeperkingen en gevoelige data | Definitieve retentie- en anonimiseringregels; zie Technisch Ontwerp: privacy, retentie, anonimisering en gegevensbescherming |
19.3 Ontwerpprincipes
| Principe | Regel |
|---|---|
| Logging is ondersteunend | Logs ondersteunen beheer, diagnose, securityanalyse en reconstructie, maar vervangen geen domeinhistorie of brondata. |
| Domein blijft eigenaar | Domeinspecifieke audit en historie worden opgeslagen bij de module die eigenaar is van de bronmutatie. |
| Geen centrale auditmodule | Er komt geen generiek OefenHub.Audit project in de basis. Audit/historie is domeingericht. |
| Geen apart securityproject | Er komt geen generiek OefenHub.Security project in de basis. Security wordt verdeeld over Web, Infrastructure, Identity, Authorization en domeinspecifieke logging. |
| Gestructureerd loggen | Logs worden als gestructureerde velden geschreven en niet als alleen vrije tekst. |
| Correlation verplicht | Request-, workflow- en jobgerelateerde logs moeten met correlation-identifiers herleidbaar zijn. |
| Privacy by design | Logs bevatten geen credentials, tokens, wachtwoorden, volledige antwoordpayloads, ruwe exportinhoud of onnodige persoonsgegevens. |
| Veilige foutrespons | Gebruikers krijgen veilige, generieke foutinformatie. Technische details blijven in logs. |
| Retry is begrensd | Retrybare fouten worden niet eindeloos herhaald en eindigen beheerbaar in een foutstatus. |
| Bij twijfel beperken | Wanneer onzeker is of informatie gevoelig is, wordt deze niet gelogd of alleen als veilige samenvatting vastgelegd. |
19.4 Begrippen en onderscheid
| Begrip | Betekenis | Voorbeelden |
|---|---|---|
| Technische log | Operationele registratie voor diagnose en beheer. | Exception, requestduur, failed job attempt, dependency failure. |
| Domeinhistorie | Functionele historie binnen één domeinmodule. | TicketHistory, CategoryHistory, ExerciseHistory, RelationshipEvents. |
| Managementlog | Auditlaag voor beheerhandelingen. | Beheerder wijzigt categorie, popup, template of accountstatus. |
| Securitylogging | Registratie van securityrelevante gebeurtenissen. | Access denied, ongeldige rolcontext, verdachte routepoging, mislukte autorisatiecontrole. |
| Correlation | Technische herleidbaarheid over request, workflow, job en modulegrenzen. | CorrelationId, RequestId, JobId, WorkflowId. |
| Foutafhandeling | Vertaling van technische fouten naar veilige respons, popup of foutpagina. | 403, 404, 500, validatiefout, reconnect-fout. |
19.5 Eigenaarschap per logtype
| Logtype | Eigenaar | Persistente opslag | Toelichting |
|---|---|---|---|
| Request-/pipeline-log | OefenHub.Web / hosting | Loggingprovider | Bevat requestpad, statuscode, duur en correlation. Geen bodyinhoud. |
| Applicatie-exception | Module waar exception ontstaat of boundary waar afgehandeld | Loggingprovider | Exceptiondetails worden intern gelogd, maar niet aan gebruiker getoond. |
| Domeinhistorie | Betreffende domeinmodule | Module-eigen schema | Bijvoorbeeld support.TicketHistory of relationships.RelationshipEvents. |
| Beheeractie | OefenHub.Admin of beheercontext-eigenaar | admin schema waar van toepassing | Bijvoorbeeld ManagementLogEntries. |
| Autorisatie/security-event | OefenHub.Authorization, OefenHub.Identity of domeineigenaar | Loggingprovider en eventueel module-eigen tabel | Geen apart security schema in de basis. |
| Joblifecycle-log | OefenHub.Scheduling | scheduling schema en loggingprovider | Pogingen, fouten, retrystatus en technische lifecycle. |
| Live-meekijk-audit | OefenHub.LiveMonitoring | live schema | LiveViewAudit is domeinaudit, geen generieke auditlaag. |
| PDF/export-fout | OefenHub.Reporting of aanroepende module | Loggingprovider; tijdelijke output niet als log | Geen PDF-inhoud of volledige exportpayload in logs. |
19.6 Geen apart audit- of securityproject
19.6.1 Audit
Audit wordt in OefenHub niet ingericht als één centraal project met één generiek datamodel. Veel auditinformatie heeft domeinbetekenis en moet naast de brondata staan zodat reconstructie en beheeranalyse inhoudelijk begrijpelijk blijven.
Voorbeelden:
| Domein | Audit-/historietype | Reden voor domeineigenaarschap |
|---|---|---|
| Relaties | RelationshipEvents | Acceptatie, afwijzing, ontkoppeling en verloop hebben relatiedomeinbetekenis. |
| Catalogus | CategoryHistory, ExerciseHistory | Wijzigingen moeten gekoppeld blijven aan categorie-, oefening- en modulecontext. |
| Support | TicketHistory, TicketClosures, TicketReopenRequests | Ticketlifecycle vereist eigen status- en sluitlogica. |
| Live meekijken | LiveViewAudit | Start/einde van meekijksessies hoort bij live-monitoring en autorisatiecontext. |
| Admin | ManagementLogEntries | Beheerhandelingen hebben beheercontext en objectcontext nodig. |
Een generieke auditmodule wordt alleen via een toekomstig Technisch Ontwerp-besluit overwogen wanneer aantoonbaar domeinoverstijgende auditgegevens ontstaan die niet logisch bij een bestaande module kunnen worden opgeslagen.
19.6.2 Security
Security wordt wel volledig beschreven en ingericht, maar niet als apart project gemodelleerd. De reden is dat de meeste securitymaatregelen onderdeel zijn van bestaande technische boundaries:
| Securityonderwerp | Technische plek |
|---|---|
| Rate limiting | OefenHub.Web middleware/pipelineconfiguratie. |
| HSTS | OefenHub.Web middleware/pipelineconfiguratie. |
| CSP | OefenHub.Web response-headerconfiguratie. |
| Security headers | OefenHub.Web via middleware of extension methods. |
| Secrets | Environment/configuration binding in OefenHub.Web en OefenHub.Infrastructure. |
| Identity-providerkoppeling | OefenHub.Identity. |
| Rolcontext en policies | OefenHub.Authorization. |
| Verdachte toegangspogingen | Pipeline, Authorization en domeinboundary-logs. |
| Access denied | Authorization en moduleboundary waar de weigering ontstaat. |
Wanneer securityconfiguratie in een toekomstige uitbreiding groot genoeg wordt voor organisatorische afscheiding, kan zij via een expliciet Technisch Ontwerp-besluit worden ondergebracht in een apart infrastructuur- of securityproject. Dat is geen uitgangspunt voor de eerste Technisch Ontwerp-baseline.
19.7 Loggingarchitectuur
OefenHub gebruikt de .NET loggingabstractie als technische basis. Modulecode logt via ILogger<T> of een module-eigen logginginterface wanneer dat nodig is voor testbaarheid of domeinspecifieke verrijking. De concrete loggingbaseline voor de V1.0-architectuur is Serilog met Seq als centrale logviewer. Hosting en infrastructuur configureren Serilog, sinks, verrijking en Seq-koppeling; domeinmodules kiezen geen concrete loggingprovider rechtstreeks.
19.7.1 Logroutes
| Bron | Route | Opmerking |
|---|---|---|
| Web request | Middleware/pipeline naar loggingprovider | Inclusief correlation en statuscode. |
| Blazor component/page | Alleen technische UI-fouten en lifecyclefouten | Geen businesslogica in Web. |
| Module service | ILogger<T> binnen module | Geen directe dependency op concrete loggingprovider. |
| Jobuitvoering | OefenHub.Scheduling plus uitvoerend domein | Zelfde correlation doorgeven. |
| Infrastructure adapter | Adapter-specifieke logging | Bijvoorbeeld storage, mail/provider, identity-providercommunicatie. |
| Exception boundary | Centrale exception middleware of workflowboundary | Technische details intern; veilige respons extern. |
19.7.2 Serilog en Seq
Voor gestructureerde technische logging geldt Serilog als concrete loggingproviderbaseline. Seq wordt gebruikt als centrale viewer voor doorzoekbare loganalyse in development, test, acceptatie en productie, voor zover dit past binnen het beheer- en privacybeleid van de omgeving.
Regels:
- Applicatie- en modulecode blijft loggen via
ILogger<T>of afgesproken module-eigen abstractions. - Serilog wordt centraal geconfigureerd in Web/Infrastructure, inclusief enrichers voor correlation, omgeving, applicatieversie en relevante technische context.
- Seq is een operationele viewer en geen functionele bron van waarheid.
- Seq mag geen secrets, tokens, volledige oefenpayloads, vrije berichtinhoud, volledige PDF-inhoud of onnodige persoonsgegevens bevatten.
- Per omgeving worden logniveau, sinkconfiguratie, opslag, retentie en toegang afgestemd met het beheer- en privacybeleid.
19.7.2.1 Serilog-/Seq-configuratiebaseline
De V1.0-baseline gebruikt een lichte Serilog-/Seq-inrichting die past bij een niet-kritieke, middelgrote applicatie. De inrichting is bedoeld voor diagnose, foutanalyse en securitymonitoring, niet voor functionele rapportage of langdurige persoonsdataopslag.
| Onderdeel | Baseline | Toelichting |
|---|---|---|
| Configuratiebron | appsettings + environment overrides + secretsmechanisme waar nodig | Loggingconfiguratie wordt centraal gebonden; modulecode configureert Serilog of Seq niet rechtstreeks. |
| Minimumniveau development | Debug of Information | Trace alleen tijdelijk en lokaal. |
| Minimumniveau test/acceptatie | Information | Frameworkruis, zoals Microsoft en System, standaard op Warning. |
| Minimumniveau productie | Information | Debug/Verbose in productie alleen tijdelijk, begrensd en met expliciete aanleiding. |
| Sinks development | Console en lokale Seq waar beschikbaar | Lokale Aspire-inrichting mag Seq starten voor ontwikkelaars. |
| Sinks test/acceptatie/productie | Console/runtime-logstream en Seq | Seq is intern/beheerd toegankelijk; publieke toegang is niet toegestaan. |
| Verrijking | CorrelationId, RequestId, omgeving, applicatieversie, machine/container, rolcontext indien beschikbaar | Geen volledige persoonsgegevens of inhoudelijke payloads opnemen. |
| Scrubbing/masking | Tokens, cookies, secrets, connectionstrings, vrije berichtinhoud, PDF-inhoud, volledige oefenpayloads en onnodige persoonsgegevens verwijderen of maskeren | Scrubbing gebeurt vóór verzending naar Seq of permanente opslag. |
| Export | Alleen incidentgericht en beheerd | Logexport is geen reguliere rapportagefunctie en mag geen ongecontroleerde persoonsgegevens bevatten. |
19.7.2.2 Logretentie
Logretentie is kort en doelgericht. De volgende termijnen zijn de V1.0-baseline, tenzij een omgeving strenger moet zijn door hosting-, privacy- of opslagbeleid.
| Logcategorie | Retentie V1.0 | Toelichting |
|---|---|---|
| Development lokaal | Best effort, maximaal 7 dagen | Lokale ontwikkellogs mogen zonder historisch behoud worden opgeschoond. |
| Test en acceptatie | 14 dagen | Genoeg voor regressieanalyse rond testcycli. |
Productie Debug/Verbose | 192 uur (8 dagen) | Alleen wanneer tijdelijk ingeschakeld. |
| Productie reguliere technische logs | 120 dagen | Voor incidentanalyse, foutpatronen en operationele diagnose. |
| Productie securityrelevante technische logs | 360 dagen | Bijvoorbeeld access-deniedpatronen, rate-limit hits en verdachte routepatronen. |
| Critical incidentselectie | Maximaal 720 dagen na expliciete incidentregistratie | Alleen beperkte, gemotiveerde selectie; geen generieke verlenging van alle logs. |
Seq-retentie wordt ingericht met een klein aantal retentiepolicies, bijvoorbeeld voor Debug/Verbose, niet-productie en productie-events. Als opslag of privacybeleid kortere termijnen vereist, gaan die voor.
19.7.3 Logniveaus
| Niveau | Gebruik |
|---|---|
| Trace | Alleen lokaal of zeer gericht tijdelijk onderzoek. Niet standaard in productie. |
| Debug | Ontwikkel- en testdiagnose. Niet structureel voor productieanalyse. |
| Information | Normale technische lifecycle-events met operationele waarde. |
| Warning | Afwijkende maar herstelbare situaties, geweigerde toegang, retrybare fouten, onverwachte lege technische toestand. |
| Error | Mislukte actie, failed job, exception die gebruikersactie of verwerking verhindert. |
| Critical | Ernstige verstoring, dataconsistentierisico, securityincident of niet-beschikbare kerncomponent. |
Gebruik van Information wordt beperkt tot gebeurtenissen die nuttig zijn voor diagnose of beheer. Elk veelvoorkomend pad mag niet onnodig ruis veroorzaken.
19.8 Verplichte logvelden
Niet ieder logrecord heeft alle velden, maar de volgende velden zijn het standaardvocabulaire voor gestructureerde logs.
| Veld | Verplicht wanneer | Toelichting |
|---|---|---|
CorrelationId | Altijd wanneer beschikbaar | Koppelt request, workflow, job en modulelogs. |
RequestId | HTTP-/Blazor-request | Technische requestherleiding. |
UserId | Ingelogde gebruiker bekend | Alleen interne id, geen naam of e-mail tenzij functioneel noodzakelijk en toegestaan. |
RoleContext | Actieve rolcontext relevant | Bijvoorbeeld Student, Teacher, Guardian, Admin. |
ModuleName | Modulelog | Naam van de module die logt. |
ActionName | Service-, workflow- of jobactie | Technisch herkenbare actie. |
EntityType | Objectcontext relevant | Bijvoorbeeld Ticket, ExerciseRun, RelationshipInvitation. |
EntityId | Objectcontext relevant | Alleen id; geen volledige payload. |
JobId | Jobuitvoering | TickerQ/jobidentifier. |
JobType | Jobuitvoering | Functionele jobsoort. |
AttemptNumber | Retrybare jobactie | Pogingnummer. |
WorkflowId | Cross-module workflow | Herleidt meerdere moduleacties binnen één workflow. |
ErrorCode | Bekende foutsoort | Stabiele technische code indien beschikbaar. |
ExceptionType | Exception gelogd | Type zonder gevoelige exceptiondata in message te forceren. |
ElapsedMs | Performance relevant | Duur van request, query, export of jobactie. |
19.8.1 Taal van technische logging
Technische logevents, operation names, exceptioncategorieën, eventnamen, propertynamen en vaste logmessage-templates worden in het Engels vastgelegd. Dit houdt logs consistent met code, libraries, infrastructuur, dashboards en externe technische tooling.
Gebruikersgerichte foutmeldingen, popupteksten, beheerlabels en functionele schermteksten blijven Nederlandstalig en worden niet rechtstreeks uit technische logmessages afgeleid. Vrije gebruikersinvoer wordt niet vertaald of verrijkt voor logging; waar logging nodig is, worden alleen veilige identifiers, statuswaarden en correlationgegevens vastgelegd.
19.9 Correlation en herleidbaarheid
Correlation is verplicht voor technische reconstructie over modulegrenzen heen. Eén gebruikersactie kan meerdere modulecalls, databaseacties, jobs, systeemberichten en readmodelupdates veroorzaken. Zonder gedeelde correlation ontstaat geen betrouwbare analyseketen.
19.9.1 Correlation-hiërarchie
| Identifier | Doel | Voorbeeld |
|---|---|---|
CorrelationId | Overkoepelende keten voor request/workflow/job. | Loginflow, ticket sluiten, oefening delen. |
RequestId | Specifiek HTTP-request of serverinteractie. | Eén Blazor-serverrequest of endpointcall. |
WorkflowId | Functionele cross-module workflow. | Ticket doorzetten naar docent. |
JobId | Technische background job. | Verlopen uitnodigingen verwerken. |
DomainEventId | Domeinevent of pending action. | RelationshipInvitationCreated. |
Wanneer een request een job aanmaakt, wordt de actieve CorrelationId meegegeven aan de jobpayload of jobmetadata. Wanneer de job wordt uitgevoerd, gebruikt zij dezelfde correlation als parent en voegt zij JobId en AttemptNumber toe.
19.9.2 Voorbeeldketen
HTTP request: gebruiker sluit melding
CorrelationId = c-123
RequestId = r-456
Support module:
TicketClosure aangemaakt
TicketHistory geschreven
CorrelationId = c-123
Scheduling:
Job aangemaakt voor vervolgcommunicatie
JobId = j-789
Parent CorrelationId = c-123
Later jobuitvoering:
JobId = j-789
AttemptNumber = 1
Communication contract aangeroepen
SystemMessage aangemaakt
CorrelationId = c-123
19.10 Privacy en verboden loginhoud
Logs mogen niet uitgroeien tot een tweede datalaag met gevoelige inhoud. OefenHub logt daarom geen inhoud die niet nodig is voor technische analyse.
| Niet loggen | Reden |
|---|---|
| Wachtwoorden | Credentialgeheim. |
| Access tokens, refresh tokens, id tokens | Tokenmisbruik voorkomen. |
| Keycloak-/identity-providercredentials | Externe identity-data blijft buiten OefenHub-loginhoud. |
| Volledige antwoordpayloads van oefeningen | Leerlinggegevens en inhoudelijke oefendata. |
| Volledige JSON/base64-modulepayloads | Kunnen antwoorden, timing en oefeninhoud bevatten. |
| Volledige privéberichtinhoud | Communicatieprivacy. |
| Volledige ticketbeschrijvingen in technische logs | Kan persoonsgegevens of gevoelige context bevatten. |
| PDF-inhoud of exportbestanden | Exportdata hoort niet in logs. |
| Browsercookies of volledige headers | Kunnen tokens of identifiers bevatten. |
| Volledige request bodies | Te groot en privacygevoelig. |
| Stacktraces richting gebruiker | Alleen intern loggen. |
Wel toegestaan zijn veilige samenvattingen, ids, statuscodes, aantallen, technische foutcodes en gehashte of gemaskeerde waarden wanneer dat functioneel noodzakelijk is.
19.11 Domeinhistorie en audit
Domeinhistorie is niet hetzelfde als technische logging. Domeinhistorie is functionele bron voor reconstructie binnen een domein en moet daarom transactioneel worden behandeld volgens de regels van dat domein.
19.11.1 Domeinhistorie binnen dezelfde transactie
Wanneer een historyrecord nodig is om een bronmutatie achteraf functioneel te reconstrueren, wordt het historyrecord in dezelfde domeintransactie opgeslagen als de bronmutatie.
Voorbeelden:
| Mutatie | History/audit | Transactiebeleid |
|---|---|---|
| Ticket formeel sluiten | TicketClosures en TicketHistory | Atomair binnen support. |
| Categorie migreren | Category migration/history | Atomair binnen catalog of expliciet gemotiveerde workflow. |
| Relatie accepteren | RelationshipEvents | Atomair binnen relationships. |
| Live meekijken starten | LiveViewAudit startrecord | Atomair met startregistratie binnen live. |
| Beheerder wijzigt popup | ManagementLogEntry | Atomair met beheerwijziging binnen admin. |
19.11.2 Domeinhistorie niet vervangen door logs
Een technisch logrecord zoals "ticket closed" is onvoldoende als formele historie. De bron van reconstructie blijft het domeinrecord, bijvoorbeeld TicketClosures of TicketHistory. Logs helpen alleen bij technische diagnose.
19.12 Securitylogging
Securitylogging registreert gebeurtenissen die relevant zijn voor beveiliging, autorisatie, misbruikdetectie en forensische analyse. De registratie mag geen gevoelige payload lekken.
19.12.1 Securityevents
| Event | Bron | Loggingniveau | Persistente domeinopslag |
|---|---|---|---|
| Toegang geweigerd door policy | Authorization of moduleboundary | Warning | Standaard alleen technische logging. |
| Ongeldige rolcontext | Authorization | Warning | Standaard alleen technische logging. |
| Niet-actief account probeert toegang | Identity | Warning | Alleen persistente identity/accountregistratie wanneer dit nodig is voor accountlifecycle of beheeractie. |
| Routeparameter probeert andere gebruiker te openen | Moduleboundary | Warning | Standaard alleen technische logging; domeinhistorie alleen wanneer het domein dit functioneel nodig heeft. |
| Repeated access denied patroon | Web/Authorization | Warning/Error afhankelijk van patroon | Operationele securitymonitoring in Seq; geen generieke securityeventtabel. |
| Verdachte job- of beheeractie | Scheduling/Admin | Warning/Error | Jobhistory of managementlog wanneer de actie al binnen dat domein bronhoudend geregistreerd moet worden. |
| Misconfiguratie security headers | Web startup/healthcheck | Error | Technische logging. |
19.12.1.1 Persistentiebaseline voor securityevents
Voor V1.0 komt er geen generieke security-schema, geen centrale SecurityEvents-tabel en geen algemene securityeventbron naast technische logging. Securityevents blijven standaard in Serilog/Seq, met korte en privacybewuste retentie.
Persistente opslag is alleen toegestaan wanneer één van deze situaties geldt:
- het event is functioneel onderdeel van domeinhistorie, zoals relatie-, support-, beheer- of live-meekijkhistorie;
- het event is nodig voor een concrete account-, beheer- of joblifecycle;
- een toekomstig Functioneel Ontwerp-, Software Requirements Specification- of privacybesluit maakt langdurige registratie verplicht;
- een securityincident vereist een tijdelijke, gemotiveerde incidentregistratie buiten de generieke loggingstroom.
Voorbeelden:
| Situatie | V1.0-opslag | Reden |
|---|---|---|
| Een ouder/voogd krijgt geen toegang tot een kindresultaat | Technische access-deniedlog | Geen functionele bronmutatie. |
| Een beheerder wijzigt een popup of template | ManagementLogEntry naast technische log | Beheeractie is functionele audit. |
| Live meekijken wordt gestart | LiveViewAudit naast technische log | Live-meekijken heeft domeinauditwaarde. |
| TickerQ-dashboardtoegang wordt geweigerd | Technische securitylog | Geen apart domeinrecord nodig. |
| Repeated access denied vanaf dezelfde bron | Seq/securitymonitoring + rate limiting | Patroonanalyse zonder centrale securityeventtabel. |
GuardianAccessDenied blijft een access-denied/security-event en wordt niet als brondata, readmodel of duurzaam domeinobject behandeld, tenzij een later besluit dit expliciet wijzigt.
19.12.2 Access denied zonder datalek
Bij autorisatiefouten wordt gelogd dat een poging is geweigerd, maar niet welke gevoelige data de gebruiker probeerde te bekijken.
Veilig:
Access denied for EntityType=ExerciseRun, EntityId=<id>, UserId=<id>, RoleContext=Guardian, Reason=MissingActiveGuardianRelationship
Niet veilig:
Access denied for run of child Sanne de Vries, exercise answers were ...
19.12.3 Verantwoordelijkheid
| Component | Verantwoordelijkheid |
|---|---|
OefenHub.Web | Pipeline-events, requeststatussen, headers, rate limiting, HSTS/CSP-configuratie. |
OefenHub.Identity | Accountstatus, provisioningfouten, externe logincontext zonder credentials/tokens. |
OefenHub.Authorization | Policy failures, rolcontextfouten, server-side contextcontrole. |
| Domeinmodule | Domeinspecifieke weigering, bijvoorbeeld ontbrekende ouderrelatie of docentcontext. |
OefenHub.Scheduling | Securityrelevante job- en dashboardtoegang, mislukte jobuitvoeringen. |
19.13 Technische foutafhandeling
Foutafhandeling gebeurt op meerdere boundaries. Elke boundary heeft een eigen taak: technisch loggen, functioneel veilig afhandelen en de gebruiker niet onnodig technische details tonen.
19.13.1 Foutboundaries
| Boundary | Voorbeelden | Gedrag |
|---|---|---|
| Web middleware | Onverwachte exception, 500 | Log exception met correlation; toon generieke foutpagina. |
| Blazor component/page | Renderfout, formulieractie faalt | Toon veilige melding of popupkey; log technisch detail. |
| Module service | Domeinvalidatie, inconsistentie | Geef typed result/exception terug; log alleen waar nuttig. |
| Workflow boundary | Cross-module actie faalt | Rollback of failed-status volgens workflowbeleid; log correlation. |
| Job boundary | Retrybare fout, definitieve jobfout | Registreer attempt, error en retry/final failed-state. |
| Infrastructure adapter | Externe dependency faalt | Log providerfout zonder secrets; geef veilige fout terug. |
19.14 Foutcategorieën
| Categorie | Voorbeeld | Gebruikersrespons | Logging |
|---|---|---|---|
| Validatiefout | Ongeldige invoer, ontbrekend verplicht veld | Formulierfeedback of popupkey | Meestal geen Error-log; eventueel Information/Warning bij misbruikpatroon. |
| Autorisatiefout | Geen actieve relatie, verkeerde rolcontext | 403 of veilige toegang-geweigerdafhandeling | Warning met correlation, geen gevoelige inhoud. |
| Niet gevonden | Object bestaat niet of niet zichtbaar | 404 of veilige niet-beschikbaarafhandeling | Information/Warning afhankelijk van context. |
| Conflict | Status gewijzigd, dubbele uitnodiging, concurrency | Veilige conflictmelding | Warning met object- en statuscontext. |
| Technische fout | Database/infra/exception | Generieke foutpagina of popupkey | Error met exception intern. |
| Securityincident | Herhaald misbruik, verdachte routepatronen | Generiek blokkeren of rate limit | Warning/Error/Critical afhankelijk van ernst. |
19.15 40x- en 50x-afhandeling
| Status | Betekenis | Technische behandeling |
|---|---|---|
| 400 | Ongeldige request of invoer | Geen technische details tonen; validatiefouten waar mogelijk functioneel teruggeven. |
| 401 | Niet ingelogd of sessie ongeldig | Veilige login-/sessieroute; geen identity-providerdetails tonen. |
| 403 | Wel ingelogd, geen toegang | Log access denied zonder payload; toon veilige toegang-geweigerdcontext. |
| 404 | Niet gevonden of niet zichtbaar | Geen onderscheid lekken tussen niet bestaan en geen toegang wanneer privacy dat vereist. |
| 409 | Conflict of concurrency | Gebruiker veilig informeren dat context is gewijzigd. |
| 429 | Rate limit | Generieke melding; log patroon op Warning. |
| 500 | Onverwachte serverfout | Correlation loggen; generieke foutpagina. |
| 503 | Tijdelijke onbeschikbaarheid | Generieke beschikbaarheidsmelding; monitoring/logging. |
Gebruikers zien geen stacktrace, SQL-fout, connectionstring, table name, token, route-internal of module-internal naam wanneer dat niet functioneel bedoeld is.
19.16 Typed results en exceptions
Modulecontracten geven bij voorkeur typed results terug voor verwachte fouttoestanden. Exceptions zijn bedoeld voor onverwachte technische fouten of invariantschendingen.
| Situatie | Voorkeur |
|---|---|
| Verwachte validatiefout | Typed result met foutcode. |
| Verwachte autorisatie-afwijzing | Typed result of domain-specific denied result. |
| Niet gevonden binnen toegestane context | Typed result. |
| Concurrency conflict | Typed result of specifieke exception die boundary vertaalt. |
| Onmogelijke domeintoestand | Domain exception met veilige code. |
| Database/providerfout | Exception loggen op technische boundary. |
Typed results bevatten stabiele foutcodes zodat Web kan bepalen welke popupkey, foutpagina of formulierfeedback gebruikt wordt zonder technische details te tonen.
19.17 Logging in cross-module workflows
Cross-module workflows worden door de functionele eigenaar gecoördineerd. Logging moet aantonen welke module welke stap heeft uitgevoerd en waar een eventuele fout is ontstaan.
19.17.1 Workflowlogvelden
| Veld | Doel |
|---|---|
WorkflowName | Technische naam, bijvoorbeeld ForwardTicketToTeacher. |
WorkflowId | Unieke uitvoering van de workflow. |
WorkflowOwnerModule | Module die functioneel eigenaar is. |
StepName | Stap binnen de workflow. |
StepOwnerModule | Module die de stap uitvoert. |
TransactionBoundary | Indicatie of stap onderdeel is van atomair gedeelte. |
RollbackRequired | Alleen voor workflows die compensatie of rollbackbeleid hebben. |
ResultStatus | Succeeded, Failed, Retried, Skipped, Compensated. |
19.17.2 Voorbeeld
WorkflowName=ForwardTicketToTeacher
WorkflowOwnerModule=Support
StepName=CloseTicket
StepOwnerModule=Support
CorrelationId=c-123
WorkflowId=w-456
ResultStatus=Succeeded
StepName=CreatePrivateMessageOnBehalfOfUser
StepOwnerModule=Communication
CorrelationId=c-123
WorkflowId=w-456
ResultStatus=Failed
ErrorCode=Communication.ThreadCreationFailed
Wanneer een workflow atomair moet zijn, mag een gedeeltelijk succesvolle toestand niet worden gecommit. Wanneer een workflow bewust retrybare vervolgacties gebruikt, moet een failed vervolgactie beheerbaar zichtbaar blijven.
19.18 Logging in background jobs
Background jobs vallen onder OefenHub.Scheduling voor technische lifecycle, maar de domeinactie blijft eigendom van de uitvoerende module.
19.18.1 Joblogvelden
| Veld | Doel |
|---|---|
JobId | Unieke jobidentifier. |
JobType | Functionele jobsoort. |
AttemptNumber | Pogingnummer. |
MaxAttempts | Maximaal aantal pogingen. |
NextRetryAtUtc | Volgende retry wanneer van toepassing. |
TriggeredByModule | Module die job heeft aangemaakt. |
ExecutingModule | Module waarvan contract wordt aangeroepen. |
CorrelationId | Herleidbaarheid naar oorspronkelijke request of workflow. |
PayloadReference | Veilige verwijzing naar payload, niet de volledige payload als die gevoelig is. |
LastErrorCode | Stabiele foutcode. |
LastErrorMessage | Veilige technische samenvatting. |
19.18.2 Failed jobs
Een job die na maximaal aantal pogingen faalt, blijft beheerbaar herleidbaar. De fout mag niet verdwijnen in alleen technische logging. Minimale informatie:
| Informatie | Doel |
|---|---|
| Jobtype | Weten welke verwerking faalde. |
| CorrelationId | Terugvinden van oorspronkelijke actie. |
| Pogingen | Vaststellen of retrybeleid werkte. |
| Laatste fout | Diagnose zonder gevoelige payload. |
| Betrokken module | Weten wie inhoudelijk eigenaar is. |
| Tijdstippen | Analyse van timing en incidenten. |
19.19 Databasefouten en EF Core
Databasefouten worden op repository-/DbContext-boundary of serviceboundary gevangen waar zij technisch zinvol vertaald kunnen worden. Ruwe SQL, connectionstrings of providerdetails worden niet aan gebruikers getoond.
| Fout | Behandeling |
|---|---|
| Concurrency conflict | Vertalen naar conflictresultaat of veilige foutcode. |
| Unique constraint violation | Vertalen naar bekende domein-/validatiefout wanneer verwacht. |
| Foreign key violation binnen module | Error; duidt meestal op bug of race condition. |
| Soft link naar ontbrekend object | Domein bepaalt veilige niet-beschikbaarafhandeling. |
| Migration/startupfout | Critical/Error bij applicatiestart of deploymentcontrole. |
| Timeout | Error/Warning afhankelijk van impact; querycontext zonder payload loggen. |
Omdat cross-module hard FK's niet standaard worden gebruikt, moeten soft-linkfouten bewust in applicatielogica worden afgehandeld. Een ontbrekende soft-linked bron mag niet leiden tot lekken van technische details.
19.20 Logging van persoonsgegevens
Persoonsgegevens mogen alleen worden gelogd wanneer dat noodzakelijk is voor beheer of securityanalyse en niet anders kan. De voorkeur gaat uit naar interne id's, geanonimiseerde waarden of momentopnames die al volgens het datamodel zijn toegestaan.
| Gegeven | Loggingbeleid |
|---|---|
UserId | Toegestaan als interne identifier. |
| Naam | Alleen indien noodzakelijk voor beheerlog/audit en toegestaan door domeinregels. |
| E-mailadres | Vermijden; eventueel gemaskeerd of gehasht voor provisioninganalyse. |
| IP-adres | Alleen waar security/technische analyse dit rechtvaardigt; retentie beperken. |
| UserAgent | Toegestaan in technische snapshot/securitycontext; niet onbeperkt bewaren. |
| Kind-/leerlingnaam | Niet loggen bij autorisatiefouten waar toegang ontbreekt. |
| Antwoorden/oefeninhoud | Niet in technische logs. |
Definitieve bewaartermijnen en anonimisering vallen onder Technisch Ontwerp: privacy, retentie, anonimisering en gegevensbescherming.
19.21 Beheerlogging
Beheeracties worden niet alleen technisch gelogd, maar waar functioneel nodig ook vastgelegd in beheer- of domeinhistorie.
Voorbeelden:
| Beheeractie | Functionele opslag | Technische log |
|---|---|---|
| Categorie wijzigen | Category history | Information met actor/object/correlation. |
| Categorie migreren | Category migration/history | Information/Error bij workflowstappen. |
| Popup wijzigen | ManagementLogEntry of popup history | Information met veldwijziging zonder onnodige payload. |
| Accountstatus aanpassen | Account lifecycle log | Warning/Information afhankelijk van actie. |
| Docentondersteuning uitvoeren | Domeineigen support/audit | Information met supportcontext. |
| Failed job opnieuw starten | Scheduling jobhistory | Warning/Information met JobId. |
Beheerlogs moeten actor, rolcontext, objecttype, objectid, actie en tijdstip kunnen herleiden. Vrije toelichting wordt alleen opgeslagen waar het functionele domein dit vereist.
19.22 Foutrespons in Web
OefenHub.Web vertaalt technische uitkomsten naar veilige UI-respons.
| Bron | Web-afhandeling |
|---|---|
| Typed validation result | Formulierfeedback. |
| Typed access denied result | 403/veilige toegang-geweigerdafhandeling. |
| Typed not found result | 404 of niet-beschikbaarcontext. |
| Known domain conflict | Veilige melding dat gegevens zijn gewijzigd. |
| Unknown exception | Generieke foutpagina of popup, correlation intern. |
| Job failed async | Niet direct tonen tenzij gebruiker nog in context wacht; beheerbaar loggen. |
Web mag foutafhandeling samenstellen, maar mag geen module-interne gegevens of technische exceptiondetails aan de gebruiker tonen.
19.23 Foutafhandeling tijdens actieve oefenrun
Tijdens een actieve oefenrun gelden aanvullende grenzen omdat afleiding en dataverlies voorkomen moeten worden.
| Situatie | Behandeling |
|---|---|
| Antwoord opslaan faalt | Gebruiker veilig informeren dat opslaan niet is gelukt; geen stilzwijgende voortgang. |
| SignalR-update faalt | Oefenvoortgang blijft server-side bron; live-kijkers krijgen reconnect/einde volgens live-regels. |
| Badge/notificatie-update faalt | Niet zichtbaar maken tijdens oefening; na de oefening opnieuw beoordelen. |
| Onverwachte fout | Geen antwoordpayload loggen; correlation gebruiken. |
19.24 Foutafhandeling bij PDF/export
PDF-export gebruikt historische runcontext en snapshots, maar logt geen PDF-inhoud.
| Fout | Behandeling |
|---|---|
| Run niet toegankelijk | Autorisatie-afhandeling zonder gedeeltelijke data. |
| Run niet gevonden | Niet-beschikbaarafhandeling. |
| Module-rendering faalt | Error met module-id/name, geen vraagpayload. |
| Tijdelijk bestand faalt | Error met export-id/correlation, geen PDF-inhoud. |
| Cleanup faalt | Retrybare jobfout via scheduling. |
19.25 Monitoringinput
Dit hoofdstuk definieert log- en foutinformatie die door monitoring gebruikt kan worden. Het is niet de volledige monitoringinrichting.
Minimale monitoringbronnen:
| Bron | Signaal |
|---|---|
| Web request logs | Foutpercentages, response times, 40x/50x-patronen. |
| Module error logs | Domein- of infrastructuurfouten. |
| Scheduling jobstatus | Failed jobs, retry storm, langdurige verwerking. |
| Securitylogging | Access denied pieken, rate-limit hits, verdachte routepatronen. |
| Databasefouten | Timeouts, migration issues, concurrencyconflicten. |
| PDF/exportfouten | Renderingproblemen of bestandsopslagproblemen. |
Operationele dashboards en alerting worden uitgewerkt in Technisch Ontwerp: beheerbeleid, monitoring, backup, restore en operatie.
19.26 Teststrategie voor logging en foutafhandeling
Logging zelf hoeft niet in iedere unittest exact te worden gevalideerd, maar kritieke foutpaden en securitygrenzen moeten wel testbaar zijn.
| Testtype | Doel |
|---|---|
| Unit tests | Typed results, validatiefouten, domeinexceptions en foutcodes. |
| Module integration tests | Databasefouten, concurrency en history/audit binnen module. |
| Workflow tests | Atomair of retrybaar gedrag bij cross-module fouten. |
| Job tests | Retrybeleid, failed-state, idempotentie en correlationdoorgifte. |
| Security tests | Access denied, geen datalek in respons, rolcontextcontrole. |
| Architecture tests | Geen directe toegang tot interne entities/DbContexts over modulegrenzen. |
| Logging tests gericht | Controleren dat verboden payloads niet worden gelogd in kritieke paden. |
19.27 Implementatiechecklist
Bij iedere nieuwe module, workflow of technisch component moet minimaal worden gecontroleerd:
- Welke logevents zijn nodig voor beheer en diagnose?
- Welke domeinhistorie is functioneel bronhoudend?
- Welke fouttoestanden zijn verwacht en krijgen typed results?
- Welke fouttoestanden zijn onverwacht en worden als exception gelogd?
- Welke correlation-identifiers moeten worden doorgegeven?
- Worden credentials, tokens, payloads en persoonsgegevens vermeden?
- Is access denied veilig zonder datalek?
- Is retry begrensd en beheerbaar?
- Is een failed job of workflow achteraf herleidbaar?
- Is duidelijk welke module eigenaar is van de foutafhandeling?
- Is de foutrespons naar de gebruiker veilig en consistent?
- Zijn retentie en anonimisering afgestemd met het privacyhoofdstuk?
19.28 Implementatieverificaties
| Punt | Toelichting | Eigenaar vervolguitwerking |
|---|---|---|
| Serilog/Seq-inrichting | Inrichting toetsen aan de vastgelegde Serilog-/Seq-configuratiebaseline, inclusief sinks, enrichers, Seq-koppeling, toegang en exportbeperking. | Infrastructuur/operatie. |
| Correlation middleware | Uitwerken hoe CorrelationId wordt aangemaakt, doorgegeven en hergebruikt. | Web/Infrastructure. |
| TickerQ-logkoppeling | Controleren hoe TickerQ job-id, schema, dashboard en loggingmetadata beschikbaar maakt. | Scheduling. |
| Security headers | CSP/HSTS/headerconfiguratie toetsen aan de vastgelegde baseline. | Web/securityconfiguratie. |
| Persistent securityevents | Toetsen dat securityevents standaard in technische logs blijven en alleen domeineigen persistent worden wanneer hoofdstuk 19 dit toestaat. | Identity/Authorization/domeinmodules. |
| Retentie logs | Seq-/logretentie controleren tegen de vastgelegde termijnen per omgeving en logcategorie. | Privacy/operatie. |
| Beheerinterface failed jobs | Bepalen welke jobfouten via interne TickerQ-interface of beheerpagina zichtbaar worden. | Scheduling/Admin. |
19.x OIDC remote failure handling
OIDC remote failures zoals Correlation failed worden als security-/auth-flowfouten behandeld en mogen niet als Developer Exception bij gebruikers terechtkomen. De authenticationlaag handelt remote failures centraal af, logt technische context met OefenHub correlation-id en toont alleen een veilige gebruikersmelding.
De handler mag geen callback zonder geldige OIDC state/correlation als succesvolle login accepteren. De oplossing moet gebruikersvriendelijk zijn voor meerdere tabs of apparaten: een venster zonder geldige correlation-context krijgt uitleg om opnieuw te starten of verder te gaan in het venster waar login wel is afgerond.