Salut,
Je continue doucement mon apprentissage de CakePHP et je suis confronté à un nouveau problème.
Je voudrais faire un système d'autorisations non pas basé sur le rôle de l'utilisateur mais sur son "lvl", paramètre de type 'int' stocké en BDD.
Comme ça, un 'lvl' 3 aura les même privilèges qu'un 'lvl' 2 plus les privilèges de 'lvl' 3. "Qui peut le plus peut le moins".
Pour l'instant, la solution que j'ai trouvé, c'est tout simplement une méthode que j'ai placé dans mon AppController :
public function requiredLvl($lvl){
if($this->Auth->User()){
if($this->Auth->User('lvl')>=$lvl){
return true;
}
else{
throw new ForbiddenException('Vous n\'avez pas les autorisations nécessaires pour accéder à cette page.');
}
}
}
Et dès que j'en ai besoin, dans n'importe quelle action de mes controllers j'ai juste à apeller cette méthode :
$this->requiredLvl(2);
Ainsi, ça me renvoie vers un erreur 403 si l'utilisateur n'a pas le niveau requis.
Est-ce que ce vous semble une bonne manière de faire au sein de ce Framework ou bien est-ce que je me casse la tête pour rien ?
Dans ce cas là, c'est similaire au système de rôle.
Utilises la fonction isAuthorized qui est plus approprié pour les autorisations.
Tu peux faire une règle générale dans ton App
Controller puis faire des modifications ou des ajouts avec la même méthode dans les controllers spécifiques.
Tu peux également paufiner dans les actions souhaitées.
Par exemple concernant l'utilisateur qui ne peut modifier un article que s'il est level 2 minimum est qu'il est l'auteur de l'article :
public function isAuthorized($user) {
// Tous les utilisateurs inscrits de level 2 minimum peuvent ajouter des articles
if ($this->action === 'add' && $user['level'] >= 2) {
return true;
} else {
$this->Session->setFlash("Vous n'êtes pas autorisé à ajouter un article");
$this->redirect($this->referer());
}
// Le propriétaire du post et les utilisateurs de level 3 minimum peuvent éditer et supprimer l'article
if (in_array($this->action, array('edit', 'delete')) && $user['level'] >= 2) {
$postId = (int) $this->request->params['pass'][0];
if ($this->Post->isOwnedBy($postId, $user['id']) || $user['level'] >= 3) {
return true;
} else {
$this->Session->setFlash("Vous n'êtes pas autorisé à éditer cet article");
$this->redirect($this->referer());
}
} else {
$this->Session->setFlash("Vous n'êtes pas autorisé à éditer un article");
$this->redirect($this->referer());
}
return parent::isAuthorized($user);
}
Et dans le modèle concerné (Post dans l'exemple) :
public function isOwnedBy($post, $user) {
return $this->field('id', array('id' => $post, 'user_id' => $user)) !== false;
}
Après, à toi de faire les modifications selon ton système.
Mais rien que de cette manière là, tu peux faire le stricte nécéssaire via une seule fonction.
Pour ce qui est d'afficher ou non un lien, tu peux directement le faire sur la vue ou sur un élément, via le composant Auth.
Dans ce cas là, tu peux toujours créer un composant.
Tu n'est pas juste limité au components, behaviors et helpers de CakePHP, tu peux aussi en créer de personnalisé.
Tu pourrais utiliser les 3 et avoir ton système de vérification pour les levels des utilisateurs.
Tu aurais par exemple une méthode dans ton helper qui permettrait de vérifier si le lien doit être affiché à l'utilisateur, la vérification pourrait être faite en utilisant ton behavior ou/et ton component, si l'utilisateur peut le voir, le helper retournerait le lien avec la méthode link du helper Html (de la librairie de CakePHP), sinon il ne renvoit rien.
De cette manière dans ta vue, tu pourrais avoir quelque chose comme :
<?= $this->Level->link("Modifier l'article", ['url' => ['controller' => 'posts', 'action' => 'edit', $post['id']], 'level' => 2]) ?>
Ce qui simplifierait pour l'affichage.
Bonjour,
personnlement, je passerai par les ACL, c'est compliqué certes au début mais ça a selon l'avantage, d'un d'être indépendant de ton code, cela veut dire que tu n'as pas besoin d'aller fouillé dans ton code pour vérifier que tel ou tel rôle à accès à telle ou telle page, et de deux c'est modulable à souhait, tu change ta config et hop c'est parti pas besoin d'aller modifier le code.
Après dans ton cas, l'idée semble pas mal mais tu dois exploiter le beforeFilter de ton controller pour ne pas te perdre dans tes actions. C'est à dire que tu gères le droits à tes actions via ton beforeFilter. Est-ce que tu vois ce que je veux dire ?
Bon courage.
Salut, merci pour ta réponse,
Alors, les ACL, je suis effectivement tombé dessus dans la doc mais j'ai trouvé ça assez compliqué pour mon niveau et qui plus est, je n'ai pas vraiment trouvé d'exemple qui se rapprochait de ce que je recherche. Maintenant si tu me dis que c'est par ça qu'il faut passer, je lirai plus attentivement.
Pour le beforeFilter, j'y ai pensé mais je n'étais pas sur que ce soit la bonne méthode, je vais regarder ça de plus près. Mais pourquoi passer par le beforeFilter plutôt que de rentrer la méthode directement dans l'action ? Juste pour que ça soit plus clair à lire ?
Et pour finir, en suivant les exemples de la doc, je pourrais faire une règle personnalisée de l'objet Authorize mais ce dernier ne retournerait que true ou false, ce qui m'obligerait à faire une condition dans mes actions.
D'autant plus que je voudrais rajouter des options dans mes vues, à la manière de wikipédia, un utilisateur qui a le 'lvl' 3 verrait apparaitre une option pour modifier un article par exemple, alors qu'un utilisateur de 'lvl' 2 ne pourrait modifier un article que s'il en est l'auteur.
Je pourrais modifier mon exemple facilement pour arriver à ce résultat, est-ce possible avec les autres manières que tu as cité ? Ou devrais-je utiliser deux méthodes différentes ?
Bonjour.
Ton histoire de level, ça va genre de 1 à 3 et non de 1 à 90 par exemple, c'est ça ?
Ok ok je crois que je commence à comprendre la logique, merci beaucoup pour cet exemple.
Effectivement c'est plus flexible comme ça.
Pour ce qui est d'afficher ou non un lien, tu peux directement le faire sur la vue ou sur un élément, via le composant Auth.
Je voulais justement éviter d'avoir à faire ça mais finalement c'est pas forcément plus compliqué.
Merci encore, bonne continuation ! :)
Je voulais justement éviter d'avoir à faire ça mais finalement c'est pas forcément plus compliqué.
Tu n'es pas obligé de le faire, étant donné que lorsque l'utilisateur cliquera dessus, la fonction isAuthorized prendra la suite.
Mais le fait d'éviter l'affichage de certains éléments, évite des requêtes inutiles pour l'application, mais il n'y a pas de moyen automatique depuis les controlleurs pour afficher ou non un élément html et ce n'est pas non plus l'utilité des controllers de s'occuper de l'affichage d'un élément html.
Surtout que sur ta vue, tu aurais juste à faire quelque chose du genre :
<?php if (AuthComponent::user('level') == 2 && $post['user_id'] == AuthComponent::user('id') || AuthComponent::user('level') >= 3): ?>
<?= $this->Html->link("Modifier l'article", ['controller' => 'posts', 'action' => 'edit', $post['id']]) ?>
<?php endif; ?>
Oui, c'est bien ça, c'est ce que je fais pour l'instant mais en fait ce que j'aurais voulu c'est justement raccourcir cette condition de manière à faire un truc du genre.
<?php if (requiredLvl(2)): ?>
<?= $this->Html->link("Modifier l'article", ['controller' => 'posts', 'action' => 'edit', $post['id']]) ?>
<?php endif ?>
Mais effectivement, c'est peut-être se casser la tête pour pas grand chose...
Ha ouais ça c'est cool, je savais pas qu'on pouvait faire ça.
Je jette un oeil plus en profondeur dès demain, merci encore pour ces petites astuces, bonne soirée.