Le système ACL intégré de CakePHP est très puissant, mais peu documenté en termes de détails d'implémentation réels. Un système que nous avons utilisé avec succès dans un certain nombre de projets basés sur CakePHP est le suivant. Il s'agit d'une modification de certains systèmes d'accès de niveau groupe documented elsewhere. Les objectifs de notre système sont d'avoir un système simple où les utilisateurs sont autorisés au niveau du groupe, mais ils peuvent avoir des droits supplémentaires spécifiques sur les éléments qui ont été créés par eux, ou par utilisateur. Nous voulions éviter de devoir créer une entrée spécifique pour chaque utilisateur (ou plus précisément pour chaque ARO) dans la table aros_acos
.
Nous avons une table d'utilisateurs, et une table de rôles.
Utilisateurs
user_id, user_name, role_id
Rôles
id, role_name
Créer l'arbre ARO pour chaque rôle (nous avons généralement 4 rôles - Invité non autorisée (id 1), autorisé Utilisateur (ID 2), Modérateur de site (ID 3) et Administrateur (ID 4)):
cake acl create aro/Role.1
cake acl create aro 1 Role.2 ... etc ...
Après cela, vous devez utiliser SQL ou phpMyAdmin ou similaire à ajouter des alias pour tous ceux-ci, comme l'outil de ligne de commande de gâteau ne le fait pas. Nous utilisons 'Role- {id}' et 'User- {id}' pour tous les nôtres.
Nous créons alors un ROOT BCA -
cake acl create aco/'ROOT'
puis créer ACOs pour tous les contrôleurs sous ce ROOT un:
cake acl create aco 'ROOT' 'MyController' ... etc ...
Jusqu'à présent, si normal. Nous ajoutons un champ supplémentaire dans la table aros_acos, appelé _editown
, que nous pouvons utiliser comme action supplémentaire dans le composant actionMap du composant 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;
On peut alors configurer le composant Auth d'utiliser la méthode « CRUD », qui valide le contrôleur/action demandée contre le contrôle d'un composant Acl(). Dans le app_controller nous avons quelque chose le long des lignes de:
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 ...
}
}
Encore une fois, cela est assez commandes standard CakePHP. Nous avons une méthode de checkAccess dans le AppController qui ajoute à la substance au niveau du groupe pour vérifier si pour vérifier un groupe ARO ou un utilisateur ARO pour accéder alors:
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('*');
}
}
}
Les méthodes setupAuth()
et checkAccess()
sont appelés dans le AppController
' s beforeFilter(
) rappel. Il existe également une méthode isMine
dans AppControler (voir ci-dessous) qui vérifie simplement que l'id_utilisateur de l'élément demandé est le même que l'utilisateur actuellement authentifié. Je l'ai laissé pour plus de clarté.
C'est vraiment tout ce qu'il y a à faire. Vous pouvez alors autoriser/refuser l'accès à certains groupes spécifiques 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'
Je suis sûr que vous obtenez l'image.
Quoi qu'il en soit, cette façon de la réponse plus que ce que je voulais que ce soit, et il est sans doute à côté de pas de sens, mais j'espère que ce sera une aide pour vous ...
- modifier -
Comme demandé, voici un édité (purement pour la clarté - il y a beaucoup de choses dans notre code standard qui n'a pas de sens ici) isMine()
méthode que nous avons dans notre AppController. Je l'ai enlevé beaucoup de choses de la vérification des erreurs aussi, mais c'est l'essence de celui-ci:
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;
}
Je suis ici à la recherche de isMine(), que j'ai essayé de définir, mais il semble que cela existe déjà. –