Case study · toegangsbeheer · restaurantplatform

Elke medewerker krijgt precies de toegang die bij zijn taak hoort

Medewerkers die reservaties moesten beheren, kregen daarmee ook automatisch toegang tot facturatie en systeeminstellingen — zonder dat dat ooit de bedoeling was. Nu heeft elke medewerker precies de rechten die bij zijn taak horen, niet meer en niet minder.

Veiligheidsrisico

Opgelost

Medewerkers hadden onbedoeld toegang tot billing en gebruikersbeheer. Nu krijgt iedereen precies wat zijn taak vraagt.

Toegangsbeheer

Per actie

Reservaties beheren, instellingen wijzigen en billing inzien zijn nu strikt van elkaar gescheiden.

Uitbreidbaarheid

Eenvoudiger

Nieuwe medewerkers of functies toevoegen zonder risico op onbedoelde toegang tot gevoelige data.

Probleem

Role-based authorization was te grof voor echte restaurantoperaties

In het platform had een gebruiker vaak wel toegang nodig tot reservaties, maar niet tot instellingen, gebruikersbeheer of billing. Toch werd die toegang lang bepaald door één brede restaurantrol. Dat werkte initieel, maar werd snel een bron van uitzonderingen en ongewenste macht.

Waarom dat gevaarlijk werd

Zodra één rol te veel acties afdekt, ontstaat sluipende privilege-uitbreiding. Dan ben je geen toegang meer aan het modelleren, maar uitzonderingen aan het stapelen.

Analyse

Het domein vroeg om beslissingen per actie, niet per brede rol

Meerdere restaurants per gebruiker.

Verschillende werkrollen binnen hetzelfde restaurant.

Acties met uiteenlopende gevoeligheid, van reservaties bekijken tot billing beheren.

Backward compatibility met de bestaande Restaurant-owner setup.

Mijn standpunt is hier eenvoudig: zodra verschillende gebruikers verschillende acties uitvoeren binnen hetzelfde domein, volstaan roles niet meer als eindmodel. Ze blijven nuttig, maar enkel als grof startpunt.

Oplossing

Toegang per actie, niet per brede rol

Het systeem werkt nu met twee lagen: rollen als grove groepering (manager, medewerker), en voor elke specifieke actie een aparte toegangscontrole. Wie geen expliciete toestemming heeft, krijgt geen toegang.

Elke actie heeft een eigen toestemming

Reservaties bekijken, instellingen aanpassen, gebruikers beheren en billing inzien zijn nu vier aparte bevoegdheden — niet gebundeld in één brede restaurantrol.

Geen toegang tenzij expliciet toegekend

Ontbreekt een toestemming? Dan is het antwoord automatisch nee. Dat voorkomt dat nieuwe functies onbedoeld openstaan voor de verkeerde mensen.

Roles vs. Permissions

Wie mag wat doen?

ActieManager
(rol)
Medewerker
(rol)
Permission
(nieuw)
Reservaties beherenManageReservations
Instellingen wijzigenManageSettings
GebruikersbeheerManageUsers
Billing inzienViewBilling

Vóór: Medewerker had door zijn rol ongewild toegang tot instellingen én gebruikersbeheer.

Na: Elke actie wordt getoetst op een specifieke permission — deny by default.

Security principle

Deny by default als expliciete ontwerpkeuze

Belangrijk detail in de implementatie: als er geen expliciet recht is, is het antwoord gewoon false. Dat voorkomt impliciete toegang en maakt nieuwe features veiliger uitbreidbaar.

Waarom dit beter is

Beveiliging faalt hier naar gesloten toestand. Dat is voorspelbaarder dan permissies achteraf dichtzetten wanneer een feature al live staat.

Trade-off

Meer onderhoud, maar ook veel betere controle

Voordelen

  • Fijnmazige controle per actie.
  • Beter geschikt voor multi-tenant restaurantbeheer.
  • Makkelijker uitbreidbaar per feature zonder nieuwe rolchaos.

Nadelen

  • Meer code en onderhoud.
  • Extra mapping tussen rollen en permissions.
  • Migratie vereist backward compatibility zolang oude accounts blijven bestaan.

Backward compatibility

Praktische fallback, maar wel gecontroleerde technische schuld

