EN | CS | Přihlásit | Registrovat

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\Per­mission. 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\Per­mission. 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é:


Login to submit a comment