Controlul accesului (autorizare)
Autorizarea determină dacă un utilizator are suficiente privilegii, de exemplu, pentru a accesa o anumită resursă sau pentru a efectua o acțiune. Autorizarea presupune o autentificare anterioară reușită, adică faptul că utilizatorul este logat.
În exemple, vom folosi un obiect din clasa Nette\Security\User, care reprezintă utilizatorul curent
și pe care îl obțineți prin trecerea acestuia cu ajutorul injecției
de dependență. În prezentări, este suficient să apelați $user = $this->getUser()
.
Pentru site-uri web foarte simple cu administrare, în care nu se face distincție între drepturile utilizatorilor, este
posibil să se utilizeze metoda deja cunoscută ca și criteriu de autorizare isLoggedIn()
. Cu alte cuvinte: odată
ce un utilizator este logat, el are permisiuni pentru toate acțiunile și invers.
Roluri
Scopul rolurilor este de a oferi o gestionare mai precisă a permisiunilor și de a rămâne independent de numele
utilizatorului. De îndată ce utilizatorul se conectează, i se atribuie unul sau mai multe roluri. Rolurile în sine pot fi
simple șiruri de caractere, de exemplu, admin
, member
, guest
, etc. Acestea sunt
specificate în cel de-al doilea argument al constructorului SimpleIdentity
, fie sub forma unui șir de caractere,
fie sub forma unei matrice.
Ca și criteriu de autorizare, vom folosi acum metoda isInRole()
, care verifică dacă utilizatorul se află în
rolul dat:
După cum știți deja, deconectarea utilizatorului nu îi șterge identitatea. Astfel, metoda getIdentity()
returnează în continuare obiectul SimpleIdentity
, inclusiv toate rolurile acordate. Cadrul Nette Framework aderă
la principiul „mai puțin cod, mai multă securitate“, astfel încât atunci când verificați rolurile, nu trebuie să
verificați și dacă utilizatorul este conectat. Metoda isInRole()
funcționează cu roluri efective, adică
dacă utilizatorul este conectat, se utilizează rolurile atribuite identității, iar dacă nu este conectat, se utilizează în
schimb un rol special automat guest
.
Autorizator
În plus față de roluri, vom introduce termenii de resursă și operațiune:
- rolul este un atribut al utilizatorului – de exemplu, moderator, editor, vizitator, utilizator înregistrat, administrator, …
- resursa este o unitate logică a aplicației – articol, pagină, utilizator, element de meniu, sondaj, prezentator, …
- operațiunea este o activitate specifică, pe care utilizatorul poate sau nu să o facă cu resursa – vizualizare, editare, ștergere, vot, …
Un autorizator este un obiect care decide dacă un anumit rol are permisiunea de a efectua o anumită
operațiune cu o anumită resursă. Este un obiect care implementează interfața Nette\Security\Authorizator cu o singură metodă
isAllowed()
:
Adăugăm autorizatorul la configurație ca serviciu al containerului DI:
Iar următorul este un exemplu de utilizare. Rețineți că de data aceasta apelăm metoda
Nette\Security\User::isAllowed()
, nu pe cea a autorizatorului, deci nu există primul parametru $role
.
Această metodă apelează secvențial MyAuthorizator::isAllowed()
pentru toate rolurile de utilizator și
returnează true dacă cel puțin unul dintre ei are autorizație.
Ambele argumente sunt opționale, iar valoarea lor implicită înseamnă tot.
Permisiune ACL
Nette vine cu o implementare încorporată a autorizatorului, clasa Nette\Security\Permission, care oferă un nivel ACL (Access Control List) ușor și flexibil pentru controlul permisiunilor și al accesului. Atunci când lucrăm cu această clasă, definim roluri, resurse și permisiuni individuale. Iar rolurile și resursele pot forma ierarhii. Pentru a explica, vom prezenta un exemplu de aplicație web:
guest
: vizitator care nu este logat, căruia i se permite să citească și să navigheze în partea publică a web-ului, adică să citească articole, să comenteze și să voteze în sondaje.registered
: utilizator logat, care poate pe deasupra să posteze comentariiadmin
: poate gestiona articole, comentarii și sondaje
Astfel, am definit anumite roluri (guest
, registered
și admin
) și am menționat
resurse (article
, comments
, poll
), pe care utilizatorii le pot accesa sau asupra cărora
pot întreprinde acțiuni (view
, vote
, add
, edit
).
Creăm o instanță a clasei Permission și definim roluri. Este posibil să se utilizeze moștenirea rolurilor, ceea
ce garantează că, de exemplu, un utilizator cu rolul admin
poate face ceea ce poate face un vizitator obișnuit al
site-ului web (și, desigur, mai mult).
Vom defini acum o listă de resurse pe care utilizatorii le pot accesa:
Resursele pot folosi, de asemenea, moștenirea, de exemplu, putem adăuga
$acl->addResource('perex', 'article')
.
Și acum cel mai important lucru. Vom defini între ele regi care să determine cine poate face ce:
Ce se întâmplă dacă vrem să împiedicăm pe cineva să acceseze o resursă?
Acum, după ce am creat setul de reguli, putem pur și simplu să punem întrebări de autorizare:
Același lucru este valabil și pentru un utilizator înregistrat, dar acesta poate face și comentarii:
Administratorul poate edita totul, cu excepția sondajelor:
De asemenea, permisiunile pot fi evaluate în mod dinamic și putem lăsa decizia în seama propriului nostru callback, căruia îi sunt trecuți toți parametrii:
Dar cum să rezolvăm o situație în care numele rolurilor și al resurselor nu sunt suficiente, adică am dori să definim
că, de exemplu, un rol registered
poate edita o resursă article
numai dacă este autorul acesteia?
Vom folosi obiecte în loc de șiruri de caractere, rolul va fi obiectul Nette\Security\Role și sursa Nette\Security\Resource. Metodele lor
getRoleId()
și getResourceId()
vor returna șirurile originale:
Și acum să creăm o regulă:
ACL este interogat prin trecerea unor obiecte:
Un rol poate moșteni unul sau mai multe roluri. Dar ce se întâmplă dacă un strămoș are o anumită acțiune permisă, iar celălalt o are refuzată? Atunci intervine greutatea rolului – ultimul rol din lista rolurilor care trebuie moștenite are cea mai mare greutate, iar primul rol are cea mai mică greutate:
Rolurile și resursele pot fi, de asemenea, eliminate (removeRole()
, removeResource()
), iar regulile
pot fi anulate (removeAllow()
, removeDeny()
). Rețeaua tuturor rolurilor părintești directe
returnează getRoleParents()
. Dacă două entități moștenesc una de la cealaltă returnează
roleInheritsFrom()
și resourceInheritsFrom()
.
Adăugarea ca serviciu
Trebuie să adăugăm ACL-ul creat de noi la configurație ca serviciu pentru a putea fi utilizat de obiectul
$user
, adică pentru a putea fi folosit în cod, de exemplu $user->isAllowed('article', 'view')
. În
acest scop, vom scrie o fabrică pentru acesta:
Și o vom adăuga la configurație:
În prezentatori, puteți verifica apoi permisiunile în metoda startup()
, de exemplu: