Role ‚XY‘ does not exist

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Bernard Williams
Člen | 207
+
0
-

Nazdárek,

řeším právě dynamickou správu přístupových polích a zasekl jsem se na chybové hlášce: Role ‚XY‘ does not exist.

Hlavičku scriptu mám:

ob_start();
require './libs/Nette/loader.php';
Debug::enable();
require './libs/dibi/dibi.php';
require './libs/Acl.php';
require './libs/Login.php';
dibi::connect(array(
    'driver'   => 'postgre',
    'host'     => 'localhost',
    'username' => 'root',
    'password' => 'root',
    'dbname' => 'bc',
    'charset'  => 'utf8',
));
Environment::setVariable('tempDir', dirname(__FILE__) . '/temp');

$user = Environment::getUser();
$user->setAuthenticationHandler(new Login);
$user->setAuthorizationHandler(new Acl);

a přístup testuji takto:

if ($user->isAllowed('admin', 'administration', 'show'))

Připomínám, že se nejedná o MVC model. Třída Acl je stejná jako zde. Tuto chybu mi to hází hned po spuštění scriptu – tj. před jakýmkoliv přihlášení. Role by měla být nastavena na Guest.

Děkuji
Bernard

HonzaMac
Člen | 40
+
0
-

Defaultní role je ,,guest''
A určitě bys je měl mít v DB vytvořeny s určitým nastavením dedičnosti. A povolit potřebné zdroje a případně i privilegia.

Ale to asi všechno máš.

David Grudl
Nette Core | 8147
+
0
-
if ($user->isAllowed('administration', 'show')) // bez 'admin'
Bernard Williams
Člen | 207
+
0
-

Tak už se mi podařilo najít chybu a taky ji odstranit. Je potřeba upravit třídy ze stránky Dynamická správa rolí a zdrojů. Konkrétně tedy hlavně metodu getRoles(), která sice vrací všechny role, ale nevrací je ve správném sledu. Je potřeba nejdřív vybrat role na nejvyšší úrovni a pak chronologicky postupovat níž. Docela mě překvapuje, že na to dosud nikdo nenarazil..

Třídy jsem upravil takto:

class AclModel extends Object {

    const ACL_TABLE = 'nette_acl';
    const PRIVILEGES_TABLE = 'nette_acl_privileges';
    const RESOURCES_TABLE = 'nette_acl_resources';
    const ROLES_TABLE = 'nette_acl_roles';

    public function getParentRoles($parent_id, $parent_name, &$roles) {
        $sql = dibi::query('SELECT id, name
                                FROM [' . self::ROLES_TABLE . ']
                                WHERE %and;', array('parent_id' => $parent_id));
        $rows = $sql->fetchAll();
        if (count($sql)) {
            foreach ($rows as $row) {
                $roles[] = array('name' => $row->name, 'parent_name' => $parent_name);
                $this->getParentRoles($row->id, $row->name, $roles);
            }
        }
    }

    public function getRoles() {
        $roles = array();
        $this->getParentRoles(NULL, NULL, $roles);
        return $roles;
    }

    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
        ');
    }
}

class Acl extends Permission {

    public function __construct() {
        $model = new AclModel();

        $roles = $model->getRoles();
        foreach($roles 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);
    }
}
Ondřej Mirtes
Člen | 1536
+
0
-

Ten kód jsem psal já, na jaký problém tam narážíš? Docela hojně to používám, spoustu rolí dědím a na žádný problém jsem nenarazil.

Bernard Williams
Člen | 207
+
0
-

Uvedu příklad. Když budu mít v DB role:

guest	| *NULL*
user	| guest

a zavolám nad třídu Acl(), tak vše proběhne v pořádku. Pokud ale budu mít role v tomto pořadí:

user	| guest
guest	| *NULL*

tak mi třída Acl() skončí s chybou Role ‚guest‘ does not exist., protože role, kterou v prvním kroku používám jako rodičovskou, ještě nebyla vytvořena.

Ondřej Mirtes
Člen | 1536
+
0
-

Aha, to máš pravdu, to se dá považovat za bug. Při postupném plnění databáze na to člověk nemůže narazit (protože při plnění databáze záznamem s ID 1 ještě nemůžeš existovat záznam s ID 2, který by představoval rodiče toho prvního), ale pokud by se pak rozhodl role přeskupit, tak se může stát, že dřívější ID bude odkazovat na nějaké následující.

Zkusím vymyslet nějaké systematické řešení (nejlépe na jeden SQL dotaz :)) a dám vědět :) Díky!

Bernard Williams
Člen | 207
+
0
-

Já používám PostgreSQL a když nějaký záznam edituji, tak se ten záznam zařadí na konec a tím taky vznikla tato chyba.

Pokoušel jsem se najít řešení v podobě jednoho SQL dotazu, ale obávám se, že jinak než postupnou rekurzí to asi řešit nejde.

Majkl578
Moderator | 1364
+
0
-

Na to jsem kdysi narazil taky, řešil jsem to tuším až na úrovni php řazení (usort).