Dynamická správa rolí a zdrojů
Ukázková struktura databáze pro
dynamickou správu rolí a zdrojů
V praxi u složitějších aplikací (v aplikacích kde chceme umožňovat právě dynamickou úpravu rolí, zdrojů) si nejspíše nevystačíme se základním objektem Nette\Security\Permission. V malých aplikacích můžeme bez problémů „natvrdo“ nadefinovat seznam rolí, zdrojů a pravidel někde v aplikaci, ale dojde-li na nějaké úpravy nebo rozšíření rolí, budeme muset ručně do aplikace zasahovat.
Nyní si ukážeme implementaci dynamické správy rolí.
U jednoduchých případů lze zaregistrovat jako autorizační handler přímo třídu Nette\Security\Permission. Pro naši potřebu si ale vytvoříme jejího potomka, třídu Acl, kterou obohatíme o konstruktor, který sestaví všechna pravidla, zdroje a role.
U příkladu budeme používat databázový layer dibi.
Uvažujeme se strukturou databáze stejnou jako na obrázku. Umožňuje zadat v tabulce acl resource_id i privilege_id NULL, taková situace znamená, že se pravidlo aplikuje na všechny dostupné resources či privileges.
Následuje kód třídy Acl:
class Acl extends Permission {
public function __construct() {
$model = new AclModel();
foreach($model->getRoles() as $role)
$this->addRole($role->name, $role->parent_name);
foreach($model->getResources() as $resource)
$this->addResource($resource->name);
foreach($model->getRules() as $rule)
$this->{$rule->allowed == 'Y' ? 'allow' : 'deny'}($rule->role, $rule->resource, $rule->privilege);
}
}
Pro načítání rolí a zdrojů z databáze použijeme třídu AclModel:
class AclModel extends Object {
const ACL_TABLE = 'users_acl';
const PRIVILEGES_TABLE = 'users_privileges';
const RESOURCES_TABLE = 'users_resources';
const ROLES_TABLE = 'users_roles';
public function getRoles() {
return dibi::fetchAll('SELECT r1.name, r2.name as parent_name
FROM ['. self::ROLES_TABLE . '] r1
LEFT JOIN ['. self::ROLES_TABLE . '] r2 ON (r1.parent_id = r2.id)
');
}
public function getResources() {
return dibi::fetchAll('SELECT name FROM ['. self::RESOURCES_TABLE . '] ');
}
public function getRules() {
return dibi::fetchAll('
SELECT
a.allowed as allowed,
ro.name as role,
re.name as resource,
p.name as privilege
FROM [' . self::ACL_TABLE . '] a
JOIN [' . self::ROLES_TABLE . '] ro ON (a.role_id = ro.id)
LEFT JOIN [' . self::RESOURCES_TABLE . '] re ON (a.resource_id = re.id)
LEFT JOIN [' . self::PRIVILEGES_TABLE . '] p ON (a.privilege_id = p.id)
ORDER BY a.id ASC
');
}
}
Nakonec nesmíme zapomenout zaregistrovat autorizační handler:
Environment::getServiceLocator()->addService('Nette\Security\IAuthorizator', new Acl);
…nebo v config.ini:
service.Nette-Security-IAuthorizator = Acl
Pokud chceme objekt třídy Acl cachovat (nesmíme zapomenout invalidovat cache při každé změně ACL v databázi):
$cache = Environment::getCache();
if (!isset($cache['acl'])) $cache['acl'] = new Acl();
Environment::getServiceLocator()->addService('Nette\Security\IAuthorizator', $cache['acl']);
Tím jsme si z tabulky vygenerovali objekt, který je potomkem
Nette\Security\Permission a umožňuje nám v aplikaci používat
dynamickou správu rolí, a zaregistrovali potřebnou službu.
Viz také:



