Skip to main content

1. Identiteit en autorisatie

Deze sectie bevat de kern van accountidentiteit, profielgegevens, gebruikersinstellingen, roltoekenning en permission-based RBAC. Rollen blijven bestaan als bundels, maar autorisatiechecks lopen tegen permissions. De effectieve permissions van een gebruiker worden afgeleid uit UserRoles, Roles, RolePermissions en Permissions. Relatie-, niveau-, kind-, run- en supportcontext blijven aanvullende domeincontroles.

1.1 Users

TabelnaamCategorieDoel / verantwoordelijkheidGerelateerde tabellen
UsersIdentiteitBasisaccount van een gebruiker, inclusief interne identiteit, koppeling naar de externe identity provider en weergave-gerelateerde gegevens.Roles, UserRoles, Permissions, RolePermissions, ProfileAvatars, UserSettings, RelationshipInvitations, UserRelationships, SystemMessages, PrivateMessages
VeldnaamTypeDefaultPKFKVerwijst naarUniqueNullableIndexOpmerking
Iduniqueidentifier-JN-JNJPrimaire sleutel van de gebruiker. GUID wordt in de applicatiecode gegenereerd; geen database-default.
Emailnvarchar(320)-NN-JNJAuthenticatie-e-mailadres voor login en uitnodigingen. Bij anonimisering wordt dit vervangen door een systeemmatig gegenereerd anoniem adres.
DisplayNamenvarchar(150)-NN-NNJWeergavenaam in de interface.
MiddleNamenvarchar(50)nullNN-NJNOptioneel tussenvoegsel voor persoonlijke aanspreking en correcte naamweergave.
LastNamenvarchar(100)nullNN-NJNAchternaam voor persoonlijke aanspreking en correcte naamweergave.
FirstNamenvarchar(100)-NN-NJNVoornaam voor persoonlijke aanspreking.
ProfileAvatarIduniqueidentifiernullNJProfileAvatars.IdNJNVerwijzing naar een vooraf gedefinieerde profielafbeelding.
IsActivebit1NN-NNJSoft delete / actieve status van account.
LastSeenAtUtcdatetime2nullNN-NJJLaatste bekende activiteit of login. De first-visit-greeting wordt bij sessieopbouw vóór het bijwerken van deze waarde afgeleid en tijdelijk in de auth-sessie opgeslagen.
OnboardingCompletedAtUtcdatetimeoffsetnullNN-NJJTijdstip waarop de centrale OefenHub-onboarding voor dit account volledig is afgerond. Leeg betekent dat de onboarding-gate nog verplichte stappen moet beoordelen.
CreatedAtUtcdatetime2sysutcdatetime()NN-NNJAanmaakdatum van het account.
UpdatedAtUtcdatetime2sysutcdatetime()NN-NNNLaatst bijgewerkt op.
ExternalIdnvarchar(100)-NN-JNJStabiele externe identity provider-id, bijvoorbeeld de Keycloak user-id. Wordt server-side gevuld en is niet via de GUI wijzigbaar.

Validaties / constraints

  • E-mailadres is uniek en case-insensitive te behandelen.
  • ExternalId is uniek en vormt de stabiele koppelsleutel naar de externe identity provider. De interne GUID in Users.Id blijft de primaire sleutel van OefenHub.
  • De waarde # is gereserveerd als systeemwaarde voor geanonimiseerde accounts en mag niet via de normale UI als tussenvoegsel worden opgeslagen.
  • DisplayName is verplicht voor uniforme weergave in UI en berichten.
  • Profielafbeelding verwijst alleen naar een vooraf gedefinieerde avatar.

Business rules

  • Een gebruiker kan meerdere rollen hebben, behalve combinaties met de rol Leerling. Rollen zijn bundels van permissions; directe rolchecks zijn geen autorisatiebeslissing meer.
  • Users bevat profielgegevens, maar geen voorkeuren of toegankelijkheidsinstellingen; die zitten in UserSettings.
  • Authenticatie, wachtwoorden, sessies en credential-lifecycle liggen buiten OefenHub bij de identity provider. Users bewaart daarom een interne identiteit plus een stabiele externe koppelsleutel, maar is niet de bronhouder van credentials.
  • Een functionele accountverwijdering in OefenHub betekent geen hard delete van het domeinrecord, maar een gecontroleerde anonimiseer- en cleanupflow met behoud van historie waar dat functioneel nodig blijft.

Lifecycle / gedrag

  • Accounts worden niet hard verwijderd. IsActive = false markeert een account als gedeactiveerd, terwijl historie behouden blijft.
  • Bij anonimisering worden naam en e-mailadres systeemmatig overschreven. Voornaam wordt Anoniem, tussenvoegsel wordt #, achternaam wordt een korte niet-voorspelbare unieke code en e-mailadres wordt vervangen door een systeemadres in de vorm anoniem.<code>@verwijderd.acc.
  • Open domeindata zoals relaties, uitnodigingen en toegangscontexten worden tijdens de accountverwijderflow administratief opgeschoond; niet-afgeronde oefenruns blijven historisch bestaan maar worden niet meer hervatbaar.

