Gestión de ACL

Instalación

Instale el módulo Ubiquity-acl desde el símbolo del sistema o desde Webtools (parte Composer).

composer require phpmv/ubiquity-acl

A continuación, active la visualización de la parte Acl en el Webtools:

../_images/display-acl.png

Interfaz ACL en webtools:

../_images/acl-part.png

Normas Acl

Las ACL se utilizan para definir el acceso a una aplicación Ubiquity. Se definen de acuerdo con los siguientes principios:

Una aplicación Ubiquity se compone de :
  • Recursos (posiblemente controladores, o acciones de estos controladores)

  • Roles, posiblemente asignados a usuarios. Cada Rol puede heredar roles padre.

  • Permisos, que corresponden a un derecho a hacer. Cada permiso tiene un nivel (representado por un valor entero).

Normas adicionales:
  • Un AclElement (Allow) concede Permiso a un Rol sobre un Recurso.

  • Cada rol hereda autorizaciones de sus padres, además de las suyas propias.

  • Si un rol tiene un determinado nivel de permiso de acceso sobre un recurso, también tendrá todos los permisos de un nivel inferior sobre ese recurso.

  • La asociación de un recurso y un permiso a un controlador o a una acción de controlador define un elemento map.

../_images/acl-diagram.png
Consejos para poner nombres:
  • Rol, en mayúsculas, empezando por una arroba (@USER, @ADMIN, @ALL…).

  • Permisos, en mayúsculas, nombrados con un verbo (READ, WRITE, OPEN…).

  • Recurso, con mayúscula inicial (Products, Customers…)

Inicio de ACL

El servicio AclManager puede iniciarse directamente desde la interfaz webtools, en la parte Security.

  • El servicio se inicia en el archivo services.php.

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

ACLCacheProvider

Este proveedor predeterminado permite gestionar ACLs definidas mediante atributos o anotaciones.

AclController

Un AclController permite la gestión automática del acceso basado en ACLs a sus propios recursos.
Es posible crearlas automáticamente desde webtools.

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

Pero es sólo un controlador básico, utilizando la característica AclControllerTrait.

Este controlador sólo va a redefinir el método _getRole, para que devuelva el rol del usuario activo, por ejemplo.

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!';
   }
}
Se ha concedido autorización para el recurso:
  • Sin especificar el recurso, las acciones del controlador se definen como un recurso.

  • Sin especificar el permiso, se utiliza el permiso ALL.

../_images/me-allow.png

Y esta asociación está presente en el mapa de Acls:

../_images/me-map.png

AclController con autenticación

Nota

El uso tanto de WithAuthTrait como de AclControllerTrait requiere eliminar la ambigüedad sobre el método 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);
   }
}

Permitir con función, recurso y permiso

Permitir sin creación previa:

@USER puede acceder al recurso Foo con permiso 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';
   }
}

Nota

El rol, el recurso y el permiso se crean automáticamente en cuanto se invocan con Allow.

Permitir con creación explícita:

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';
   }
}

Añadir ACL en tiempo de ejecución

Ya sea en un controlador o en un servicio, es posible añadir Roles, Recursos, Permisos y Autorizaciones en tiempo de ejecución:

Por ejemplo: Añadir un rol @USER que herede de @GUEST.

use Ubiquity\security\acl\AclManager;

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

Definición de ACL con base de datos

Las ACLs definidas en la base de datos son adicionales a las ACLs definidas mediante anotaciones o atributos.

Inicialización

La inicialización permite crear las tablas asociadas a las ACLs (Role, Resource, Permission, AclElement). Debe realizarse una sola vez, y únicamente en modo dev.

Para colocar por ejemplo en el archivo app/config/bootstrap.php :

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

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

Comenzando

En el archivo 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)
]);

Estrategias para definir ACLs

Con pocos recursos:

Definición de autorizaciones para cada acción o grupo de acciones del controlador:

Los recursos corresponden lógicamente a los controladores, y los permisos a las acciones. Pero esta regla puede no respetarse, y una acción puede definirse como un recurso, según sea necesario.

La única regla obligatoria es que un par Controlador/acción sólo puede corresponder a un par Recurso/permiso (no necesariamente único).

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!';
   }

}

Con más recursos:

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!';
   }

}