Commit ecf07ed41539832f33aad71c8f99002d97444149

Authored by Etienne Pallier
1 parent e79e058b

Début de mise en place du nouveau système ACL simplifié basé sur les

tableaux $easyACL (pas encore actif)
README-LABINVENT.md
... ... @@ -47,14 +47,11 @@ Logiciel testé et validé sur les configurations suivantes :
47 47  
48 48 VERSION ACTUELLE
49 49  
50   -Date: 30/08/2017
51   -Version: 2.7.9
52   - - Renommages plus explicites des fonctions et variables
53   - - Bugfix important sur détection du role "Utilisateur" pour les personnes du ldap qui ne sont pas dans la table utilisateurs !!!
54   - - Creation d'un fake utilisateur pour simuler un utilisateur qui n'est pas dans la table utilisateurs (et tester le cas ci-dessus)
55   - - Ajout de tests avec cet utilisateur
  50 +Date: 01/09/2017
  51 +Version: 2.8.0
  52 + - Début de mise en place du nouveau système ACL simplifié basé sur les tableaux $easyACL (pas encore actif)
56 53  
57   -Version majeure en cours (2.7): https://projects.irap.omp.eu/versions/162
  54 +Version majeure en cours (2.8): https://projects.irap.omp.eu/versions/162
58 55  
59 56 ROADMAP: https://projects.irap.omp.eu/projects/labinvent/roadmap
60 57  
... ... @@ -68,6 +65,9 @@ CHANGEMENTS IMPORTANTS (MILESTONES)
68 65  
69 66 Liste complète des évolutions: https://gitlab.irap.omp.eu/epallier/labinvent/commits/master
70 67  
  68 +??/09/2017 Version: 2.8.X
  69 + - Nouveau système ACL simplifié basé sur les tableaux $easyACL
  70 +
