Ověřování oprávnění (Autorizace)
Autorizace zjišťuje, zda má uživatel dostatečná oprávnění například pro přístup k určitému zdroji či pro provedení nějaké akce. Autorizace předpokládá předchozí úspěšnou autentizaci, tj. že uživatel je přihlášen.
V příkladech budeme používat objekt třídy Nette\Security\User, který představuje aktuálního
uživatele a ke kterému se dostanete tak, že si jej necháte předat pomocí dependency injection. V presenterech stačí jen zavolat
$user = $this->getUser()
.
U velmi jednoduchých webů s administrací, kde se nerozlišují oprávnění uživatelů, je možné jako autorizační
kritérium použít již známou metodu isLoggedIn()
. Jinými slovy: jakmile je uživatel přihlášen, má veškerá
oprávnění a naopak.
Role
Smyslem rolí je nabídnout přesnější řízení oprávnění a zůstat nezávislý na uživatelském jméně. Každému
uživateli hned při přihlášení přiřkneme jednu či více rolí, ve kterých bude vystupovat. Role mohou být jednoduché
řetězce například admin
, member
, guest
, apod. Uvádí se jako druhý parametr
konstruktoru SimpleIdentity
, buď jako řetězec nebo pole řetězců – rolí.
Jako autorizační kritérium nyní použijeme metodu isInRole()
, která prozradí, zda uživatel vystupuje
v dané roli:
Jak už víte, po odhlášení uživatele se nemusí smazat jeho identita. Tedy i nadále metoda getIdentity()
vrací objekt SimpleIdentity
, včetně všech udělených rolí. Nette Framework vyznává princip „less code, more
security“, kdy méně psaní vede k více zabezpečenému kódu, proto při zjišťování rolí nemusíte ještě ověřovat,
zda je uživatel přihlášený. Metoda isInRole()
pracuje s efektivními rolemi, tj. pokud je uživatel
přihlášen, vychází z rolí uvedených v identitě, pokud přihlášen není, má automaticky speciální roli
guest
.
Autorizátor
Kromě rolí zavedeme ještě pojmy zdroj a operace:
- role je vlastnost uživatele – např. moderátor, redaktor, návštěvník, zaregistrovaný uživatel, správce…
- zdroj (resource) je nějaký logický prvek webu – článek, stránka, uživatel, položka v menu, anketa, presenter, …
- operace (operation) je nějaká konkrétní činnost, kterou uživatel může či nemůže se zdrojem dělat – například smazat, upravit, vytvořit, hlasovat, …
Autorizátor je objekt, který rozhoduje, zda má daná role povolení provést určitou operaci s určitým
zdrojem. Jde o objekt implementující rozhraní Nette\Security\Authorizator s jedinou metodu
isAllowed()
:
Autorizátor přidáme do konfigurace jako službu DI kontejneru:
A následuje příklad použití. Pozor, tentokrát voláme metodu Nette\Security\User::isAllowed()
, nikoliv
autorizátor, takže tam není první parametr $role
. Tato metoda volá MyAuthorizator::isAllowed()
postupně pro všechny uživatelovy role a vrací true, pokud alespoň jedna z nich má povolení.
Oba parametry jsou volitelné, výchozí hodnota null
má význam cokoliv.
Permission ACL
Nette přichází s vestavěnou implementací autorizátoru, a to třídou Nette\Security\Permission poskytující programátorovi lehkou a flexibilní ACL (Access Control List) vrstvu pro řízení oprávnění a přístupů. Práce s ní spočívá v definici rolí, zdrojů a jednotlivých oprávnění. Přičemž role a zdroje umožňují vytvářet hierarchie. Na vysvětlenou si ukážeme příklad webové aplikace:
guest
: nepřihlášený návštěvník, který může číst a procházet veřejnou část webu, tzn. číst články, komentáře a volit v anketáchregistered
: přihlášený registrovaný uživatel, který navíc může komentovatadmin
: může spravovat články, komentáře i ankety
Nadefinovali jsme si tedy určité role (guest
, registered
a admin
) a zmínili zdroje
(article
, comment
, poll
), ke kterým mohou uživatelé s nějakou rolí přistupovat nebo
provádět určité operace (view
, vote
, add
, edit
).
Vytvoříme instanci třídy Permission a nadefinujeme role. Lze přitom využít tzv. dědičnost rolí, která
zajistí, že např. uživatel s rolí administrátora (admin
) může dělat i to co obyčejný návštěvník webu
(a samozřejmě i více).
Nyní nadefinujeme i seznam zdrojů, ke kterým mohou uživatelé přistupovat.
I zdroje mohou používat dědičnost, bylo by možné například zadat
$acl->addResource('perex', 'article')
.
A teď to nejdůležitější. Nadefinujeme mezi nimi pravidla určující, kdo co může s čím dělat:
Co když chceme někomu zamezit k určitému zdroji přístup?
Nyní, když máme vytvořený seznam pravidel, můžeme jednoduše klást autorizační dotazy:
Totéž platí pro registrovaného uživatele, ten však může i komentovat:
Administrátor může editovat vše, kromě anket:
Oprávění mohou také být vyhodnocována dynamicky a můžeme rozhodnutí nechat na vlastním callbacku, kterému se předají všechny parametry:
Jak ale třeba řešit situaci, kdy nestačí jen názvy rolí a zdrojů, ale chtěli bychom definovat, že třeba role
registered
může editovat zdroj article
jen pokud je jeho autorem? Místo řetězců použijeme
objekty, role bude objekt Nette\Security\Role a zdroj Nette\Security\Resource. Jejich metody
getRoleId()
resp. getResourceId()
budou vracet původní řetezce:
A nyní vytvoříme pravidlo:
A dotaz na ACL se provede předáním objektů:
Role může dědit od jiné role či od více rolí. Co se ale stane, pokud má jeden předek akci zakázanou a druhý povolenou? Jaké budou práva potomka? Určuje se to podle váhy role – poslední uvedená role v seznamu předků má největší váhu, první uvedená role tu nejmenší. Více názorné je to z příkladu:
Role a zdroje lze i odebírat (removeRole()
, removeResource()
), lze revertovat i pravidla
(removeAllow()
, removeDeny()
). Pole všech přímých rodičovských rolí vrací
getRoleParents()
, zda od sebe dvě entity dědí vrací roleInheritsFrom()
a
resourceInheritsFrom()
.
Přidání jako služby
Námi vytvořené ACL si potřebujeme předat do konfigurace jako službu, aby jej začal používat objekt $user
,
tedy aby bylo možné používat v kódu např. $user->isAllowed('article', 'view')
. Za tím účelem si na něj
napíšeme továrnu:
A přidáme ji do konfigurace:
V presenterech pak můžete ověřit oprávnění například v metodě startup()
: