sql >> Database >  >> RDS >> Database

Rollen en statussen in een systeem beheren

Er zijn veel manieren om een ​​probleem op te lossen, en dat is het geval bij het beheren van rollen en gebruikersstatussen in softwaresystemen. In dit artikel vind je een eenvoudige evolutie van dat idee, evenals enkele handige tips en codevoorbeelden.

Basisidee

In de meeste systemen is er meestal behoefte aan rollen en gebruikers statussen .

Rollen zijn gerelateerd aan rechten die gebruikers hebben bij het gebruik van een systeem na succesvol inloggen. Voorbeelden van rollen zijn “callcentermedewerker”, “callcentermanager”, “backofficemedewerker”, “backofficemanager” of “manager”. Over het algemeen betekent dit dat een gebruiker toegang heeft tot bepaalde functionaliteit als hij of zij de juiste rol heeft. Het is verstandig om aan te nemen dat een gebruiker meerdere rollen tegelijk kan hebben.

Statussen zijn veel strenger en ze bepalen of de gebruiker rechten heeft om in te loggen op het systeem of niet. Een gebruiker kan slechts één status . hebben tegelijk. Voorbeelden van statussen zijn:"werkend", "op vakantie", "met ziekteverlof", "contract beëindigd".

Wanneer we de status van een gebruiker wijzigen, kunnen we nog steeds alle rollen met betrekking tot die gebruiker ongewijzigd laten. Dat is erg handig omdat we meestal alleen de status van de gebruiker willen wijzigen. Als een gebruiker die als callcentermedewerker werkt op vakantie gaat, kunnen we zijn status eenvoudig wijzigen in "op vakantie" en deze terugzetten naar de status "werkend" wanneer hij terugkomt.

Door rollen en statussen tijdens het inloggen te testen, kunnen wij bepalen wat er gaat gebeuren. Misschien willen we bijvoorbeeld inloggen verbieden, zelfs als de gebruikersnaam en het wachtwoord correct zijn. We zouden dit kunnen doen als de huidige gebruikersstatus niet impliceert dat hij aan het werk is of als de gebruiker geen rol in het systeem heeft.

In alle onderstaande modellen zijn de tabellen status en role zijn hetzelfde.

Tabel status heeft de velden id en status_name en het attribuut is_active . Als het kenmerk is_active is ingesteld op "True", wat betekent dat de gebruiker met die status momenteel werkt. De status "werkend" zou bijvoorbeeld het attribuut is_active . hebben met een waarde van True, terwijl andere (“op vakantie”, “met ziekteverlof”, “contract beëindigd”) een waarde van False zouden hebben.

De rollentabel heeft slechts twee velden:id en role_name .

Het user_account tabel is hetzelfde als de user_account tabel gepresenteerd in dit artikel. Alleen in het eerste model heeft de user_account tabel twee extra attributen bevatten (role_id en status_id ).

Er zullen enkele modellen worden gepresenteerd. Ze werken allemaal en kunnen worden gebruikt, maar hebben hun voor- en nadelen.

Eenvoudig model

Het eerste idee zou kunnen zijn dat we simpelweg externe sleutelrelaties toevoegen aan het user_account tabel, verwijzend naar tabellen status en role . Beide role_id en status_id zijn verplicht.