Designkeuzes

  • Gebruikeridentiteit is bewust gescheiden van rollen, relaties en gebruikersinstellingen om multi-role gedrag en uitbreidbare voorkeuren mogelijk te maken.
  • De combinatie van een interne GUID en een aparte ExternalId voorkomt dat de tabelstructuur direct afhankelijk wordt van één specifieke identity provider en houdt toekomstige providerwissels of migraties beter beheersbaar.
  • De zeer herleidbare lifecycle van accountaanmaak, verwijdering en anonimisering wordt niet alleen via relationele historie ondersteund, maar aanvullend via een apart applicatielogbestand voor accountacties.

Foreign keys op databaseniveau

  • Harde foreign keys op databaseniveau: ProfileAvatarId -> ProfileAvatars.Id.

Functionele / logische verwijzingen zonder harde FK

  • De velden Email bevatten inhoudelijke of technische contextwaarden en zijn daarom logische samenhang zonder harde foreign key.

FK + snapshot

  • FK + snapshot: niet van toepassing binnen deze tabel.

1.2 Roles

TabelnaamCategorieDoel / verantwoordelijkheidGerelateerde tabellen
RolesAutorisatieDefinitie van beschikbare rollen binnen OefenHub. Rollen groeperen permissions via RolePermissions.UserRoles, RolePermissions, UserRelationships, RelationshipInvitations
VeldnaamTypeDefaultPKFKVerwijst naarUniqueNullableIndexOpmerking
Iduniqueidentifier-JN-JNJPrimaire sleutel van de rol. GUID wordt in de applicatiecode gegenereerd; geen database-default.
Codenvarchar(50)-NN-JNJTechnische code, bijvoorbeeld Student, Teacher, Guardian, Admin.
Namenvarchar(100)-NN-JNNLeesbare naam van de rol.
IsActivebit1NN-NNJMaakt rol selecteerbaar of verborgen.
IsPublicbit0NN-NNJBepaalt of deze rol door een gebruiker zelf gekozen mag worden bij publieke registratie of profieluitbreiding. Rollen zoals Admin en TestDocent hebben IsPublic = 0 en worden alleen via beheer toegekend.
CreatedAtUtcdatetime2sysutcdatetime()NN-NNNAanmaakdatum.

Validaties / constraints

  • Code en Name zijn uniek.
  • Rollen worden centraal beheerd en zijn beperkt tot de in het systeem toegestane waarden.
  • Niet-publieke rollen mogen niet zelfstandig door eindgebruikers gekozen worden.
  • Rollen geven geen directe autorisatie; effectieve rechten ontstaan via actieve RolePermissions naar actieve Permissions.

Business rules

  • Publieke rollen zoals Leerling, Docent en Ouder/Voogd kunnen door een nieuwe gebruiker zelf gekozen worden.
  • Rollen zoals Beheerder en TestDocent vereisen expliciete toekenning via een administratieve of beheerlaag.
  • Beheer van rollen, permissions en RolePermissions zelf is buiten scope van Feature 11-5 en wordt voorbereid in userstory 17-091 met een aparte Admin-/autorisatiebeheerrol.

Lifecycle / gedrag

  • Rollen worden in de praktijk zelden verwijderd; deactiveren geniet de voorkeur boven delete.
  • IsPublic bepaalt of een actieve rol in publieke registratie- en keuzeprocessen zichtbaar is.

Designkeuzes

  • Rollen in aparte tabel voorkomt hardcoding in meerdere domeinen en maakt rolcontext expliciet in relaties.
  • De extra vlag IsPublic scheidt bewust vrij kiesbare rollen van beheer- of testrollen.
  • De autorisatielaag zit niet meer in rollen zelf, maar in Permissions en RolePermissions zodat rollen als beheerbare bundels kunnen functioneren.

Foreign keys op databaseniveau

  • Geen harde foreign keys op databaseniveau binnen deze tabel.

Functionele / logische verwijzingen zonder harde FK

  • De velden Code functioneren als code-, enum- of sleutelwaarden en verwijzen bewust niet via een harde foreign key.

FK + snapshot

  • FK + snapshot: niet van toepassing binnen deze tabel.

1.3 UserRoles

TabelnaamCategorieDoel / verantwoordelijkheidGerelateerde tabellen
UserRolesAutorisatieKoppelt één of meerdere rollen aan een gebruiker, inclusief auditinformatie over toekenning en intrekking.Users, Roles, Permissions, RolePermissions
VeldnaamTypeDefaultPKFKVerwijst naarUniqueNullableIndexOpmerking
Iduniqueidentifier-JN-JNJPrimaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default.
UserIduniqueidentifier-NNUsers.IdNNJSoft link naar Users.Id; geen harde database-FK vanwege de scheiding tussen het authorization-domein en het identity-domein.
RoleIduniqueidentifier-NJRoles.IdNNJToegekende rol.
IsActivebit1NN-NNJGeeft aan of roltoekenning actief is.
GrantedAtUtcdatetime2sysutcdatetime()NN-NNNMoment van toekenning.
GrantedByUserIduniqueidentifiernullNNUsers.IdNJNSoft link naar Users.Id voor de actor die de rol toekende; geen harde database-FK.
RevokedAtUtcdatetime2nullNN-NJNMoment van intrekking.
RevokedByUserIduniqueidentifiernullNNUsers.IdNJNSoft link naar Users.Id voor de actor die de rol introk; geen harde database-FK.

