sql >> Databáze >  >> RDS >> Database

Správa rolí a stavů v systému

Existuje mnoho způsobů, jak vyřešit problém, a to je případ správy rolí a uživatelských stavů v softwarových systémech. V tomto článku najdete jednoduchý vývoj této myšlenky a také několik užitečných tipů a ukázek kódu.

Základní myšlenka

Ve většině systémů je obvykle potřeba mít role a stavy uživatele .

Role souvisejí s právy které mají uživatelé při používání systému po úspěšném přihlášení. Příklady rolí jsou „zaměstnanec call centra“, „manažer call centra“, „zaměstnanec back office“, „manažer back office“ nebo „manažer“. Obecně to znamená, že uživatel bude mít přístup k některým funkcím, pokud má příslušnou roli. Je rozumné předpokládat, že uživatel může mít více rolí současně.

Stavy jsou mnohem přísnější a určují, zda má uživatel práva k přihlášení do systému či nikoli. Uživatel může mít pouze jeden stav včas. Příklady stavů:„pracuje“, „dovolená“, „nemocná“, „smlouva ukončena“.

Když změníme stav uživatele, stále můžeme ponechat všechny role související s tímto uživatelem beze změny. To je velmi užitečné, protože většinou chceme změnit pouze stav uživatele. Pokud uživatel, který pracuje jako zaměstnanec call centra, odejde na dovolenou, můžeme jednoduše změnit jeho stav na „na dovolené“ a po návratu jej vrátit do stavu „pracuje“.

Testování rolí a stavů během přihlašování nám umožňuje rozhodnout, co se stane. Například možná chceme zakázat přihlášení, i když je uživatelské jméno a heslo správné. Můžeme tak učinit, pokud aktuální stav uživatele nenaznačuje, že pracuje, nebo pokud uživatel nemá v systému žádnou roli.

U všech níže uvedených modelů jsou tabulky status a role jsou stejné.

status má pole id a status_name a atribut is_active . Pokud je atribut is_active je nastaven na „True“, to znamená, že uživatel, který má tento stav, aktuálně pracuje. Například stav „working“ by měl atribut is_active s hodnotou True, zatímco ostatní („na dovolené“, „na nemocenské“, „smlouva skončila“) by měly hodnotu False.

Tabulka rolí má pouze dvě pole:id a role_name .

user_account tabulka je stejná jako user_account tabulka uvedená v tomto článku. Pouze v prvním modelu user_account tabulka obsahuje dva další atributy (role_id a status_id ).

Bude představeno několik modelů. Všechny fungují a lze je použít, ale mají své výhody a nevýhody.

Jednoduchý model

První myšlenka by mohla být taková, že jednoduše přidáme vztahy s cizím klíčem do user_account tabulka, odkazující na status a role . Oba role_id a status_id jsou povinné.




To je docela jednoduché na design a také na zpracování dat s dotazy, ale má několik nevýhod:

  1. Neuchováváme žádná historická (ani budoucí) data.

    Když změníme stav nebo roli, jednoduše aktualizujeme status_id a role_id v user_account stůl. To bude zatím fungovat dobře, takže když provedeme změnu, projeví se to v systému. To je v pořádku, pokud nepotřebujeme vědět, jak se statusy a role historicky měnily. Problém je také v tom, že nemůžeme přidat budoucnost role nebo status bez přidávání dalších tabulek do tohoto modelu. Jednou ze situací, kdy bychom pravděpodobně chtěli mít tuto možnost, je situace, kdy víme, že od příštího pondělí bude někdo na dovolené. Dalším příkladem je situace, kdy máme nového zaměstnance; možná chceme vstoupit do jeho postavení a role nyní a aby to někdy v budoucnu nabylo platnosti.

    Komplikace nastává také v případě, že máme plánované akce které používají role a stavy. Události, které připravují data na další pracovní den, obvykle běží, když většina uživatelů systém nepoužívá (např. v noci). Takže pokud někdo zítra nebude pracovat, budeme muset počkat do konce aktuálního dne a pak podle potřeby změnit jeho role a status. Pokud máme například zaměstnance, kteří aktuálně pracují a mají roli „zaměstnanec call centra“, dostanou seznam klientů, kterým musí zavolat. Pokud měl někdo omylem tento status a roli, získá také své klienty a my budeme muset věnovat čas nápravě.

  2. Uživatel může mít vždy pouze jednu roli.

    Obecně by uživatelé měli mít možnost mít více než jednu roli v systému. Možná v době, kdy navrhujete databázi, něco takového není potřeba. Mějte na paměti, že může dojít ke změnám v pracovním postupu/procesu. Klient se například mohl v určité době rozhodnout sloučit dvě role do jedné. Jedním z možných řešení je vytvořit novou roli a přiřadit k ní všechny funkcionality z předchozích rolí. Dalším řešením (pokud uživatelé mohou mít více než jednu roli) by bylo, že klient jednoduše přiřadí obě role uživatelům, kteří je potřebují. Toto druhé řešení je samozřejmě praktičtější a dává klientovi možnost rychleji si systém přizpůsobit svým potřebám (což tento model nepodporuje).

Na druhou stranu má tento model oproti ostatním i jednu velkou výhodu. Je to jednoduché, takže dotazy na změnu statusů a rolí by byly také jednoduché. Také dotaz, který kontroluje, zda má uživatel práva k přihlášení do systému, je mnohem jednodušší než v jiných případech:

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 a @password jsou proměnné ze vstupního formuláře, zatímco dotaz vrací id uživatele a role_id, které má. V případech, kdy uživatelské_jméno nebo heslo nejsou platné, dvojice uživatelské_jméno a heslo neexistuje nebo má uživatel přiřazený stav, který není aktivní, dotaz nevrátí žádné výsledky. Tímto způsobem můžeme zakázat přihlášení.

