Вопрос

Я пытаюсь реализовать ACL в CakePHP.После прочтения документации в руководство по торту а также несколько других руководств, сообщений в блогах и т. д., я нашел превосходное руководство Арана Джонсона, которое помогло заполнить многие пробелы.Его примеры, похоже, противоречат другим, которые я видел в некоторых местах, особенно в древовидной структуре ARO, которую он использует.

В его Примеры его группы пользователей настроены как каскадное дерево, при этом наиболее общий тип пользователя находится на вершине дерева, а его дочерние элементы разветвляются для каждого более ограниченного типа доступа.В другом месте я обычно рассматривал каждый тип пользователя как дочерний элемент одного и того же общего типа пользователя.

Как настроить ARO и ACO в CakePHP?Любые советы приветствуются!

Это было полезно?

Решение

Встроенная система ACL CakePHP действительно мощная, но плохо документирована с точки зрения реальных деталей реализации.Система, которую мы с некоторым успехом использовали в ряде проектов на основе CakePHP, выглядит следующим образом.

Это модификация некоторых систем доступа на уровне группы, которые были задокументировано в другом месте.Цель нашей системы — создать простую систему, в которой пользователи авторизуются на уровне группы, но могут иметь определенные дополнительные права на созданные ими элементы или на уровне каждого пользователя.Мы хотели избежать необходимости создавать отдельную запись для каждого пользователя (или, точнее, для каждого ARO) в aros_acos стол.

У нас есть таблица «Пользователи» и таблица «Роли».

Пользователи

user_id, user_name, role_id

Роли

id, role_name

Создайте дерево ARO для каждой роли (обычно у нас есть 4 роли — Неавторизованный гость (идентификатор 1), Авторизованный пользователь (идентификатор 2), Модератор сайта (идентификатор 3) и Администратор (идентификатор 4)):

cake acl create aro / Role.1

cake acl create aro 1 Role.2 ... etc ...

После этого вам придется использовать SQL, phpMyAdmin или аналогичный, чтобы добавить псевдонимы для всех из них, поскольку инструмент командной строки Cake не делает этого.Мы используем «Роль-{id}» и «Пользователь-{id}» для всех наших.

Затем мы создаем ROOT ACO —

cake acl create aco / 'ROOT'

а затем создайте ACO для всех контроллеров под этим ROOT:

cake acl create aco 'ROOT' 'MyController' ... etc ...

Пока всё нормально.Мы добавляем дополнительное поле в таблицу aros_acos под названием _editown который мы можем использовать в качестве дополнительного действия в actionMap компонента ACL.

CREATE TABLE IF NOT EXISTS `aros_acos` (
`id` int(11) NOT NULL auto_increment,
`aro_id` int(11) default NULL,
`aco_id` int(11) default NULL,
`_create` int(11) NOT NULL default '0',
`_read` int(11) NOT NULL default '0',
`_update` int(11) NOT NULL default '0',
`_delete` int(11) NOT NULL default '0',
`_editown` int(11) NOT NULL default '0',
PRIMARY KEY  (`id`),
KEY `acl` (`aro_id`,`aco_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

Затем мы можем настроить компонент Auth для использования метода crud, который проверяет запрошенный контроллер/действие на соответствие AclComponent::check().В app_controller у нас есть что-то вроде:

private function setupAuth() {
    if(isset($this->Auth)) {
        ....
        $this->Auth->authorize = 'crud';
        $this->Auth->actionMap = array( 'index'     => 'read',
                        'add'       => 'create',
                        'edit'      => 'update'
                        'editMine'  => 'editown',
                        'view'      => 'read'
                        ... etc ...
                        );
        ... etc ...
    }
}

Опять же, это довольно стандартная работа CakePHP.Затем у нас есть метод checkAccess в AppController, который добавляет элементы уровня группы, чтобы проверить, следует ли проверять групповой ARO или пользовательский ARO на доступ:

private function checkAccess() {
    if(!$user = $this->Auth->user()) {
        $role_alias = 'Role-1';
        $user_alias = null;
    } else {
        $role_alias = 'Role-' . $user['User']['role_id'];
        $user_alias = 'User-' . $user['User']['id'];
    }

    // do we have an aro for this user?
    if($user_alias && ($user_aro = $this->User->Aro->findByAlias($user_alias))) {
        $aro_alias = $user_alias;
    } else {
        $aro_alias = $role_alias;
    }

    if ('editown' == $this->Auth->actionMap[$this->action]) {
        if($this->Acl->check($aro_alias, $this->name, 'editown') and $this->isMine()) {
            $this->Auth->allow();
        } else {
            $this->Auth->authorize = 'controller';
            $this->Auth->deny('*');
        }
    } else {
        // check this user-level aro for access
        if($this->Acl->check($aro_alias, $this->name, $this->Auth->actionMap[$this->action])) {
            $this->Auth->allow();
        } else {
            $this->Auth->authorize = 'controller';
            $this->Auth->deny('*');
        }
    }
}

А setupAuth() и checkAccess() методы вызываются в AppController's beforeFilter() перезвонить.Есть isMine также в AppControler (см. ниже), который просто проверяет, совпадает ли user_id запрошенного элемента с идентификатором текущего аутентифицированного пользователя.Я оставил это для ясности.

Вот и все, что нужно сделать.Затем вы можете разрешить/запретить определенным группам доступ к определенным acos –

cake acl grant 'Role-2' 'MyController' 'read'

cake acl grant 'Role-2' 'MyController' 'editown'

cake acl deny 'Role-2' 'MyController' 'update'

cake acl deny 'Role-2' 'MyController' 'delete'

Я уверен, что вы поняли.

В любом случае, этот ответ намного длиннее, чем я предполагал, и, вероятно, он не имеет почти никакого смысла, но я надеюсь, что он вам чем-то поможет...

-- редактировать --

Как и просили, вот отредактированный (исключительно для ясности — в нашем шаблонном коде много вещей, которые здесь бессмысленны) isMine() метод, который у нас есть в нашем AppController.Я также удалил много вещей, связанных с проверкой ошибок, но вот суть:

function isMine($model=null, $id=null, $usermodel='User', $foreignkey='user_id') {
    if(empty($model)) {
        // default model is first item in $this->uses array
        $model = $this->uses[0];
    }

    if(empty($id)) {
        if(!empty($this->passedArgs['id'])) {
        $id = $this->passedArgs['id'];
        } elseif(!empty($this->passedArgs[0])) {
            $id = $this->passedArgs[0];
        }
    }

    if(is_array($id)) {
        foreach($id as $i) {
            if(!$this->_isMine($model, $i, $usermodel, $foreignkey)) {
                return false;
            }
        }

        return true;
    }

    return $this->_isMine($model, $id, $usermodel, $foreignkey);
}


function _isMine($model, $id, $usermodel='User', $foreignkey='user_id') {
    $user = Configure::read('curr.loggedinuser'); // this is set in the UsersController on successful login

    if(isset($this->$model)) {
        $model = $this->$model;
    } else {
        $model = ClassRegistry::init($model);
    }

    //read model
    if(!($record = $model->read(null, $id))) {
        return false;
    }

    //get foreign key
    if($usermodel == $model->alias) {
        if($record[$model->alias][$model->primaryKey] == $user['User']['id']) {
            return true;
        }
    } elseif($record[$model->alias][$foreignkey] == $user['User']['id']) {
        return true;
    }

    return false;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top