Validaties / constraints

  • Actieve combinatie UserId + RoleId is uniek.
  • Een gebruiker kan meerdere rollen hebben binnen de toegestane combinatieregels.

Business rules

  • Roltoekenning bepaalt niet automatisch autorisatie. De effectieve permissions worden via actieve rollen en RolePermissions bepaald en gecachet.
  • Iedere wijziging in actieve UserRoles moet de permissioncache voor de gebruiker invalidaten.

Lifecycle / gedrag

  • Intrekking van een rol verwijdert historie niet.
  • Intrekking of toekenning van een rol leegt de permissioncache voor de gebruiker.
  • Eerdere relatie- en berichtdata blijven naar de gebruiker verwijzen.

Designkeuzes

  • Aparte koppelingslaag maakt multi-role gebruikers mogelijk met volledige audittrail.

Foreign keys op databaseniveau

  • Harde foreign keys op databaseniveau: RoleId -> Roles.Id.

Functionele / logische verwijzingen zonder harde FK

  • UserId, GrantedByUserId en RevokedByUserId zijn soft links naar Users.Id. Zij worden server-side gevalideerd via het identity-/authorizationcontract, maar krijgen geen harde database-FK omdat Users in het identity-domein ligt en UserRoles in het authorization-domein. Overige velden zijn inhouds-, status- of auditwaarden.

FK + snapshot

  • FK + snapshot: niet van toepassing binnen deze tabel.

1.4 Permissions

TabelnaamCategorieDoel / verantwoordelijkheidGerelateerde tabellen
PermissionsAutorisatieDefinitie van alle expliciete permission-codes die in OefenHub gebruikt mogen worden voor routes, schermen, menu-items, frontpageblokken, services en domeinacties.Roles, RolePermissions, UserRoles
VeldnaamTypeDefaultPKFKVerwijst naarUniqueNullableIndexOpmerking
Iduniqueidentifier-JN-JNJPrimaire sleutel van de permission. GUID wordt in de applicatiecode gegenereerd; geen database-default.
Codenvarchar(150)-NN-JNJTechnische permission-code volgens <resource>.<action>[.<scope>], bijvoorbeeld student-results.read.assigned.
Namenvarchar(150)-NN-NNNKorte leesbare naam voor beheer-/diagnoseweergave.
Descriptionnvarchar(1000)-NN-NNNEngelse toelichting op wat de permission precies toestaat en welke domeinchecks nog nodig zijn.
IsActivebit1NN-NNJAlleen actieve permissions tellen mee bij effectieve rechten.
CreatedAtUtcdatetime2sysutcdatetime()NN-NNNAanmaakdatum.
UpdatedAtUtcdatetime2sysutcdatetime()NN-NNNLaatst bijgewerkt op.

Validaties / constraints

  • Code is uniek en lowercase.
  • Code volgt de conventie <resource>.<action>[.<scope>].
  • Description is verplicht en beschrijft in het Engels wat de permission doet.
  • Inactieve permissions worden niet meegenomen in permissioncache of autorisatiechecks.
  • Startup-/testvalidatie moet falen wanneer code een permission declareert die niet in deze tabel of seeddata voorkomt.

Business rules

  • Permissions zijn het autorisatiecontract voor ingangen en acties.
  • Een permission is geen objecttoegang; domeinservices controleren daarna de concrete scope.
  • Permissions worden niet per individueel puur visueel element gemaakt, tenzij dat element een zelfstandige security-impact heeft.
  • Er komt geen grote centrale C#-permissiecatalogus als tweede bron van waarheid; de runtime bron is deze tabel.

Lifecycle / gedrag

  • Permissions worden initieel via migratie/seed beheerd.
  • Deactiveren heeft de voorkeur boven verwijderen, omdat RolePermissions en auditcontext historisch herleidbaar moeten blijven.
  • Wijzigen of deactiveren van permissions vereist invalidatie van betrokken gebruikerscaches wanneer de wijziging runtime-effect heeft.

Designkeuzes

  • Permissions zijn bewust apart van Roles gemodelleerd zodat rollen alleen bundels zijn.
  • Description is verplicht om beheer, review en audit begrijpelijk te houden.
  • De permissionnaamgeving is vastgelegd in docs/ontwerpbronnen/rbac-permissieregister.md.

Foreign keys op databaseniveau

  • Geen harde foreign keys op databaseniveau binnen deze tabel.