71 71 30/08/2017 Version: 2.7.9
72 72 - fonction intelligente AppController::getUserRole() qui donne le role "Utilisateur" par défaut pour un utilisateur non privilégié
73 73 - Refactorisation des ACL (authorizations) dans isAuthorized() et beforeFilter()
... ...
src/Controller/AppController.php
... ... @@ -55,7 +55,154 @@ class AppController extends Controller {
55 55 // Current ROLE (by default = "Utilisateur")
56 56 private $CURRENT_ROLE = null;
57 57  
58   -
  58 + // EP 08/2017
  59 + protected $easyACL = array(
  60 +
  61 + /** Default ACL for ALL (logged) users
  62 + *
  63 + * Les actions non mentionnées sont accessibles à tous (par défaut),
  64 + * exemple 'find', 'index'...
  65 + * Ces default ACL peuvent être surchargées pour un profil précis
  66 + * (par exemple pour 'USER' qui est plus restreint)
  67 + */
  68 + //'ALL' => array (
  69 + 'DEFAULT' => array (
  70 + // 'action' => 'condition for execution' (= 'Y', 'N', or '<condition>'),
  71 + // with <condition> like "'field name' == 'value'"
  72 + // CRUD actions :
  73 + //'edit' => 'N', // update
  74 + //'delete' => 'N',
  75 + 'autre' => 'Y',
  76 + ),
  77 +
  78 + // Ajoute des ACL plus spécifiques (ci-dessus) pour le profil USER qui est plus restreint
  79 + // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL')
  80 + 'USER' => array (
  81 + // CRUD actions
  82 + 'add' => 'Y', // C
  83 + 'index' => 'Y', // R all
  84 + 'view' => 'Y', // R one
  85 + //'edit' => 'N', // U
  86 + 'edit' => 'is_creator', // is_creator = (nom_createur == CURRENT_USER_NAME)
  87 + 'delete' => 'N', // D
  88 + // OTHER actions
  89 + 'find' => 'Y', // create
  90 + /* ceci n'a aucun sens car l'action sur le modèle "Pages" s'appelle toujours "display" (et non pas tools, infos, ou printers...)
  91 + 'tools' => 'N',
  92 + 'infos' => 'N',
  93 + 'printers' => 'N',
  94 + */
  95 + ),
  96 +
  97 + // Surcharge des ACL par défaut (ci-dessus) pour le profil RESPONSABLE qui est plus restreint
  98 + // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL')
  99 + 'RESPONSABLE' => array (
  100 + ),
  101 +
  102 + // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMIN
  103 + 'ADMIN' => array (
  104 + //'add' => 'Y', // create
  105 + 'edit' => 'Y', // update
  106 + //'delete' => 'Y', // update
  107 + ),
  108 +
  109 + // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMINPLUS
  110 + 'ADMINPLUS' => array (
  111 + //'edit' => 'Y', // update
  112 + ),
  113 +
  114 + // Surcharge des ACL par défaut (ci-dessus) pour le profil SUPERADMIN
  115 + 'SUPERADMIN' => array (
  116 + //'add' => 'Y', // create
  117 + //'edit' => 'Y', // update
  118 + 'delete' => 'Y',
  119 + ),
  120 + ); // $easyACL
  121 +
  122 +
  123 + //@todo
  124 + public function startsWith($haystack, $needle) {
  125 + return true;
  126 + }
  127 + //@todo
  128 + public function endsWith($haystack, $needle) {
  129 + return true;
  130 + }
  131 + public function evalACL($condition) {
  132 + // Simple case
  133 + if ($condition == 'Y') return true;
  134 + if ($condition == 'F') return false;
  135 + // Complex case
  136 + //@todo
  137 + return true;
  138 + }
  139 + public function evalSpecificRule($condition, AppController $controller, $user, $role, $action, $id=null) {
  140 + // if starts with "&&" eval DEFAULT rule && specific rule
  141 + if ($this->startsWith($condition,'&&')) return $this->evalACL($controller->easyACL['DEFAULT'][$action]) && $this->evalACL($condition);
  142 + // otherwise, eval only specific rule
  143 + return $this->evalACL($condition);
  144 + }
  145 + /**
  146 + * @param AppController $controller // a subclass of AppController (MaterielsController or any other)
  147 + * @param array $user
  148 + * @param string $role
  149 + * @param string $action
  150 + * @param string $id
  151 + */
  152 + public function isAuthorizedAction(AppController $controller, $user, $role, $action, $id=null) {
  153 +
  154 + // 1) controller specific (role) rule for action
  155 + $condition = $controller->easyACL[$role][$action];
  156 + if (isset($condition)) return $this->evalSpecificRule($condition, $controller, $user, $role, $action);
  157 +
  158 + // 2) AppController ($this) specific (role) rule for action
  159 + $condition = self::easyACL[$role][$action];
  160 + if (isset($condition)) return $this->evalSpecificRule($condition, $this, $user, $role, $action);
  161 +
  162 + // 3) controller general (DEFAULT) rule for action
  163 + $condition = $controller->easyACL['DEFAULT'][$action];
  164 + if (isset($condition)) return $this->evalACL($condition);
  165 +
  166 + /*
  167 + * (RECURSIVE CALL)
  168 + * 4) controller previous specific (role) rule for action
  169 + * ex: if role is 'SUPER', use 'ADMIN' rule
  170 + * ex: if role is 'ADMIN', use 'RESP' rule
  171 + * ex: if role is 'RESP', use 'USER' rule
  172 + * ex: if role is 'USER', stop recursive call
  173 + * Stop recursive call if role is 'USER' => no rule found, so default is authorize (Y)
  174 + */
  175 + if ($role == 'USER') return true;
  176 + return $this->isAuthorizedAction($controller, $user, $role-1, $action, $id);
  177 +
  178 + }
  179 +
  180 + //@todo
  181 + public function getMandatoryFieldsForAction($action) {
  182 + $fields = [];
  183 + // meme fonctionnement recursif que isAuthorizedAction() ci-dessus
  184 + return $fields;
  185 + }
  186 + //@todo
  187 + public function getHiddenFieldsForAction($action) {
  188 + $fields = [];
  189 + // meme fonctionnement recursif que isAuthorizedAction() ci-dessus
  190 + return $fields;
  191 + }
  192 + //@todo
  193 + public function getReadOnlyFieldsForAction($action) {
  194 + $fields = [];
  195 + // meme fonctionnement recursif que isAuthorizedAction() ci-dessus
  196 + return $fields;
  197 + }
  198 + //@todo
  199 + public function getDefaultValueFieldsForAction($action) {
  200 + $fields = [];
  201 + // meme fonctionnement recursif que isAuthorizedAction() ci-dessus
  202 + return $fields;
  203 + }
  204 +
  205 +
59 206 public static function getRoleLevel($role) {
60 207 //return $this->allProfiles[$role];
61 208 //debug("role is" .$role);
... ... @@ -105,7 +252,7 @@ class AppController extends Controller {
105 252 $action = $this->getActionPassed();
106 253 //$role = $this->getUserRole($user);
107 254  
108   - // Seul Administration (et +) peut ajouter, supprimer ou modifier un organisme
  255 + // Seul Administration (et +) peut ajouter, supprimer ou modifier
109 256 if( in_array($action,['add','delete','edit'])) {
110 257 if ($this->USER_IS_ADMIN_AT_LEAST()) return true;
111 258 // Les autres n'y ont pas accès
... ... @@ -158,8 +305,7 @@ class AppController extends Controller {
158 305 return true;
159 306  
160 307 // ACL : Super-Admin peut accéder à toutes les actions
161   - if ($role == 'Super Administrateur')
162   - return true;
  308 + if ($role == 'Super Administrateur') return true;
163 309  
164 310 // ACL : Par défaut refuser
165 311 return false;
... ...
src/Controller/MaterielsController.php
... ... @@ -30,7 +30,94 @@ class MaterielsController extends AppController {
30 30 'TOBEARCHIVED'
31 31 ];
32 32  
33   - //public $role;
  33 + // EP 08/2017
  34 + private $FIELDS = array(
  35 + 'name',
  36 + 'description',
  37 + 'prix_ht',
  38 + //...
  39 + );
  40 +
  41 + // EP 08/2017
  42 + protected $easyACL = array(
  43 +
  44 + /** Default ACL for ALL (logged) users
  45 + *
  46 + * Les actions non mentionnées sont accessibles à tous (par défaut),
  47 + * exemple 'find', 'index'...
  48 + * Ces default ACL peuvent être surchargées pour un profil précis
  49 + * (par exemple pour 'USER' qui est plus restreint)
  50 + */
  51 + //'ALL' => array (
  52 + 'DEFAULT' => array (
  53 + // 'action' => 'condition for execution' (= 'Y', 'N', or '<condition>'),
  54 + // with <condition> like "'field name' == 'value'"
  55 + // CRUD actions :
  56 + //'index' => 'Y', // read all
  57 + //'view' => 'Y', // read one
  58 + //'add' => 'Y', // create
  59 + //'find' => 'Y', // create
  60 + 'edit' => '(status == CREATED || status == VALIDATED)', // update
  61 + 'delete' => '(status == CREATED)',
  62 + 'autre' => 'N',
  63 + ),
  64 +
  65 + // Ajoute des ACL plus spécifiques (ci-dessus) pour le profil USER qui est plus restreint
  66 + // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL')
  67 + 'USER' => array (
  68 + 'edit' => '=ALL&& (is_creator [[ is_user)', // is_owner = (nom_createur == CURRENT_USER_NAME)
  69 + // except fields status, owner, etiquette, and admin data, VALIDATED (only some fields, cf $modifiableFields in View/Materiels/scaffold.form.ctp)
  70 + 'delete' => '=ALL&& is_owner',
  71 + /* ceci n'a aucun sens car l'action sur le modèle "Pages" s'appelle toujours "display" (et non pas tools, infos, ou printers...)
  72 + 'tools' => 'N',
  73 + 'infos' => 'N',
  74 + 'printers' => 'N',
  75 + */
  76 + 'statusCreated' => 'N', // Dé-valider (in-valider), rétrograder le statut (admin+)
  77 + 'statusValidated' => 'N', // Valider (admin+)
  78 + 'statusToBeArchived' => 'Y', // Demander la sortie de l'inventaire
  79 + 'statusArchived' => 'N', // Sortir de l'inventaire, archiver (admin+)
  80 + 'execActions' => 'N', // calls updateSelectedStatus() (admin+)
  81 + ),
  82 +
  83 + // Surcharge des ACL par défaut (ci-dessus) pour le profil RESPONSABLE qui est plus restreint
  84 + // Les actions absentes ne sont pas surchargées (elles sont exécutées selon les conditions définies pour 'ALL')
  85 + 'RESPONSABLE' => array (
  86 + 'edit' => '=ALL&& is_resp', // is_owner = (nom_createur == CURRENT_USER_NAME)
  87 + //'delete' => 'is_resp && (status == CREATED)',
  88 + // Valider les matos dont il est responsable :
  89 + // is_resp = (groupe_thematique == CURRENT_USER_NAME.groupe_thematique [[ groupe_metier == CURRENT_USER_NAME.groupe_metier)
  90 + //'statusValidated' => 'N',
  91 + ),
  92 +
  93 + // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMIN
  94 + 'ADMIN' => array (
  95 + //'execActions' => 'Y', // calls updateSelectedStatus(), admin+
  96 + //'add' => 'Y', // create
  97 + //'edit' => '(status == CREATED || status == VALIDATED)', // update
  98 + 'edit' => '=ALL', // update
  99 + 'delete' => '=ALL', // update
  100 + 'statusCreated' => 'Y', // Dé-valider (in-valider), rétrograder le statut (admin+)
  101 + 'statusValidated' => 'Y', // Valider (admin+)
  102 + 'statusToBeArchived' => 'Y', // Demander la sortie de l'inventaire
  103 + 'statusArchived' => 'Y', // Sortir de l'inventaire, archiver (admin+)
  104 + 'execActions' => 'Y', // calls updateSelectedStatus() (admin+)
  105 + ),
  106 +
  107 + // Surcharge des ACL par défaut (ci-dessus) pour le profil ADMINPLUS
  108 + 'ADMINPLUS' => array (
  109 + //'edit' => 'Y', // update
  110 + ),
  111 +
  112 + // Surcharge des ACL par défaut (ci-dessus) pour le profil SUPERADMIN
  113 + 'SUPERADMIN' => array (
  114 + //'execActions' => 'Y', // calls updateSelectedStatus(), admin+
  115 + //'add' => 'Y', // create
  116 + //'edit' => 'Y', // update
  117 + //'delete' => 'Y',
  118 + ),
  119 + ); // $ACL
  120 +
34 121  
35 122 /*
36 123 * EP added 13/6/17
... ... @@ -44,7 +131,22 @@ class MaterielsController extends AppController {
44 131 $this->set('CREATED', self::CREATED);
45 132 $this->set('VALIDATED', self::VALIDATED);
46 133 $this->set('TOBEARCHIVED', self::TOBEARCHIVED);
47   - $this->set('ARCHIVED', self::ARCHIVED);
  134 + $this->set('ARCHIVED', self::ARCHIVED);
  135 +
  136 + /*
  137 + * @todo EP 08/2017 Nouvelle organisation des ACL avec $easyACL
  138 + *
  139 + $mandatoryFields = $this->getMandatoryFieldsForAction($action);
  140 + $hiddenFields = $this->getHiddenFieldsForAction($action);
  141 + $readOnlyFields = $this->getReadOnlyFieldsForAction($action);
  142 + $haveDefaultValueFields = $this->getDefaultValueFieldsForAction($action);
  143 + // Seul utile pour la vue 'index'
  144 + $this->set('hiddenFields', $hiddenFields);
  145 + // Inutiles pour la vue 'index':
  146 + $this->set('mandatoryFields', $mandatoryFields);
  147 + $this->set('readOnlyFields', $readOnlyFields);
  148 + $this->set('haveDefaultValueFields', $haveDefaultValueFields);
  149 + */
48 150 }
49 151  
50 152 /**
... ... @@ -53,7 +155,6 @@ class MaterielsController extends AppController {
53 155 *
54 156 * Give authorization for materiels
55 157 */
56   - // (EP) TODO: ameliorer ca avec des variables globales IS_VALIDATED, IS_ADMIN, ...
57 158 public function isAuthorized($userFromSession) {
58 159 $user = $userFromSession;
59 160 $configuration = $this->confLabinvent;
... ... @@ -74,6 +175,14 @@ class MaterielsController extends AppController {
74 175 $id = $this->getIdPassed();
75 176  
76 177 /*
  178 + * @todo EP 08/2017 Nouvelle organisation des ACL avec $easyACL
  179 + *
  180 + * //return $this->isAuthorizedAction($this, $user, $role, $action, $id);
  181 + *
  182 + * Tout le reste en dessous de cette ligne devient inutile !!!
  183 + */
  184 +
  185 + /*
77 186 * Structure mise en place:
78 187 *
79 188 * switch ACTION
... ... @@ -86,7 +195,15 @@ class MaterielsController extends AppController {
86 195 // INDEX, VIEW, ADD, FIND
87 196 // ACL: Accessibles à tous ( cf parent::isAuthorized() )
88 197  
89   - // EDIT
  198 + // CREATE (add)
  199 + // All roles can 'add'
  200 + /*
  201 + case 'add':
  202 + return true;
  203 + break;
  204 + */
  205 +
  206 + // UPDATE (edit)
90 207 //if ($action == 'edit') {
91 208 case 'edit':
92 209 //BETTER:
... ...
src/Template/Layout/default.ctp
... ... @@ -115,7 +115,7 @@ $cakeDescription = &#39;Labinvent 2&#39;;
115 115 </i></td>
116 116 <td id="version">
117 117 <!-- VERSION M.m.f.b (version (M)ajeure, version (m)ineure, numero de nouvelle (f)onctionnalite, numero de (b)ugfix) -->
118   - <font color="black">VERSION 2.7.9 (30/08/2017)</font>
  118 + <font color="black">VERSION 2.8.0 (01/09/2017)</font>
119 119 <br/>
120 120 <font color="black"><a href="<?php
121 121  
... ...