Gestion des ACL

Installation

Installez le module Ubiquity-acl à partir de l’invite de commande ou des Webtools (partie Composer).

composer require phpmv/ubiquity-acl

Activez ensuite l’affichage de la partie Acl dans les Webtools :

../_images/display-acl.png

Interface ACL dans les webtools :

../_images/acl-part.png

Règles Acl

Les ACLs sont utilisées pour définir l’accès à une application Ubiquity. Elles sont définies selon les principes suivants :

Une application Ubiquity est composée de :
  • Ressources (éventuellement des contrôleurs, ou des actions de ces contrôleurs)

  • Rôles, éventuellement attribués à des utilisateurs. Chaque Rôle peut hériter de rôles parents.

  • Permissions, qui correspondent à un droit de faire. Chaque permission a un niveau (représenté par une valeur entière).

Règles supplémentaires :
  • Un AclElement (Allow) accorde une permission à un rôle sur une ressource.

  • Chaque rôle hérite des autorisations de ses parents, en plus des siennes.

  • Si un rôle a un certain niveau d’autorisation d’accès à une ressource, il aura également toutes les autorisations d’un niveau inférieur sur cette ressource.

  • L’association d’une ressource et d’une permission à un contrôleur ou à une action de contrôleur définit un élément map.

../_images/acl-diagram.png
Règles de nommage :
  • Rôle, en lettres capitales, commençant par une arobase (@USER, @ADMIN, @ALL…).

  • Permissions, en majuscules, nommées à l’aide d’un verbe (READ, WRITE, OPEN…).

  • Ressource, majuscule à la première lettre (Produits, Clients…)

Démarrage des ACLs

Le service AclManager peut être démarré directement depuis l’interface webtools, dans la partie Security.

  • Le service est démarré dans le fichier services.php.

app/config/services.php
 \Ubiquity\security\acl\AclManager::startWithCacheProvider();

ACLCacheProvider

Ce fournisseur par défaut vous permet de gérer les ACL définies par des attributs ou des annotations.

AclController

Un AclController permet de gérer automatiquement les accès à ses propres ressources en se basant sur des ACL.
Il est possible de les créer automatiquement à partir des webtools.

../_images/new-acl-controller.png

Mais il ne s’agit que d’un contrôleur de base, utilisant le trait AclControllerTrait.

Ce contrôleur va juste redéfinir la méthode _getRole, pour qu’elle renvoie le rôle de l’utilisateur actif, par exemple.

app/controllers/BaseAclController.php
<?php
namespace controllers;

use Ubiquity\controllers\Controller;
use Ubiquity\security\acl\controllers\AclControllerTrait;
use Ubiquity\attributes\items\acl\Allow;

class BaseAclController extends Controller {
use AclControllerTrait;

   #[Allow('@ME')]
   public function index() {
      $this->loadView("BaseAclController/index.html");
   }

   public function _getRole() {
      $_GET['role']??'@ME';//Just for testing: logically, this is the active user's role
   }

   /**
    * {@inheritdoc}
    * @see \Ubiquity\controllers\Controller::onInvalidControl()
    */
   public function onInvalidControl() {
      echo $this->_getRole() . ' is not allowed!';
   }
}
L’autorisation a été accordée pour la ressource :
  • Sans spécifier la ressource, chaque action du contrôleur est définie comme une ressource.

  • Sans spécifier la permission, la permission « ALL » est utilisée.

../_images/me-allow.png

Et cette association est présente dans la map des Acls :

../_images/me-map.png

AclController avec authentication

Note

L’utilisation à la fois de WithAuthTrait et de ``AclControllerTrait`” nécessite de lever l’ambiguïté sur la méthode ``isValid`”.

app/controllers/BaseAclController.php
class BaseAclController extends Controller {
   use AclControllerTrait,WithAuthTrait{
      WithAuthTrait::isValid insteadof AclControllerTrait;
      AclControllerTrait::isValid as isValidAcl;
   }

   public function isValid($action){
        return parent::isValid($action)&& $this->isValidAcl($action);
   }
}

Allow avec Rôle, ressource et permission

Allow sans création préalable :

@USER est autorisé à accéder à la ressource Foo avec la permission READ.

app/controllers/BaseAclController.php
use Ubiquity\attributes\items\acl\Allow;

class BaseAclController extends Controller {
use AclControllerTrait;
   ...

   #[Allow('@USER','Foo', 'READ')]
   public function foo(){
      echo 'foo page allowed for @USER and @ME';
   }
}