Functionele / logische verwijzingen zonder harde FK

  • Code functioneert als technische sleutel richting declaratieve metadata in code, maar codebestanden zijn geen database-FK.

FK + snapshot

  • FK + snapshot: niet van toepassing binnen deze tabel.

1.5 RolePermissions

TabelnaamCategorieDoel / verantwoordelijkheidGerelateerde tabellen
RolePermissionsAutorisatieKoppelt permissions aan rollen, inclusief auditinformatie over toekenning en intrekking.Roles, Permissions, UserRoles
VeldnaamTypeDefaultPKFKVerwijst naarUniqueNullableIndexOpmerking
Iduniqueidentifier-JN-JNJPrimaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default.
RoleIduniqueidentifier-NJRoles.IdNNJRol waaraan de permission gekoppeld is.
PermissionIduniqueidentifier-NJPermissions.IdNNJPermission die via de rol wordt toegekend.
IsActivebit1NN-NNJAlleen actieve koppelingen tellen mee bij effectieve rechten.
GrantedAtUtcdatetime2sysutcdatetime()NN-NNNMoment van koppelen.
GrantedByUserIduniqueidentifiernullNNUsers.IdNJNSoft link naar Users.Id voor de actor die de koppeling maakte; geen harde database-FK.
RevokedAtUtcdatetime2nullNN-NJNMoment van intrekken.
RevokedByUserIduniqueidentifiernullNNUsers.IdNJNSoft link naar Users.Id voor de actor die de koppeling introk; geen harde database-FK.

Validaties / constraints

  • Actieve combinatie RoleId + PermissionId is uniek.
  • RoleId verwijst naar een bestaande rol.
  • PermissionId verwijst naar een bestaande permission.
  • Alleen actieve rollen, actieve permissions en actieve RolePermissions tellen mee bij effectieve rechten.

Business rules

  • RolePermissions bepalen welke permissions via een rol beschikbaar worden.
  • Een gebruiker met meerdere rollen krijgt de distinct union van permissions uit alle actieve rollen.
  • Wijzigen van RolePermissions vereist invalidatie van de permissioncache van alle gebruikers met de betrokken rol.
  • Beheer van RolePermissions via UI is buiten scope van Feature 11-5 en wordt verder uitgewerkt in Feature 17 story 091.

Lifecycle / gedrag

  • Intrekking van een RolePermission verwijdert historie niet.
  • Eerdere autorisatiebeslissingen worden niet achteraf herschreven; nieuwe checks gebruiken de actuele cache of herladen bij cache miss/invalidation.

Designkeuzes

  • De naam RolePermissions volgt de technische richting rol -> permissie en is explicieter dan PermissionRoles.
  • Auditvelden zijn aanwezig omdat wijzigingen direct security-impact hebben.

Foreign keys op databaseniveau

  • Harde foreign keys op databaseniveau: RoleId -> Roles.Id; PermissionId -> Permissions.Id.

Functionele / logische verwijzingen zonder harde FK

  • GrantedByUserId en RevokedByUserId zijn soft links naar Users.Id en worden applicatief gevalideerd.

FK + snapshot

  • FK + snapshot: niet van toepassing binnen deze tabel.

1.6 ProfileAvatars

TabelnaamCategorieDoel / verantwoordelijkheidGerelateerde tabellen
ProfileAvatarsProfielVooraf gedefinieerde profielafbeeldingen waar gebruikers uit kiezen in plaats van vrije uploads.Users
VeldnaamTypeDefaultPKFKVerwijst naarUniqueNullableIndexOpmerking
Iduniqueidentifier-JN-JNJPrimaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default.
Codenvarchar(50)-NN-JNJTechnische code of sleutel van de avatar.
DisplayNamenvarchar(100)-NN-NNNLeesbare naam voor selectie in de UI.
AssetPathnvarchar(500)-NN-JNNPad of referentie naar het vaste avatarbestand.
IsActivebit1NN-NNJSoft delete / actieve status.
CreatedAtUtcdatetime2sysutcdatetime()NN-NNNAanmaakdatum.

Validaties / constraints

  • Code en AssetPath zijn uniek. Alleen vooraf goedgekeurde avatars zijn toegestaan.

Business rules

  • Gebruikers kiezen uit deze vaste set; vrije uploads zijn niet toegestaan.
  • Afbeeldingen zijn ontworpen voor compact gebruik binnen de applicatie, bijvoorbeeld 64x64.

Lifecycle / gedrag

  • Avatars worden normaal via code en/of migratie toegevoegd en niet door eindgebruikers aangemaakt.
  • Deactiveren heeft de voorkeur boven verwijderen.

Designkeuzes

  • Aparte avatartabel voorkomt ongewenste afbeeldingen en houdt profielafbeeldingen veilig, beheersbaar en uniform.

Foreign keys op databaseniveau

  • Geen harde foreign keys op databaseniveau binnen deze tabel.