We hebben de bestaande owner-setup niet abrupt gebroken. Accounts die al actief waren, bleven gewoon werken tijdens de overstap. Dat is praktisch voor migratie, maar precies daarom moet je het ook benoemen als tijdelijke maatregel en niet als permanent ontwerp.

Waarom dit werkt

De overstap naar het nieuwe model hoeft niet alles op één dag te veranderen. Bestaande gebruikers blijven werken, nieuwe functies worden meteen correct beveiligd. Zo migreer je zonder operationele onderbreking.

Resultaat

Voorspelbaarder, leesbaarder en beter onderhoudbaar

Reservaties wijzigen, instellingen aanpassen en gebruikers beheren kregen elk hun eigen permission.

Een medewerker kan nu reservaties beheren zonder impliciet toegang te krijgen tot alle restaurantinstellingen.

Controlleracties zijn leesbaarder geworden doordat ze expliciet de vereiste toestemming vragen.

Het autorisatiemodel is beter uitbreidbaar per feature zonder nieuwe rol-explosie.

Een actie als ToggleReservatiesActief hoeft daardoor niet langer “restaurantbreed” beschermd te worden, maar kan exact gekoppeld worden aan de juiste permission voor die functionaliteit.

Lessons learned

Roles voor grouping, permissions voor echte controle

Start met een kleine permission set en laat ze groeien op basis van echte use cases.

Hou backward compatibility expliciet in het oog als bestaande accounts al actief zijn.

Centraliseer authorization in een service in plaats van logica te verspreiden over controllers.

Test per actie en per permission, niet alleen per rolnaam.

Gebruik deny-by-default als veilige basisregel.

Ik zou dit patroon niet aanraden voor een zeer klein team met een triviale adminomgeving. Maar in een platform waar meerdere gebruikers verschillende taken uitvoeren binnen één restaurant, is dit de juiste balans tussen veiligheid en werkbaarheid.

Takeaway

Als toegang per feature verschilt, gebruik roles dan niet meer als eindmodel.

Gebruik roles als grouping en permissions als beslissingslaag. Dat maakt restaurantbeheer veiliger, duidelijker en veel beter schaalbaar in onderhoud, zonder in vage enterprise-complexiteit te vervallen.

Voor de technisch geïnteresseerde

Implementatiedetails

De onderstaande codevoorbeelden tonen hoe het toegangsmodel concreet is uitgewerkt in C# met een enum, servicecontract en fallback-logica.

Permission enum — alle beschikbare rechten

public enum RestaurantPermission
{
    ViewReservations,
    ViewStatistics,
    ViewSettings,
    EditReservation,
    ManageOpeningHours,
    ManageTables,
    ManageUsers,
    ManageBilling,
    ManageContract
}

Servicecontract

Task<bool> HasPermissionAsync(string userId, string restaurantId, RestaurantPermission permission);
Task<IEnumerable<RestaurantRole>> GetUserRolesForRestaurantAsync(string userId, string restaurantId);

Fallback voor bestaande owner-accounts (tijdelijk)

if (userId == restaurantId)
{
    return RolePermissions.HasPermission(RestaurantRole.Owner, permission);
}

Veelgestelde vragen

Waarom zijn permissions hier beter dan roles alleen?
Omdat roles te grof worden zodra meerdere gebruikers verschillende verantwoordelijkheden hebben binnen hetzelfde restaurant. Permissions laten je exact modelleren welke actie wel of niet mag.
Zijn roles dan overbodig geworden?
Nee. Roles blijven nuttig als groepering of vertrekpunt. Ze zijn alleen niet meer het eindmodel dat rechtstreeks elke gevoelige actie bepaalt.
Waarom is deny-by-default zo belangrijk?
Omdat impliciet toegang geven altijd riskanter is. Wanneer een recht ontbreekt, moet het systeem veilig falen. Dat voorkomt dat nieuwe features per ongeluk openstaan voor de verkeerde gebruikers.
Is die owner-fallback geen probleem?
Functioneel is ze handig voor migratie, maar architecturaal is het technische schuld. Daarom moet zo’n fallback bewust tijdelijk blijven en later uitgefaseerd worden.
Mitch

Werk jij met een systeem waarin toegangsrechten telkens uitzonderingen en workarounds nodig hebben?

Plan een vrijblijvende digitale kennismaking met Mitch en ontdek wat wij voor jouw organisatie kunnen betekenen.