From ecf07ed41539832f33aad71c8f99002d97444149 Mon Sep 17 00:00:00 2001 From: Etienne Pallier Date: Fri, 1 Sep 2017 11:08:46 +0200 Subject: [PATCH] Début de mise en place du nouveau système ACL simplifié basé sur les tableaux $easyACL (pas encore actif) --- README-LABINVENT.md | 14 +++++++------- src/Controller/AppController.php | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/Controller/MaterielsController.php | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/Template/Layout/default.ctp | 2 +- 4 files changed, 279 insertions(+), 16 deletions(-) diff --git a/README-LABINVENT.md b/README-LABINVENT.md index 93a670e..ea223bb 100755 --- a/README-LABINVENT.md +++ b/README-LABINVENT.md @@ -47,14 +47,11 @@ Logiciel testé et validé sur les configurations suivantes : VERSION ACTUELLE -Date: 30/08/2017 -Version: 2.7.9 - - Renommages plus explicites des fonctions et variables - - Bugfix important sur détection du role "Utilisateur" pour les personnes du ldap qui ne sont pas dans la table utilisateurs !!! - - Creation d'un fake utilisateur pour simuler un utilisateur qui n'est pas dans la table utilisateurs (et tester le cas ci-dessus) - - Ajout de tests avec cet utilisateur +Date: 01/09/2017 +Version: 2.8.0 + - Début de mise en place du nouveau système ACL simplifié basé sur les tableaux $easyACL (pas encore actif) -Version majeure en cours (2.7): https://projects.irap.omp.eu/versions/162 +Version majeure en cours (2.8): https://projects.irap.omp.eu/versions/162 ROADMAP: https://projects.irap.omp.eu/projects/labinvent/roadmap @@ -68,6 +65,9 @@ CHANGEMENTS IMPORTANTS (MILESTONES) Liste complète des évolutions: https://gitlab.irap.omp.eu/epallier/labinvent/commits/master +??/09/2017 Version: 2.8.X + - Nouveau système ACL simplifié basé sur les tableaux $easyACL + 30/08/2017 Version: 2.7.9 - fonction intelligente AppController::getUserRole() qui donne le role "Utilisateur" par défaut pour un utilisateur non privilégié - Refactorisation des ACL (authorizations) dans isAuthorized() et beforeFilter() diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 18d811e..3a96f24 100755 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -55,7 +55,154 @@ class AppController extends Controller { // Current ROLE (by default = "Utilisateur") private $CURRENT_ROLE = null; - + // EP 08/2017 + protected $easyACL = array( + + /** Default ACL for ALL (logged) users + * + * Les actions non mentionnées sont accessibles à tous (par défaut), + * exemple 'find', 'index'... + * Ces default ACL peuvent être surchargées pour un profil précis + * (par exemple pour 'USER' qui est plus restreint) + */ + //'ALL' => array ( + 'DEFAULT' => array ( + // 'action' => 'condition for execution' (= 'Y', 'N', or ''), + // with like "'field name' == 'value'" + // CRUD actions : + //'edit' => 'N', // update + //'delete' => 'N', + 'autre' => 'Y', + ), + + // Ajoute des ACL plus spécifiques (ci-dessus) pour le profil USER qui est plus restreint + // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL') + 'USER' => array ( + // CRUD actions + 'add' => 'Y', // C + 'index' => 'Y', // R all + 'view' => 'Y', // R one + //'edit' => 'N', // U + 'edit' => 'is_creator', // is_creator = (nom_createur == CURRENT_USER_NAME) + 'delete' => 'N', // D + // OTHER actions + 'find' => 'Y', // create + /* ceci n'a aucun sens car l'action sur le modèle "Pages" s'appelle toujours "display" (et non pas tools, infos, ou printers...) + 'tools' => 'N', + 'infos' => 'N', + 'printers' => 'N', + */ + ), + + // Surcharge des ACL par défaut (ci-dessus) pour le profil RESPONSABLE qui est plus restreint + // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL') + 'RESPONSABLE' => array ( + ), + + // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMIN + 'ADMIN' => array ( + //'add' => 'Y', // create + 'edit' => 'Y', // update + //'delete' => 'Y', // update + ), + + // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMINPLUS + 'ADMINPLUS' => array ( + //'edit' => 'Y', // update + ), + + // Surcharge des ACL par défaut (ci-dessus) pour le profil SUPERADMIN + 'SUPERADMIN' => array ( + //'add' => 'Y', // create + //'edit' => 'Y', // update + 'delete' => 'Y', + ), + ); // $easyACL + + + //@todo + public function startsWith($haystack, $needle) { + return true; + } + //@todo + public function endsWith($haystack, $needle) { + return true; + } + public function evalACL($condition) { + // Simple case + if ($condition == 'Y') return true; + if ($condition == 'F') return false; + // Complex case + //@todo + return true; + } + public function evalSpecificRule($condition, AppController $controller, $user, $role, $action, $id=null) { + // if starts with "&&" eval DEFAULT rule && specific rule + if ($this->startsWith($condition,'&&')) return $this->evalACL($controller->easyACL['DEFAULT'][$action]) && $this->evalACL($condition); + // otherwise, eval only specific rule + return $this->evalACL($condition); + } + /** + * @param AppController $controller // a subclass of AppController (MaterielsController or any other) + * @param array $user + * @param string $role + * @param string $action + * @param string $id + */ + public function isAuthorizedAction(AppController $controller, $user, $role, $action, $id=null) { + + // 1) controller specific (role) rule for action + $condition = $controller->easyACL[$role][$action]; + if (isset($condition)) return $this->evalSpecificRule($condition, $controller, $user, $role, $action); + + // 2) AppController ($this) specific (role) rule for action + $condition = self::easyACL[$role][$action]; + if (isset($condition)) return $this->evalSpecificRule($condition, $this, $user, $role, $action); + + // 3) controller general (DEFAULT) rule for action + $condition = $controller->easyACL['DEFAULT'][$action]; + if (isset($condition)) return $this->evalACL($condition); + + /* + * (RECURSIVE CALL) + * 4) controller previous specific (role) rule for action + * ex: if role is 'SUPER', use 'ADMIN' rule + * ex: if role is 'ADMIN', use 'RESP' rule + * ex: if role is 'RESP', use 'USER' rule + * ex: if role is 'USER', stop recursive call + * Stop recursive call if role is 'USER' => no rule found, so default is authorize (Y) + */ + if ($role == 'USER') return true; + return $this->isAuthorizedAction($controller, $user, $role-1, $action, $id); + + } + + //@todo + public function getMandatoryFieldsForAction($action) { + $fields = []; + // meme fonctionnement recursif que isAuthorizedAction() ci-dessus + return $fields; + } + //@todo + public function getHiddenFieldsForAction($action) { + $fields = []; + // meme fonctionnement recursif que isAuthorizedAction() ci-dessus + return $fields; + } + //@todo + public function getReadOnlyFieldsForAction($action) { + $fields = []; + // meme fonctionnement recursif que isAuthorizedAction() ci-dessus + return $fields; + } + //@todo + public function getDefaultValueFieldsForAction($action) { + $fields = []; + // meme fonctionnement recursif que isAuthorizedAction() ci-dessus + return $fields; + } + + public static function getRoleLevel($role) { //return $this->allProfiles[$role]; //debug("role is" .$role); @@ -105,7 +252,7 @@ class AppController extends Controller { $action = $this->getActionPassed(); //$role = $this->getUserRole($user); - // Seul Administration (et +) peut ajouter, supprimer ou modifier un organisme + // Seul Administration (et +) peut ajouter, supprimer ou modifier if( in_array($action,['add','delete','edit'])) { if ($this->USER_IS_ADMIN_AT_LEAST()) return true; // Les autres n'y ont pas accès @@ -158,8 +305,7 @@ class AppController extends Controller { return true; // ACL : Super-Admin peut accéder à toutes les actions - if ($role == 'Super Administrateur') - return true; + if ($role == 'Super Administrateur') return true; // ACL : Par défaut refuser return false; diff --git a/src/Controller/MaterielsController.php b/src/Controller/MaterielsController.php index 6ab7bb9..88b67a6 100755 --- a/src/Controller/MaterielsController.php +++ b/src/Controller/MaterielsController.php @@ -30,7 +30,94 @@ class MaterielsController extends AppController { 'TOBEARCHIVED' ]; - //public $role; + // EP 08/2017 + private $FIELDS = array( + 'name', + 'description', + 'prix_ht', + //... + ); + + // EP 08/2017 + protected $easyACL = array( + + /** Default ACL for ALL (logged) users + * + * Les actions non mentionnées sont accessibles à tous (par défaut), + * exemple 'find', 'index'... + * Ces default ACL peuvent être surchargées pour un profil précis + * (par exemple pour 'USER' qui est plus restreint) + */ + //'ALL' => array ( + 'DEFAULT' => array ( + // 'action' => 'condition for execution' (= 'Y', 'N', or ''), + // with like "'field name' == 'value'" + // CRUD actions : + //'index' => 'Y', // read all + //'view' => 'Y', // read one + //'add' => 'Y', // create + //'find' => 'Y', // create + 'edit' => '(status == CREATED || status == VALIDATED)', // update + 'delete' => '(status == CREATED)', + 'autre' => 'N', + ), + + // Ajoute des ACL plus spécifiques (ci-dessus) pour le profil USER qui est plus restreint + // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL') + 'USER' => array ( + 'edit' => '=ALL&& (is_creator [[ is_user)', // is_owner = (nom_createur == CURRENT_USER_NAME) + // except fields status, owner, etiquette, and admin data, VALIDATED (only some fields, cf $modifiableFields in View/Materiels/scaffold.form.ctp) + 'delete' => '=ALL&& is_owner', + /* ceci n'a aucun sens car l'action sur le modèle "Pages" s'appelle toujours "display" (et non pas tools, infos, ou printers...) + 'tools' => 'N', + 'infos' => 'N', + 'printers' => 'N', + */ + 'statusCreated' => 'N', // Dé-valider (in-valider), rétrograder le statut (admin+) + 'statusValidated' => 'N', // Valider (admin+) + 'statusToBeArchived' => 'Y', // Demander la sortie de l'inventaire + 'statusArchived' => 'N', // Sortir de l'inventaire, archiver (admin+) + 'execActions' => 'N', // calls updateSelectedStatus() (admin+) + ), + + // Surcharge des ACL par défaut (ci-dessus) pour le profil RESPONSABLE qui est plus restreint + // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL') + 'RESPONSABLE' => array ( + 'edit' => '=ALL&& is_resp', // is_owner = (nom_createur == CURRENT_USER_NAME) + //'delete' => 'is_resp && (status == CREATED)', + // Valider les matos dont il est responsable : + // is_resp = (groupe_thematique == CURRENT_USER_NAME.groupe_thematique [[ groupe_metier == CURRENT_USER_NAME.groupe_metier) + //'statusValidated' => 'N', + ), + + // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMIN + 'ADMIN' => array ( + //'execActions' => 'Y', // calls updateSelectedStatus(), admin+ + //'add' => 'Y', // create + //'edit' => '(status == CREATED || status == VALIDATED)', // update + 'edit' => '=ALL', // update + 'delete' => '=ALL', // update + 'statusCreated' => 'Y', // Dé-valider (in-valider), rétrograder le statut (admin+) + 'statusValidated' => 'Y', // Valider (admin+) + 'statusToBeArchived' => 'Y', // Demander la sortie de l'inventaire + 'statusArchived' => 'Y', // Sortir de l'inventaire, archiver (admin+) + 'execActions' => 'Y', // calls updateSelectedStatus() (admin+) + ), + + // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMINPLUS + 'ADMINPLUS' => array ( + //'edit' => 'Y', // update + ), + + // Surcharge des ACL par défaut (ci-dessus) pour le profil SUPERADMIN + 'SUPERADMIN' => array ( + //'execActions' => 'Y', // calls updateSelectedStatus(), admin+ + //'add' => 'Y', // create + //'edit' => 'Y', // update + //'delete' => 'Y', + ), + ); // $ACL + /* * EP added 13/6/17 @@ -44,7 +131,22 @@ class MaterielsController extends AppController { $this->set('CREATED', self::CREATED); $this->set('VALIDATED', self::VALIDATED); $this->set('TOBEARCHIVED', self::TOBEARCHIVED); - $this->set('ARCHIVED', self::ARCHIVED); + $this->set('ARCHIVED', self::ARCHIVED); + + /* + * @todo EP 08/2017 Nouvelle organisation des ACL avec $easyACL + * + $mandatoryFields = $this->getMandatoryFieldsForAction($action); + $hiddenFields = $this->getHiddenFieldsForAction($action); + $readOnlyFields = $this->getReadOnlyFieldsForAction($action); + $haveDefaultValueFields = $this->getDefaultValueFieldsForAction($action); + // Seul utile pour la vue 'index' + $this->set('hiddenFields', $hiddenFields); + // Inutiles pour la vue 'index': + $this->set('mandatoryFields', $mandatoryFields); + $this->set('readOnlyFields', $readOnlyFields); + $this->set('haveDefaultValueFields', $haveDefaultValueFields); + */ } /** @@ -53,7 +155,6 @@ class MaterielsController extends AppController { * * Give authorization for materiels */ - // (EP) TODO: ameliorer ca avec des variables globales IS_VALIDATED, IS_ADMIN, ... public function isAuthorized($userFromSession) { $user = $userFromSession; $configuration = $this->confLabinvent; @@ -74,6 +175,14 @@ class MaterielsController extends AppController { $id = $this->getIdPassed(); /* + * @todo EP 08/2017 Nouvelle organisation des ACL avec $easyACL + * + * //return $this->isAuthorizedAction($this, $user, $role, $action, $id); + * + * Tout le reste en dessous de cette ligne devient inutile !!! + */ + + /* * Structure mise en place: * * switch ACTION @@ -86,7 +195,15 @@ class MaterielsController extends AppController { // INDEX, VIEW, ADD, FIND // ACL: Accessibles à tous ( cf parent::isAuthorized() ) - // EDIT + // CREATE (add) + // All roles can 'add' + /* + case 'add': + return true; + break; + */ + + // UPDATE (edit) //if ($action == 'edit') { case 'edit': //BETTER: diff --git a/src/Template/Layout/default.ctp b/src/Template/Layout/default.ctp index d8a53a2..b8e4b16 100755 --- a/src/Template/Layout/default.ctp +++ b/src/Template/Layout/default.ctp @@ -115,7 +115,7 @@ $cakeDescription = 'Labinvent 2'; - VERSION 2.7.9 (30/08/2017) + VERSION 2.8.0 (01/09/2017)