Functionele / logische verwijzingen zonder harde FK

  • De velden Code functioneren als code-, enum- of sleutelwaarden en verwijzen bewust niet via een harde foreign key. De velden AssetPath bevatten inhoudelijke of technische contextwaarden en zijn daarom logische samenhang zonder harde foreign key.

FK + snapshot

  • FK + snapshot: niet van toepassing binnen deze tabel.

1.7 UserSettings

TabelnaamCategorieDoel / verantwoordelijkheidGerelateerde tabellen
UserSettingsGebruikersinstellingenGebruikersspecifieke voorkeuren en toegankelijkheidsinstellingen die buiten authenticatie en basisprofiel vallen.Users, TeacherLevels, SiteFeatureToggles
VeldnaamTypeDefaultPKFKVerwijst naarUniqueNullableIndexOpmerking
Iduniqueidentifier-JN-JNJPrimaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default.
UserIduniqueidentifier-NJUsers.IdJNJÉén-op-één koppeling naar de gebruiker.
SelectedTeacherLevelIduniqueidentifiernullNNTeacherLevels.IdNJJSoft link naar TeacherLevels.Id; geen harde database-FK vanwege de scheiding met het catalog-/docentstructuurdomein.
DontWarnAgainOnDunnobit0NN-NNNVerborgen voorkeur voor de waarschuwing bij "Geen idee".
AccessibilityHighContrastEnabledbit0NN-NNNVerhoogd contrast binnen OefenHub.
AccessibilityUseDyslexiaFontbit0NN-NNNGebruikt het dyslexielettertype binnen OefenHub.
AccessibilityFontScalePercentint100NN-NNNStandaard lettergrootte in procenten, bijvoorbeeld 90, 100 of 110.
TeacherStudentListNameFormatnvarchar(50)"Default"NN-NNNPersoonlijke voorkeur voor naamweergave in docentoverzichten.
TeacherStudentListSortOrdernvarchar(10)"ASC"NN-NNNPersoonlijke sorteervoorkeur in docentoverzichten.
UpdatedAtUtcdatetime2sysutcdatetime()NN-NNNLaatst bijgewerkt op.

Validaties / constraints

  • UserId is uniek zodat per gebruiker één settingsrecord bestaat. SelectedTeacherLevelId moet null zijn of verwijzen naar een bestaand niveau.
  • AccessibilityFontScalePercent gebruikt alleen vooraf toegestane waarden of een afgebakend bereik.

Business rules

  • Deze tabel bevat alle niet-profielgebonden gebruikersinstellingen, waaronder toegankelijkheidsopties en verborgen voorkeuren.
  • Toegankelijkheidsinstellingen worden in de database opgeslagen en daarnaast gespiegeld naar een cookie zodat ze al vóór inloggen binnen OefenHub toegepast kunnen worden.
  • Inkomende en uitgaande cookies zijn afgeleid van deze brondata en vormen niet de primaire waarheid.

Lifecycle / gedrag

  • Instellingen worden direct opgeslagen zodra de gebruiker ze wijzigt.
  • Wanneer de sitebrede toegankelijkheidsfeature is uitgeschakeld, blijven bestaande gebruikerswaarden bewaard maar worden ze functioneel genegeerd totdat de feature weer is ingeschakeld.

Designkeuzes

  • Voorkeuren en toegankelijkheid zitten bewust in dezelfde tabel, omdat zij samen het instelbare niet-profielgebonden gebruikersgedrag vormen.
  • Authenticatie en wachtwoordbeheer blijven buiten OefenHub in Keycloak.

Foreign keys op databaseniveau

  • Harde foreign keys op databaseniveau: UserId -> Users.Id.

Functionele / logische verwijzingen zonder harde FK

  • SelectedTeacherLevelId is een soft link naar TeacherLevels.Id. De gekozen waarde wordt server-side gevalideerd tegen de toegestane niveaucontext van de gebruiker, maar krijgt geen harde database-FK vanwege de modulegrens. De velden TeacherStudentListNameFormat en vergelijkbare voorkeuren functioneren als code-, enum- of sleutelwaarden en verwijzen bewust niet via een harde foreign key.

FK + snapshot

  • FK + snapshot: niet van toepassing binnen deze tabel.

1.8 Relatiecontext binnen identiteit en autorisatie

Deze aanvullende regels zijn afgeleid uit de uitgewerkte relatie-usecases voor bekijken, uitnodigen, accepteren/afwijzen en ontkoppelen.

Registratie en openstaande relatie-uitnodigingen

  • Na succesvolle registratie controleert OefenHub of er openstaande RelationshipInvitations bestaan met hetzelfde genormaliseerde e-mailadres.
  • Zolang er nog geen Users.Id bestaat, kan een uitnodiging naar een onbekend e-mailadres alleen met RelationshipInvitations.ToEmail worden vastgelegd. ToUserId blijft dan null.
  • Pas nadat een account is aangemaakt en de uitnodiging nog geldig is, mag ToUserId worden gekoppeld en kan er een SystemMessages-record voor de nieuwe gebruiker worden aangemaakt.
  • De externe identity provider maakt het account aan; de koppeling met openstaande OefenHub-uitnodigingen is OefenHub-applicatielogica.