Dit is vrij eenvoudig te ontwerpen en ook om gegevens met query's te verwerken, maar heeft een paar nadelen:

  1. We bewaren geen historische (of toekomstige) gegevens.

    Wanneer we de status of rol wijzigen, updaten we eenvoudig status_id en role_id in het user_account tafel. Dat werkt voorlopig prima, dus als we een wijziging aanbrengen, wordt dit in het systeem weerspiegeld. Dit is oké als we niet hoeven te weten hoe statussen en rollen historisch zijn veranderd. Er is ook een probleem dat we toekomst . niet kunnen toevoegen rol of status zonder extra tabellen aan dit model toe te voegen. Een situatie waarin we die optie waarschijnlijk graag zouden willen hebben, is wanneer we weten dat iemand vanaf aanstaande maandag op vakantie gaat. Een ander voorbeeld is wanneer we een nieuwe medewerker hebben; misschien willen we zijn status en rol nu invoeren en ervoor zorgen dat het op een bepaald moment in de toekomst geldig wordt.

    Er is ook een complicatie in het geval dat we geplande evenementen hebben die rollen en statussen gebruiken. Evenementen die gegevens voorbereiden voor de volgende werkdag worden meestal uitgevoerd terwijl de meeste gebruikers het systeem niet gebruiken (bijvoorbeeld 's nachts). Dus als iemand morgen niet werkt, zullen we moeten wachten tot het einde van de huidige dag en dan zijn rollen en status waar nodig veranderen. Als we bijvoorbeeld werknemers hebben die momenteel werken en de rol "callcentermedewerker" hebben, krijgen ze een lijst met klanten die ze moeten bellen. Als iemand per ongeluk die status en rol had, krijgt hij ook zijn klanten en zullen we tijd moeten besteden aan het corrigeren ervan.

  2. Gebruiker kan slechts één rol tegelijk hebben.

    Over het algemeen moeten gebruikers meer dan één rol kunnen hebben in het systeem. Misschien is er op het moment dat u de database aan het ontwerpen bent, zoiets niet nodig. Houd er rekening mee dat er wijzigingen in de workflow/het proces kunnen plaatsvinden. De klant kan bijvoorbeeld op een bepaald moment besluiten om twee rollen samen te voegen tot één. Een mogelijke oplossing is om een ​​nieuwe rol aan te maken en daar alle functionaliteiten van de vorige rollen aan toe te kennen. De andere oplossing (als gebruikers meer dan één rol kunnen hebben) zou zijn dat de client eenvoudigweg beide rollen toewijst aan gebruikers die ze nodig hebben. Natuurlijk is die tweede oplossing praktischer en geeft de klant de mogelijkheid om het systeem sneller aan te passen aan zijn behoeften (wat niet wordt ondersteund door dit model).

Aan de andere kant heeft dit model ook één groot voordeel ten opzichte van andere. Het is eenvoudig en daarom zouden vragen om statussen en rollen te wijzigen ook eenvoudig zijn. Ook is een query die controleert of de gebruiker rechten heeft om in te loggen op het systeem veel eenvoudiger dan in andere gevallen:

select user_account.id, user_account.role_id
from user_account
left join status on user_account.status_id = status.id
where status.is_user_working = True
and user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password;

@user_name en @password zijn variabelen uit een invoerformulier, terwijl de query het gebruikers-ID en het role_id dat hij heeft teruggeeft. In gevallen waarin gebruikersnaam of wachtwoord niet geldig zijn, gebruikersnaam en wachtwoord niet bestaan, of de gebruiker een toegewezen status heeft die niet actief is, levert de zoekopdracht geen resultaten op. Op die manier kunnen we inloggen verbieden.

Dit model kan worden gebruikt in gevallen waarin:

  • we zijn er zeker van dat er geen wijzigingen in het proces zullen zijn waarvoor gebruikers meer dan één rol moeten hebben
  • we hoeven rollen/statuswijzigingen in de geschiedenis niet bij te houden
  • we verwachten niet veel rol-/statusadministratie te hebben.

Tijdcomponent toegevoegd

Als we de rol- en statusgeschiedenis van een gebruiker moeten bijhouden, moeten we veel tot veel relaties toevoegen tussen de user_account en role en het user_account en status . Natuurlijk verwijderen we role_id en status_id van het user_account tafel. Nieuwe tabellen in het model zijn user_has_role en user_has_status en alle velden erin, behalve eindtijden, zijn verplicht.




De tabel user_has_role bevat gegevens over alle rollen die gebruikers ooit in het systeem hebben gehad. De alternatieve sleutel is (user_account_id , role_id , role_start_time ) omdat het geen zin heeft om dezelfde rol op hetzelfde moment meer dan eens aan een gebruiker toe te wijzen.

De tabel user_has_status bevat gegevens over alle statussen die gebruikers ooit in het systeem hebben gehad. De alternatieve sleutel hier is (user_account_id , status_start_time ) omdat een gebruiker niet twee statussen kan hebben die op exact hetzelfde moment beginnen.

De starttijd kan niet nul zijn, want wanneer we een nieuwe rol/status invoegen, weten we vanaf welk moment deze zal starten. De eindtijd kan nul zijn als we niet weten wanneer de rol/status zou eindigen (de rol is bijvoorbeeld geldig vanaf morgen tot er in de toekomst iets gebeurt).

Naast een volledige geschiedenis, kunnen we nu statussen en rollen toevoegen in de toekomst. Maar dit zorgt voor complicaties omdat we moeten controleren op overlapping wanneer we een invoeging of update uitvoeren.

Een gebruiker kan bijvoorbeeld slechts één status tegelijk hebben. Voordat we een nieuwe status invoegen, moeten we de begintijd en eindtijd van een nieuwe status vergelijken met alle bestaande statussen voor die gebruiker in de database. We kunnen een zoekopdracht als deze gebruiken:

select *
from user_has_status
where user_has_status.user_account_id = @user_account_id
and 
(
# test if @start_time included in interval of some previous status
(user_has_status.status_start_time <= @start_time and ifnull(user_has_status.status_end_time, "2200-01-01") >= @start_time)
or
# test if @end_time included in interval of some previous status  
(user_has_status.status_start_time <= @end_time and ifnull(user_has_status.status_end_time, "2200-01-01") >= ifnull(@end_time, "2199-12-31"))  
or  
# if @end_time is null we cannot have any statuses after @start_time
(@end_time is null and user_has_status.status_start_time >= @start_time)  
or
# new status "includes" old satus (@start_time <= user_has_status.status_start_time <= @end_time)
(user_has_status.status_start_time >= @start_time and user_has_status.status_start_time <= ifnull(@end_time, "2199-12-31"))  
)

@start_time en @end_time zijn variabelen die de starttijd en eindtijd bevatten van een status die we willen invoegen en @user_account_id is het gebruikers-ID waarvoor we het invoegen. @end_time kan null zijn en we moeten het in de query verwerken. Hiervoor worden null-waarden getest met de ifnull() functie. Als de waarde null is, wordt een hoge datumwaarde toegewezen (hoog genoeg dat wanneer iemand een fout in de zoekopdracht opmerkt, we al lang weg zijn :). De query controleert alle combinaties van begintijd en eindtijd op een nieuwe status ten opzichte van de begintijd en eindtijd van bestaande statussen. Als de query records retourneert, hebben we overlap met bestaande statussen en moeten we het invoegen van de nieuwe status verbieden. Het zou ook leuk zijn om een ​​aangepaste fout te melden.

Als we de lijst met huidige rollen en statussen (gebruikersrechten) willen controleren, testen we eenvoudig met start- en eindtijd.

select user_account.id, user_has_role.id
from user_account
left join user_has_role on user_has_role.user_account_id = user_account.id
left join user_has_status on user_account.id = user_has_status.user_account_id
left join status on user_has_status.status_id = status.id
where user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password
and user_has_role.role_start_time <= @time and ifnull(user_has_role.role_end_time,"2200-01-01") >= @time
and user_has_status.status_start_time <= @time and ifnull(user_has_status.status_end_time,"2200-01-01") >= @time
and status.is_user_working = True

@user_name en @password zijn variabelen uit het invoerformulier terwijl @time kan worden ingesteld op Nu(). Wanneer een gebruiker probeert in te loggen, willen we op dat moment zijn rechten controleren. Het resultaat is een lijst van alle rollen die een gebruiker in het systeem heeft in het geval gebruikersnaam en wachtwoord overeenkomen en de gebruiker momenteel een actieve status heeft. Als de gebruiker een actieve status heeft maar er geen rollen zijn toegewezen, levert de query niets op.

Deze query is eenvoudiger dan die in sectie 3 en dit model stelt ons in staat om een ​​geschiedenis van statussen en rollen te hebben. Bovendien kunnen we statussen en rollen voor de toekomst beheren en werkt alles prima.

Eindmodel

Dit is slechts een idee van hoe het vorige model zou kunnen worden gewijzigd als we de prestaties wilden verbeteren. Aangezien een gebruiker slechts één actieve status tegelijk kan hebben, kunnen we status_id . toevoegen in het user_account tabel (current_status_id ). Op die manier kunnen we de waarde van dat kenmerk testen en hoeven we ons niet aan te sluiten bij de user_has_status tafel. De gewijzigde zoekopdracht zou er als volgt uitzien:

select user_account.id, user_has_role.id
from user_account
left join user_has_role on user_has_role.user_account_id = user_account.id
left join status on user_account.current_status_id = status.id
where user_account.user_name = @user_name
and user_account.password_hash_algorithm = @password
and user_has_role.role_start_time <= @time and ifnull(user_has_role.role_end_time,"2200-01-01") >= @time
and status.is_user_working = True




Dit vereenvoudigt uiteraard de query en leidt tot betere prestaties, maar er is een groter probleem dat moet worden opgelost. De current_status_id in het user_account tabel moet worden gecontroleerd en indien nodig gewijzigd in de volgende situaties:

  • bij elke invoeg/bijwerking/verwijdering in user_has_status tafel
  • elke dag in een gepland evenement moeten we controleren of de status van iemand is gewijzigd (de huidige actieve status is verlopen of/en een toekomstige status is actief geworden) en deze dienovereenkomstig bijwerken

Het zou verstandig zijn om waarden op te slaan die query's vaak zullen gebruiken. Op die manier voorkomen we dat we steeds dezelfde controles uitvoeren en taken splitsen. Hier vermijden we deelname aan de user_has_status tabel en we zullen wijzigingen aanbrengen op current_status_id alleen wanneer ze gebeuren (invoegen/bijwerken/verwijderen) of wanneer het systeem niet zo vaak in gebruik is (geplande gebeurtenissen worden meestal uitgevoerd wanneer de meeste gebruikers het systeem niet gebruiken). Misschien hebben we in dit geval niet veel baat bij current_status_id maar beschouw dit als een idee dat in vergelijkbare situaties kan helpen.


  1. Equivalent van LIMIT en OFFSET voor SQL Server?

  2. Is er een SQLite-equivalent van MySQL's DESCRIBE [tabel]?

  3. Hoe u uw ProxySQL-loadbalancers kunt clusteren

  4. hoe saldi in een boekhoudsoftware te berekenen met behulp van de postgres-vensterfunctie