Note

Le rôle, la ressource et la permission sont automatiquement créés dès qu’ils sont invoqués avec Allow.

Allow avec création explicite :

app/controllers/BaseAclController.php
use Ubiquity\attributes\items\acl\Allow;
use Ubiquity\attributes\items\acl\Permission;

class BaseAclController extends Controller {
use AclControllerTrait;
   ...

   #[Permission('READ',500)]
   #[Allow('@USER','Foo', 'READ')]
   public function foo(){
      echo 'foo page allowed for @USER and @ME';
   }
}

Ajout d’ACL à l’exécution

Que ce soit dans un contrôleur ou dans un service, il est possible d’ajouter des rôles, des ressources, des permissions et des autorisations au moment de l’exécution :

Par exemple :\N- Ajouter un rôle @USER héritant de @GUEST.

use Ubiquity\security\acl\AclManager;

AclManager::addRole('@GUEST');
AclManager::addRole('@USER',['@GUEST']);

Définir les ACLs avec une base de données

Les ACL définies dans la base de données s’ajoutent aux ACL définies via les annotations ou les attributs.

Initialisation

L’initialisation permet de créer les tables associées aux ACLs (Role, Resource, Permission, AclElement). Elle ne doit être faite qu’une seule fois, et en mode dev uniquement.

A placer par exemple dans le fichier app/config/bootstrap.php :

use Ubiquity\controllers\Startup;
use Ubiquity\security\acl\AclManager;

$config=Startup::$config;
AclManager::initializeDAOProvider($config, 'default');

Démarrage

Dans le fichier app/config/services.php :

use Ubiquity\security\acl\AclManager;
use Ubiquity\security\acl\persistence\AclCacheProvider;
use Ubiquity\security\acl\persistence\AclDAOProvider;
use Ubiquity\orm\DAO;

DAO::start();//Optional, to use only if dbOffset is not default

AclManager::start();
AclManager::initFromProviders([
    new AclCacheProvider(), new AclDAOProvider($config)
]);

Stratégies de définition des ACL

Avec peu de ressources :

Définir les autorisations pour chaque action du contrôleur ou chaque groupe d’actions :

Les ressources correspondent logiquement aux contrôleurs, et les permissions aux actions. Mais cette règle peut ne pas être respectée, et une action peut être définie comme une ressource, selon les besoins.

La seule règle obligatoire est qu’une paire contrôleur/action ne peut correspondre qu’à une seule paire ressource/permission (pas nécessairement unique).

app/controllers/BaseAclController.php
namespace controllers;

use Ubiquity\controllers\Controller;
use Ubiquity\security\acl\controllers\AclControllerTrait;
use Ubiquity\attributes\items\acl\Permission;
use Ubiquity\attributes\items\acl\Resource;

#[Resource('Foo')]
#[Allow('@ADMIN')]
class FooController extends Controller {
   use AclControllerTrait;

   #[Allow('@NONE')]
   public function index() {
      echo 'index';
   }

   #[Allow('@USER')]
   public function read() {
      echo 'read';
   }

   #[Allow('@USER')]
   public function write() {
      echo 'write';
   }

   public function admin() {
      echo 'admin';
   }

   public function _getRole() {
      return $_GET['role']??'@NONE';
   }

   /**
    * {@inheritdoc}
    * @see \Ubiquity\controllers\Controller::onInvalidControl()
    */
   public function onInvalidControl() {
      echo $this->_getRole() . ' is not allowed!';
   }

}

Avec plus de ressources :

app/controllers/BaseAclController.php
namespace controllers;

use Ubiquity\controllers\Controller;
use Ubiquity\security\acl\controllers\AclControllerTrait;
use Ubiquity\attributes\items\acl\Permission;
use Ubiquity\attributes\items\acl\Resource;

#[Resource('Foo')]
class FooController extends Controller {
   use AclControllerTrait;

   #[Permission('INDEX',1)]
   public function index() {
      echo 'index';
   }

   #[Permission('READ',2)]
   public function read() {
      echo 'read';
   }

   #[Permission('WRITE',3)]
   public function write() {
      echo 'write';
   }

   #[Permission('ADMIN',10)]
   public function admin() {
      echo 'admin';
   }

   public function _getRole() {
      return $_GET['role']??'NONE';
   }

   /**
    * {@inheritdoc}
    * @see \Ubiquity\controllers\Controller::onInvalidControl()
    */
   public function onInvalidControl() {
      echo $this->_getRole() . ' is not allowed!';
   }

}