Rolkeuze bij acceptatie van relatie-uitnodigingen

  • Wanneer een relatie-uitnodiging een doelrol vereist die de gebruiker nog niet actief heeft, moet de gebruiker deze rol bewust kunnen kiezen of toevoegen binnen de acceptatieflow.
  • Alleen actieve publieke rollen (Roles.IsPublic = 1) mogen door een gebruiker zelf gekozen worden bij registratie, profieluitbreiding of acceptatie van een relatie-uitnodiging.
  • Niet-publieke rollen, zoals beheerrollen, mogen niet via relatie-uitnodigingen worden toegekend.
  • Het toevoegen van een rol tijdens acceptatie wordt vastgelegd in UserRoles met auditvelden. Het ontkoppelen van een relatie trekt zo'n rol niet automatisch in.

Multi-role context

  • Een gebruiker kan meerdere rollen hebben. De actieve frontendcontext bepaalt welke acties in een scherm beschikbaar zijn.
  • Guardian is de technische/backendwaarde voor de zichtbare rol Ouder/voogd.
  • Roltoekenning is niet hetzelfde als actieve schermcontext; schermen en usecases moeten server-side de juiste rolcontext controleren.

1.9 Profiel-, voorkeuren- en toegankelijkheidscontext

Deze aanvullende regels zijn afgeleid uit de generieke profielusecases voor bekijken, wijzigen, verplicht niveau, profielfoto, toegankelijkheid en voorkeuren.

1.9.1 Afbakening tussen identity provider en OefenHub-profiel

  • OefenHub beheert applicatieprofielgegevens, profielafbeelding, gebruikersinstellingen, voorkeuren en actieve niveaucontext.
  • Authenticatie, wachtwoorden, sessies en credential-lifecycle blijven eigendom van de externe identity provider.
  • E-mailadres en wachtwoord mogen niet rechtstreeks via OefenHub-profielmutaties worden aangepast. OefenHub mag hiervoor hoogstens doorverwijzen naar de identity-providerflow.
  • Users.ExternalId blijft de stabiele technische koppeling met de identity provider en is niet via de GUI wijzigbaar.
  • Server-side sessiecontext bepaalt altijd welk gebruikersprofiel gelezen of gewijzigd wordt; een gebruiker mag nooit via route- of formulierdata een ander profiel als eigen profiel laten verwerken.

1.9.2 Profielgegevens en naamvalidatie

  • Profielwijzigingen mogen alleen de eigen OefenHub-profielvelden aanpassen.
  • DisplayName blijft verplicht voor uniforme weergave in berichten, meldingen, relaties en profielweergaven.
  • De gereserveerde waarde # mag niet via normale profielinvoer als tussenvoegsel worden opgeslagen, omdat deze waarde is gereserveerd voor geanonimiseerde accounts.
  • Een profielmutatie mag geen rollen, relaties, autorisaties, berichten, meldingen of oefenresultaten wijzigen.

1.9.3 Verplicht niveau en actieve niveaucontext

  • Voor gebruikers waarvoor een actief niveau functioneel verplicht is, moet het ontbreken van UserSettings.SelectedTeacherLevelId als blokkade- of attenderingssituatie worden behandeld.
  • De gekozen waarde voor SelectedTeacherLevelId is een soft link en moet server-side worden gevalideerd tegen de actuele toegestane niveaucontext van de gebruiker.
  • Een ontbrekend verplicht niveau mag niet stilzwijgend worden vervangen door een willekeurig standaardniveau.
  • Het instellen of wijzigen van de actieve niveaucontext wijzigt geen docent-leerlingrelatie en kent geen nieuwe niveau-autorisatie toe.

1.9.4 Profielafbeelding

  • Users.ProfileAvatarId verwijst uitsluitend naar een actieve vooraf gedefinieerde avatar in ProfileAvatars.
  • Vrije upload, externe avatar-URL's en gebruikersafbeeldingen buiten de vooraf goedgekeurde set zijn niet toegestaan.
  • Wanneer een eerder gekozen avatar later inactief wordt, blijft historische verwijzing herleidbaar; nieuwe keuzes mogen alleen uit actieve avatars komen.
  • Toegankelijkheidsinstellingen in UserSettings zijn de primaire bron van waarheid zodra een gebruiker is ingelogd.
  • Een technische cookie of vergelijkbare browserwaarde mag toegankelijkheidskeuzes vóór login toepassen, maar vormt geen tweede bron van waarheid.
  • De browserwaarde mag geen persoonsgegevens, autorisatiedata, rolcontext of profielgegevens bevatten.
  • Ongeldige, ontbrekende of corrupte browserwaarden worden genegeerd en mogen geen fouttoestand veroorzaken.
  • Bij login worden profielwaarden leidend; waar nodig worden relevante waarden opnieuw naar de browserwaarde gespiegeld.
  • Wanneer de sitebrede toegankelijkheidsfeature uitgeschakeld is, blijven opgeslagen waarden bewaard maar worden zij functioneel genegeerd totdat de feature weer actief is.

