Я пытаюсь реализовать ACL в CakePHP. После прочтения документации в руководстве по тортам, а также нескольких других уроков, сообщений в блогах и т. Д., Я нашел отличный учебник Арана Джонсона, который помог заполнить многие пробелы. Его примеры, похоже, противоречат другим, которые я видел, хотя в нескольких местах – особенно в структуре дерева ARO, которую он использует.
В его примерах его группы пользователей настроены как каскадное дерево, причем наиболее общий тип пользователя находится в верхней части дерева, а его дочерние ветви для каждого более ограниченного типа доступа. В другом месте я обычно рассматривал каждый тип пользователя как дочерний элемент того же типа общего типа.
Как вы настраиваете свои ARO и ACO в CakePHP? Все советы приветствуются!
Встроенная система ACL CakePHP действительно мощная, но плохо документированная с точки зрения фактических деталей реализации. Система, которую мы использовали с некоторыми успехами в ряде проектов на основе CakePHP, выглядит следующим образом.
Это модификация некоторых систем доступа на уровне группы, которые были задокументированы в других местах . Целью нашей системы является простая система, в которой пользователи авторизованы на групповом уровне, но могут иметь определенные дополнительные права на элементы, созданные ими или на основе каждого пользователя. Мы хотели избежать необходимости создавать конкретную запись для каждого пользователя (или, более конкретно, для каждой ARO) в таблице aros_acos
.
У нас есть таблица «Пользователи» и таблица «Роли».
пользователей
user_id, user_name, role_id
Роли
id, role_name
Создайте дерево ARO для каждой роли (обычно у нас есть 4 роли – Unauthorized Guest (id 1), Авторизованный пользователь (id 2), Модератор сайта (id 3) и Администратор (id 4)):
cake acl create aro / Role.1
cake acl create aro 1 Role.2 ... etc ...
После этого вы должны использовать SQL или phpMyAdmin или аналогичные для добавления псевдонимов для всех этих элементов, поскольку инструмент командной строки cake не делает этого. Мы используем «Role- {id}» и «User-{id}» для всех наших.
Затем мы создаем ROOT ACO –
cake acl create aco / 'ROOT'
а затем создайте ACOs для всех контроллеров под этим 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()
вызываются в beforeFilter(
) AppController
. В AppControler также isMine
метод isMine
(см. Ниже), который просто проверяет, что 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; }