Pokročilá architektura oprávnění v Nette
- tobice
- Člen | 30
Začínáme s vývojem poměrně komplexního softwaru a potřebujeme vymyslet/doporučit architekturu oprávnění. Klíčové požadavky:
- Oprávnění musí být dynamicky spravovatelná z GUI.
- Oprávnění musí být nějak jednoduše testovatelná přímo v Latte šablonách (tj. aby bylo možné jednoduše skrývat odkazy, ke kterým uživatel nemá oprávnění).
- PHP kód by měl obsahovat co nejméně balastu (chtěli bychom se vyhnout tomu psát pořád dokola isAllowed(…)).
Minulá verze softwaru byla napsána v Code Igniteru a oprávnění se udělovala přímo na jednotlivé URL adresy. Administrační rozhraní bylo schopno reflexí proskenovat všechny controllery a vypsat public metody (odpovídají URL adresám), které se pak skrz role a skupiny přiřazovaly uživatelům. Díky tomu bylo možné jednoduše kontrolovat, zda uživatel na danou adresu může vstoupit, v kódu se oprávnění nemuselo vůbec řešit a i při generování GUI bylo možné rovnou skrýt nepovolené odkazy.
Problém je, že v Nette by tento přístup tak elegantně nefungoval (třeba jen kvůli komponentám) a také to není příliš „čisté“ řešení. Samozřejmě by bylo vhodnější přesunout kontrolu oprávnění do nějaké samostatné bezpečnostní vrstvy mezi presenter a modelovou vrstvu. Otázka je, jak ji naimplenetovat, aby splňovala výše uvedené požadavky (a zda jsou vůbec splnitelné).
Nehledáme ani tak hotový kód, jako spíše nějaké popsaný pattern, kterého bychom se mohli držet.
- Oli
- Člen | 1215
Na úrovni presenteru můžeš mít nějakou službu, které předáš uživatele, oprávnění a v jaké jsi action a presenteru. Tím by jsi to měl v podsatě povolené na úrovni URL a nemusíš pak v podstatě zasahovat do presenteru.
Co se šablon týče, tam nevím o žádným jiným řešení než testovat každej odkaz. Šlo by leda udělat nějaký makro nebo si přepsat link().
- enumag
- Člen | 2118
V Nette popsaný přístup může fungovat poměrně elegantně a to i včetně komponent. Tvé požadavky splnitelné jsou – kromě dynamické správy oprávnění (která je nezávislá na zbytku takže ji lze dodělat) to mám i implementované.
Hlavní myšlenky přístupu který používám popsal Oli poměrně dobře. Jen přidám, že naprosto klíčová je zde metoda checkRequirements a je vhodné mít ji povolenou i pro továrničky komponent (což nyní Nette nemá). K tomu několik maker a služba na ověřování + interface přes který je možné definovat pravidla. Díky tomu jsem schopen odebrat z presenteru i anotace s definicí těch pravidel, které jsem tam měl dříve.
Dále bude problém s oprávnění mi typu „redaktor má právo mazat pouze své články“, ale i to mám vyřešené pomocí jisté magie.
Celkově kvůli tomu mám asi 6 extensions, takže to není vůbec easy task, ale šlape to skvěle.
- Tomáš Votruba
- Moderator | 1114
@enumag: Budu brzy resit neco podobneho. Jsi ochotny se o sve reseni podelit? Pres extensions se mi to moc libi.
- enumag
- Člen | 2118
@Tomáš Votruba: Jak víš, původně jsem měl v plánu to vypustit při vydání Nette 2.1 (které nakonec přišlo podstatně dříve než jsem očekával). Jsou tu ale důvody, kvůli kterým jsem to přehodnotil:
- Stálo mě to několik set hodin času, nedokážu odhadnout přesněji.
- Vývoj mi trval něco přes rok (mám k tomu i dokumentaci a testy).
- Poslední nápady které jsem k tomu měl jsem dokončil teprve před několika dny.
- Stále nemám čas nad tím napsat pořádný systém a ani nemám nikoho kdo by to udělal za mne.
- Dá se říct že mi to ještě nevydělalo ani korunu. Jen jsem se při tom naučil spoustu nových věcí a dokázal sám sobě že to zvládnu.
V podstatě nevidím důvod proč to pouštět ven když to sám ještě nevyužívám. Řešení je v podstatě kompletní protože až dnes dokážu říct že mi došly nápady co tam ještě opravit či přidat. Začalo to jako experiment autorizace pomocí anotací, dnes to není omezené ani na autorizaci ani na anotace takže nemyslím že by mi ostatní začali posílat nějaké větší PR s novými funkcemi. Čili, co bych tím získal?
Jsem ochoten se domluvit soukromě, ale prozatím bohužel nemám jiný důvod to zveřejňovat než vrátit komunitě to co mi dala a udělat si jméno. Jméno budu potřebovat až si budu hledat zaměstnání, což zatím není aktuální (navíc stejně dost možná založím vlastní firmu) a přispět vývoji Nette se snažím i tak poměrně často (např. včerejší RFC).
Editoval enumag (10. 2. 2014 15:25)
- romiix.org
- Člen | 343
Niečo podobné som 2× riešil. Prvý krát som zaviedol sadu anotácií pomocou ktorých bolo možné definovať prístupy pre rôzne role a používateľov. Niektoré prípady (napr. mazanie vlastných článkov) boli veľmi zložito a neintuitívne riešiteľné.
V ďalšom projekte na to šiel trochu inak. Definoval som zoznam zdrojov a subjektov.
Zdroje obsahovali presenter a akciu, prípadne ešte jemnejšie delenie. Ak končil názov oprávnenia na pomlčku, tak sa bral do úvahy aj identifikátor. Tzn. zdrojom article sa dalo povoliť/zakázať prístup k celému presentru article, ale napríklad zdroj article-delete- (pomlčka na konci) sa dalo nastaviť oprávnenie na zmazanie konkrétneho článku. ID článku sa k oprávneniu v prípade potreby prilepilo. V konfigurácií stačilo nastaviť, že v prípade ak začína názov oprávnenia na article, tak sa berie do úvahy napr. identifikátor article.id_article.
Subjekty boli používatelia alebo role ktorých oprávnenie bolo nastavované + ich identifikátor.
Vo výsledku je takto možné hierarchicky nastaviť oprávnenie na ľubovoľnú akciu pre ktoréhokoľvek používateľa/rolu.
Do odkazov som pridával
n:if="$acl->isAllowed("article", "article-delete", "artice-delete-$id)"
.
Obdobne funguje overovanie v ľubovoľnej metóde. isAllowed
vracia boolean
a checkRequirements
v prípade
FALSE
presmeruje na prihlásenie alebo vyhodí výnimku.
Ako najvhodnejší postup overovanie práv mi nakoniec vyšiel postup:
- Ak nie je nastavené oprávnenie na žiadny zo zdrojov (v kombinácií
s aktuálnou rolou a používateľom) tak je je výsledkom
FALSE
(predvolene). - Ak je niektoré oprávnenie
TRUE
, stále ho môže prepísaťFALSE
. - Oprávnenie používateľa má prednosť pred oprávnením skupiny.
K tomuto postačí jednoduchá tabuľka resource
,
resource_id
, subject
, subject_id
a
access
(TRUE
/FALSE
).
Na automatizované pridávanie/odoberanie práv v prípade nejakých akcií,
zmien stavov a pod. sa mi osvedčilo zavesenie na udalosti pomocou
Kdyby/Events
. V ideálnom prípade by bola štruktúra tabuľky so
zoznamom oprávnení premakanejšia a cez triggre a cudzie kľúče by sa
automaticky mazali nepotrebné pravidlá, ale k tomu nebol čas.
Ako GUI postačil obyčajný GRID.