1.9.6 Voorkeuren

  • Voorkeuren wijzigen presentatiegedrag of gebruikersgedrag, maar nooit autorisaties, zichtbare gegevenssets of domeintoegang.
  • Rolgebonden voorkeuren mogen alleen worden gebruikt binnen een rolcontext die de gebruiker daadwerkelijk heeft.
  • Verborgen of systeemgestuurde voorkeuren, zoals DontWarnAgainOnDunno, mogen niet als algemene vrije voorkeurensleutel via de GUI worden gemuteerd.
  • Voorkeuren moeten server-side worden gevalideerd op sleutel, type, toegestane waarde en eventuele rolcontext.

1.10 Accountprovisioning en account-lifecycle

1.10.1 Identity-providerafbakening

De externe identity provider blijft bronhouder voor authenticatie, registratie, wachtwoorden, wachtwoord-reset, e-mailverificatie, credential lifecycle en primaire sessie-uitgifte. OefenHub verwerkt na succesvolle authenticatie uitsluitend de applicatiecontext. OefenHub bewaart geen wachtwoorden, credentialstatus of identity-providerinterne sessiegegevens in domeintabellen.

Een succesvolle authenticatie bij de identity provider geeft pas reguliere OefenHub-toegang wanneer het interne Users-record eenduidig bestaat, actief is en naar een bruikbare applicatiecontext kan worden vertaald.

1.10.2 Users.ExternalId en provisioning

Users.ExternalId is de stabiele koppelsleutel tussen het identity-provideraccount en het interne OefenHub-account. Deze waarde wordt server-side gevuld, is niet via de GUI wijzigbaar en moet uniek zijn binnen de interne accountadministratie.

Eerste login na succesvolle identity-providerauthenticatie mag een intern Users-record aanmaken wanneer nog geen account voor dezelfde ExternalId bestaat. Deze provisioning is idempotent: opnieuw inloggen of een herhaalde callback met dezelfde ExternalId mag nooit een tweede intern account opleveren. Conflicten zoals meerdere interne records met dezelfde ExternalId blokkeren sessieopbouw en worden technisch/accountlogmatig vastgelegd.

1.10.3 UserSettings, rolcontext en frontendcontext

Bij accountprovisioning wordt een bijbehorend UserSettings-record aangemaakt of aantoonbaar geïnitialiseerd. Bij bestaande actieve accounts waarbij UserSettings ontbreekt, mag OefenHub dit veilig herstellen met toegestane defaults. Deze herstelinitialisatie mag geen rollen, relaties, autorisaties, berichten, meldingen, oefendata of niveauautorisaties wijzigen.

Na login bepaalt OefenHub server-side de beschikbare permissions en frontendcontexten op basis van Users, actieve UserRoles, actieve Roles, actieve RolePermissions, actieve Permissions, UserSettings en business rules. Clientstate, routeparameters, oude browsercontext of formulierwaarden zijn nooit bron van waarheid voor rol- of frontendcontext.

1.10.4 Gedeactiveerde, geanonimiseerde en onvolledige accounts

Users.IsActive = false blokkeert reguliere OefenHub-toegang, ook wanneer de identity provider een geldige authenticatiecontext levert. Bij succesvolle sessieverwerking voor een actief intern account wordt Users.LastSeenAtUtc bijgewerkt. Bij geweigerde login wegens gedeactiveerd of geanonimiseerd account wordt LastSeenAtUtc niet als succesvolle activiteit bijgewerkt.

Een geauthenticeerde gebruiker zonder actieve rolcontext krijgt geen reguliere leerling-, ouder/voogd-, docent- of beheerderfrontpage, maar uitsluitend de beperkte context zonder rol. Ontbrekende verplichte profiel- of niveaucontext wordt niet automatisch aangevuld; de gebruiker wordt naar de bestaande profiel- of niveauflow geleid.

1.10.5 Accountverwijdering, anonimisering en sessies

Selfservice-accountverwijdering betreft uitsluitend het eigen interne OefenHub-account. De identity provider blijft buiten deze mutatie. Functioneel leidt verwijderen direct tot anonimisering, blokkade van reguliere toegang en opruiming van afhankelijke toegang.

Bij anonimisering worden zichtbare persoonsgegevens vervangen door de vastgestelde systeemwaarden, waaronder FirstName = Anoniem, MiddleName = #, een niet-voorspelbare systeemcode in de naamweergave en een geanonimiseerd e-mailadres volgens anoniem.<code>@verwijderd.acc. De waarde # is gereserveerd voor systeemgebruik en mag niet via normale gebruikersinvoer worden opgeslagen.

Reguliere login, sessieverwerking en logout introduceren geen aparte OefenHub-domeinsessietabel. Technische sessiestate blijft onderdeel van de identity-provider- en applicatie-infrastructuur.

