Nette Framework: Ověřování oprávnění a role

Pomalu žádná webová aplikace se neobejde bez mechanismu přihlašování uživatelů a ověřování uživatelských oprávnění. Minule jsme si ukázali, jak je Nette Framework nápomocen při přihlašování uživatelů, a dnes se podíváme na uživatelská oprávnění.
Seriál: Začínáme s Nette Framework (17 dílů)
- Nette Framework: zvyšte svoji produktivitu 10. 3. 2009
- Nette Framework: Odvšivujeme 17. 3. 2009
- Nette Framework: MVC & MVP 24. 3. 2009
- Nette Framework: Refactoring 31. 3. 2009
- Nette Framework: Chytré šablony 7. 4. 2009
- Nette Framework: adresářová struktura aplikace 14. 4. 2009
- Nette Framework: AJAX 21. 4. 2009
- Nette Framework: AJAX (pokračování) 28. 4. 2009
- Nette Framework: AJAX (dokončení) 5. 5. 2009
- Nette Framework: Sessions 12. 5. 2009
- Nette Framework: Přihlašování uživatelů 19. 5. 2009
- Nette Framework: Ověřování oprávnění a role 26. 5. 2009
- Nette Framework: Neprůstřelné formuláře 2. 6. 2009
- Nette Framework: Neprůstřelné formuláře II 9. 6. 2009
- Nette Framework: Neprůstřelné formuláře III 16. 6. 2009
- Nette Framework: Cache 23. 6. 2009
- Nette Framework: Co se do seriálu nevešlo? 30. 6. 2009
Nálepky:
Nejprve mi dovolte připomenout dva důležité termíny: autentizace a autorizace. Pod autentizací se rozumí přihlašování uživatelů, tedy proces, při kterém se ověřuje, zda je uživatel opravdu tím, za koho se vydává. A při autorizaci se zjišťuje, zda má již autentizovaný uživatel dostatečná oprávnění pro přístup k určitému souboru či pro provedené nějaké akce. Což si probereme právě dnes.
Autorizace se může v Nette Framework vyhodnocovat na základě členství uživatele v určitých skupinách či přidělených rolích. Začněme ale od úplného začátku.
Chcete se naučit o Nette víc?
Akademie Root.cz školení Úvod do Nette Framework. Kurz je určen všem programátorům v PHP, kteří se chtějí naučit tvořit webové aplikace rychle a kvalitně, bez bezpečnostních děr. Jako aplikační rámec slouží Nette Framework. Školí sám autor Nette – David Grudl. Máte zájem o jiné školení? Napište nám!
Triviální autorizace
Zopakuji, že autorizace předpokládá předchozí úspěšnou autentizaci, tj. že uživatel je spolehlivě přihlášen. U jednoduchých webů s administrací, do které se přihlašuje jen jeden uživatel, je možné jako autorizační kritérium použít metodu isAuthenticated()
, kterou znáte z minula. Řečeno srozumitelnějším jazykem: jakmile je uživatel přihlášen, má veškerá oprávnění a naopak.
// use NetteEnvironment;
$user = Environment::getUser();
if ($user->isAuthenticated()) { // je uživatel přihlášen?
deleteItem(); // pak má k operaci oprávnění
}
Autorizace na základě rolí
Jemnější řízení oprávnění nabízí tzv. role (nebo též skupiny). 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 pojmenovány například admin
, member
, guest
apod. Upravíme autentizační handler z předchozího dílu tak, aby při úspěšném přihlášení předal identitě i aktuální roli uživatele:
class MyAuthenticator extends Object implements IAuthenticator
{
public function authenticate(array $credentials)
{
...
$row = dibi::fetch('SELECT realname, password, role FROM users WHERE login=%s', $username);
...
return new Identity($row->realname, $row->role); // vrátíme identitu včetně role
}
}
Druhý parametrem konstruktoru Identity
je buď řetězec s názvem role, nebo pole řetězců – rolí.
Jako autorizační kritérium nyní použijeme metodu isInRole()
, která prozradí, zda-li uživatel vystupuje v dané roli:
$user = Environment::getUser();
if ($user->isInRole('admin')) { // je uživatel v roli admina?
deleteItem(); // pak má k operaci oprávnění
}
Výhodou a smyslem rolí je nabídnout přesnější řízení oprávnění, ale zůstat nezávislý na uživatelském jméně.
Ještě musím zmínit jednu důležitou věc. Jak už totiž víte, po odhlášení uživatele nemusí dojít ke smazání jeho identity. Tedy i nadále metoda getIdentity()
vrací objekt Identity
, včetně všech udělených rolí. Nette Framework vyznávající princip „less code, more security“, kdy méně psaní vede k více zabezpečenému kódu, nechce nutit programátora všude psát if ($user->isAuthenticated() && $user->isInRole('admin'))
a proto metoda isInRole()
pracuje s efektivními rolemi. Pokud uživatel je přihlášen, vrací se role udělené v autentizačním handleru, pokud přihlášen není, má virtuální roli guest
.
Autorizační handler
Představuje nejjemnější možné řízení oprávnění. Autorizační handler je objekt implementující rozhraní NetteSecurityIAuthorizator. To má jedinou metodu isAllowed()
s úkolem rozhodnout, zda má daná role povolení provést určitou operaci s určitým zdrojem. Rámcová podoba implementace vypadá takto:
require 'Nette/loader.php';
// pokud používáte verzi pro PHP 5.3, odkomentujte následující řádek:
// use NetteObject, NetteSecurityIAuthorizator;
class MyAuthorizator extends Object implements IAuthorizator
{
public function isAllowed($role = self::ALL, $resource = self::ALL, $privilege = self::ALL)
{
return ...; // vrací bool
}
}
A následuje příklad použití:
$user = Environment::getUser();
// zaregistrujeme autorizační handler
$user->setAuthorizationHandler(new MyAuthorizator);
if ($user->isAllowed('file')) { // má uživatel oprávnění ke zdroji 'file'?
useFile();
}
if ($user->isAllowed('file', 'delete')) { // má uživatel oprávnění ke zdroji 'file' a operaci 'delete'?
deleteFile();
}
V tuto chvíli záleží čistě na vás, jak implementujete autorizační handler. Nicméně Nette Framework disponuje i jednou poměrně silnou předpřipravenou implementací.
Access control list
Jde o třídu NetteSecurityPermission implementující model Access control list. 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. Příklad:
// use NetteEnvironment, NetteSecurityPermission;
$acl = new Permission;
// definujeme role
$acl->addRole('guest');
$acl->addRole('member');
$acl->addRole('administrator', 'member'); // administrator je potomkem member
// definujeme zdroje
$acl->addResource('file');
$acl->addResource('article');
// pravidlo: host může jen prohlížet články
$acl->allow('guest', 'article', 'view');
// pravidlo: člen může prohlížet vše, soubory i články
$acl->allow('member', Permission::ALL, 'view');
// administrátor dědí práva od člena, navíc má právo vše editovat
$acl->allow('administrator', Permission::ALL, array('view', 'edit'));
// zaregistrujeme autorizační handler
Environment::getUser()->setAuthorizationHandler($acl);
Na oprávnění se poté opět dotazujeme metodou isAllowed()
, jak bylo ukázáno výše.
Pokračování příště
Abychom si mohli přihlašování uživatelů a ověřování uživatelských oprávnění vyzkoušet na funkčních příkladech, hodilo by se nám vytvořit a zprovoznit přihlašovací formulář. Příště se proto podíváme na tuto velmi silnou stránku frameworku a naučíme se tvořit neprůstřelné webové formuláře.
Autor článku je vývojář na volné noze, specializuje se na návrh a programování moderních webových aplikací. Vyvíjí open-source knihovny Texy, dibi a Nette Framework a pravidelně pořádá školení pro tvůrce webových aplikací, které od podzimu 2009 nabídne kurz vývoje AJAXových aplikací.
Čekal bych spíš žádnou nebo více.
uzivatel muze mit i vice roli… isAllowed vrati true, kdyz alespon jedna z nich vrati true…
Nevim jestli sem neco neprehlidnul.. ale jestli to dobre chapu… tak nemam zpusob jak napriklad uzivateli ze skupiny admin zakazat napr. prohlizeni clanku (i kdyz skupina admin ma opravneni ke vsemu)… nebo snad ano? muj autorizacni mechanizmus funguje tak ze zkusi najit pozadovane opravneni od prihlaseneho uzivatele… kdyz nenajde tak zkusi jestli ma opravneni prohlizet vsechny stranky.. pak to zkusi pro vsechny skupiny ve kterych je uzivatel a kdyz nenajde vubec zadne nastavene opravneni tak akci zakaze… tim ziskam moznost spravovat opravneni ve skupinach.. a zaroven z techto skupin delat vyjimky… tj muzu se rozhodnout ze Frantovi zakazu pridavat prispevky…
$acl->deny('administrator', 'article', 'view');
… dost zajimavy zpusob ladeni tak popularniho webu.. ehm.
Chápu správně, že práva lze přiřazovat jen rolím, nikoli
uživatelům?
Privilegia lze přiřazovat rolím a zdrojům.
Nicméně o vztahu mezi uživatelem a rolí rozhoduje čistě návrhář
aplikace, tudíž i uživatelské jméno může být rolí (nejlépe třeba
s prefixem: ‚user/franta‘). Pak je možné řídit privilegia na úrovni
uživatelů.
Měl bych dva dotazy:
1) Jakou formou se v Nette řeší situace, kdy má uživatel přístup
pouze k podmnožině objektů daného typu? Například má konkrétní
uživatel možnost upravovat pouze články, které sám vytvořil. V takové
situaci mam pravidlo nastaveno jako „role, stránka, akce, objekt“, ale bere
se v potaz konkrétní přihlášený uživatel.
2) Řeší Nette nějak elegantně situaci, kdy mam například soudní spis,
u něj je kolekce přiložených dokumentů (případně další kolekce „pod
dokumenty“) a já chci vyhodnotit právo přístupu k dokumentu na základě
práva přístupu ke spisu? Nemyslím přitom řešení pro konkrétní
příklad, ale o obecné řešení na úrovni aplikace.
Díky
ad 1) docela podrobně je to zpracované tady http://forum.nettephp.com/…i-permission
ad 2) to by asi byla variace předchozího s využít hierarchie
(dedičnosti), ne?
Díky za odpověď.