Tento model lze použít v případech, kdy:

  • jsme si jisti, že v procesu nedojde k žádným změnám, které by vyžadovaly, aby uživatelé měli více než jednu roli
  • nepotřebujeme sledovat změny rolí/stavů v historii
  • Neočekáváme, že budeme mít mnoho správy rolí/stavů.

Časová složka přidána

Pokud potřebujeme sledovat historii role a stavu uživatele, musíme přidat mnoho k mnoha vztahům mezi user_account a role a user_account a status . role_id samozřejmě odstraníme a status_id z user_account stůl. Nové tabulky v modelu jsou user_has_role a user_has_status a všechna pole v nich, kromě časů ukončení, jsou povinná.




Tabulka user_has_role obsahuje údaje o všech rolích, které kdy uživatelé v systému měli. Alternativní klíč je (user_account_id , role_id , role_start_time ), protože nemá smysl přidělovat uživateli stejnou roli ve stejnou dobu více než jednou.

Tabulka user_has_status obsahuje údaje o všech stavech, které kdy uživatelé v systému měli. Zde je alternativní klíč (user_account_id , status_start_time ), protože uživatel nemůže mít dva stavy, které začínají přesně ve stejnou dobu.

Čas zahájení nemůže být null, protože když vložíme novou roli/stav, víme okamžik, od kterého se spustí. Čas ukončení může být nulový v případě, že nevíme, kdy by role/stav skončil (např. role je platná od zítřka, dokud se v budoucnu něco nestane).

Kromě úplné historie můžeme nyní v budoucnu přidávat stavy a role. To však vytváří komplikace, protože při vkládání nebo aktualizaci musíme kontrolovat překrývání.

Uživatel může mít například vždy pouze jeden stav. Než vložíme nový stav, musíme porovnat počáteční a koncový čas nového stavu se všemi existujícími stavy daného uživatele v databázi. Můžeme použít dotaz jako tento:

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 a @end_time jsou proměnné obsahující počáteční a koncový čas stavu, který chceme vložit, a @user_account_id je ID uživatele, pro které jej vkládáme. @end_time může být null a musíme to zpracovat v dotazu. Za tímto účelem jsou hodnoty null testovány pomocí ifnull() funkce. Pokud je hodnota null, je přiřazena vysoká hodnota data (dostatečně vysoká, že když si někdo všimne chyby v dotazu, budeme dávno pryč :). Dotaz kontroluje všechny kombinace počátečního a koncového času na nový stav v porovnání s počátečním a koncovým časem existujících stavů. Pokud dotaz vrátí nějaké záznamy, pak se překrýváme s existujícími stavy a měli bychom zakázat vkládání nového stavu. Také by bylo hezké vyvolat vlastní chybu.

Pokud chceme zkontrolovat seznam aktuálních rolí a stavů (uživatelských práv), jednoduše otestujeme pomocí počátečního a koncového času.

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 a @password jsou proměnné ze vstupního formuláře zatímco @time lze nastavit na Now(). Když se uživatel pokusí přihlásit, chceme zkontrolovat jeho práva. Výsledek je seznam všech rolí, které má uživatel v systému v případě, že se uživatelské jméno a heslo shodují a uživatel má aktuálně aktivní stav. Pokud má uživatel aktivní stav, ale nemá přiřazené role, dotaz nic nevrátí.

Tento dotaz je jednodušší než dotaz v sekci 3 a tento model nám umožňuje mít historii stavů a ​​rolí. Navíc můžeme spravovat stavy a role do budoucna a vše bude fungovat dobře.

Konečný model

Toto je jen představa, jak by se dal předchozí model změnit, pokud bychom chtěli zlepšit výkon. Protože uživatel může mít v jednu chvíli pouze jeden aktivní stav, mohli bychom přidat status_id do user_account tabulka (current_status_id ). Tímto způsobem můžeme otestovat hodnotu tohoto atributu a nebudeme se muset připojit k user_has_status stůl. Upravený dotaz by vypadal takto:

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




To samozřejmě zjednodušuje dotaz a vede k lepšímu výkonu, ale existuje větší problém, který by bylo třeba vyřešit. current_status_id v user_account tabulka by měla být zkontrolována a v případě potřeby změněna v následujících situacích:

  • při každém vložení/aktualizaci/smazání v user_has_status stůl
  • každý den v naplánované události bychom měli zkontrolovat, zda se něčí stav nezměnil (aktuální aktivní stav vypršel nebo se stal aktivním nějaký budoucí stav), a podle toho jej aktualizovat

Bylo by moudré uložit hodnoty, které budou dotazy často používat. Tímto způsobem se vyhneme opakování stejných kontrol a rozdělení práce. Zde se vyhneme připojení k user_has_status tabulky a provedeme změny v current_status_id pouze tehdy, když k nim dojde (vložení/aktualizace/smazání) nebo když se systém tolik nepoužívá (plánované události se obvykle spouštějí, když většina uživatelů systém nepoužívá). Možná bychom v tomto případě z current_status_id ale podívejte se na to jako na nápad, který může pomoci v podobných situacích.


  1. Jak seskupit podle dvou sloupců v SQL

  2. Stanovení a identifikace cílů řádků v prováděcích plánech

  3. Typy dat SQL:5 nejhorších možností, které musíte zastavit ještě dnes

  4. Získejte sadu výsledků z uložené procedury Oracle