1.11 Beheerder- en ouder-/voogdcontext

OnderwerpAanscherping
BeheerdercontextToegang tot beheerderusecases wordt uitsluitend server-side bepaald vanuit passende beheerpermissions en domeincontext. Clientstate, routeparameters of zichtbare menu-items mogen geen beheercontext afdwingen.
CombinatierollenWanneer een gebruiker Beheerder, Docent en/of Ouder/voogd combineert, wordt de frontpage runtime samengesteld uit permissiongedreven blokken met vaste volgorde: Beheerder, daarna Docent, daarna Ouder/voogd. De rol Leerling blijft niet combineerbaar.
Niet-publieke rollenRollen zoals Beheerder, TestDocent en de toekomstige Admin-/autorisatiebeheerrol blijven niet-publiek en mogen uitsluitend via expliciete beheer-/adminflow worden toegekend of ingetrokken. Zelfregistratie of profielwijziging mag deze rollen nooit activeren.
AccountstatusUsers.IsActive = false blokkeert reguliere OefenHub-toegang, maar verwijdert geen historie. Heractiveren vereist een expliciete beheerhandeling en server-side hercontrole van rollen en context.
AccountanonimiseringBeheerdergestuurde anonimisering beëindigt actieve toegang, rollen, relaties en afhankelijke contexten volgens domeinregels, terwijl historische audit en functionele reconstructie zonder actuele persoonsgegevens mogelijk blijven.
AccountgeschiedenisAccountbeheer heeft een eigen lifecycle- en beheerlogperspectief waarin statuswijzigingen, rolmutaties, instellingencorrecties en anonimisering herleidbaar zijn.
Gebruikersinstellingen door beheerderEen beheerder mag alleen expliciet ondersteunde gebruikersinstellingen corrigeren. De wijziging mag geen authenticatiegegevens, wachtwoorden, credentials of identity-providerdata aanpassen.
Online-statusAccount-online-status is een readmodel of sessie-afgeleide waarde. Raadplegen daarvan veroorzaakt geen sessie-, rol- of profielmutatie.

Provisioningfouten na geslaagde externe authenticatie

Wanneer de externe identity-providerlogin of -registratie slaagt, maar de interne OefenHub-provisioning of initialisatie niet volledig en consistent kan worden afgerond, mag de gebruiker geen reguliere OefenHub-sessie krijgen. De fout wordt technisch gelogd met correlatie naar de externe identiteit en eventueel het interne Users.Id wanneer dat al transactioneel of tijdelijk is ontstaan. Credentials, tokens, wachtwoorden en identity-providerinterne sessiedata worden daarbij niet opgeslagen.

Deze situatie wordt niet automatisch door de gebruiker opgelost. De gebruikersgerichte route is een generieke accountfout of contactroute, waarna beheer of technische analyse bepaalt of sprake is van een incident, dataconflict of structureel probleem.

Aanvulling - OnboardingCompletedAtUtc

identity.Users bevat een accountniveauveld OnboardingCompletedAtUtc.

VeldTypeBetekenis
OnboardingCompletedAtUtcnullable datetimeoffsetTijdstip waarop de centrale OefenHub-onboarding voor dit account volledig is afgerond. Leeg betekent dat de onboarding-gate nog verplichte stappen moet beoordelen.

Dit veld hoort niet in identity.UserSettings, omdat het geen gebruikersvoorkeur is maar een lifecycle-/accountstatus. De gebruiker kan deze waarde niet zelf aanpassen. Latere onboardingstappen kunnen dezelfde centrale gate uitbreiden; pas wanneer alle verplichte stappen afgerond zijn wordt dit veld gevuld.

1.12 Permissioncache en invalidatie

OefenHub gebruikt permissioncache per gebruiker om te voorkomen dat iedere autorisatiecheck een databasequery veroorzaakt.

OnderwerpRegel
CachebronActieve UserRoles + actieve Roles + actieve RolePermissions + actieve Permissions.
CachekeyInterne Users.Id plus vaste authorization-prefix.
CachewaardeDistinct set van Permissions.Code.
TTLAppsettingswaarde Authorization:PermissionCacheDurationMinutes, functioneel standaard 60 minuten.
CachetypeVoorlopig IMemoryCache; bij toekomstige multi-instance deployment vervangen of aanvullen met gedeelde invalidatie.
LoginLogin probeert de cache te pre-warmen.
CheckCheck gebruikt cache hit; bij cache miss wordt opnieuw uit de database geladen.
UserRole-mutatieLeegt cache van de betrokken gebruiker.
RolePermission-mutatieLeegt cache van alle gebruikers met de betrokken rol.
AccountstatusmutatieLeegt cache van de betrokken gebruiker.
Publieke rolwijzigingLeegt cache van de betrokken gebruiker.

Permissioncache is een performanceoptimalisatie en geen permanente autorisatiebron. De cache mag nooit in browser storage, claims of onbeveiligde clientstate worden opgeslagen.