Commit 9d1259e4 authored by David Surville's avatar David Surville
Browse files

Refonte de la documentation en intégrant les périmètres

parent 9f458846
Loading
Loading
Loading
Loading

doc/assertion.md

0 → 100644
+375 −0
Changes for doc/assertion.md: 375 added lines, 0 removed lines.
Original line number Diff line number Diff line
# Les assertions

<!-- TOC -->
* [Mise en place d'une assertion](#mise-en-place-dune-assertion)
  * [Déclaration d'une assertion](#déclaration-dune-assertion)
  * [Création d'une assertion](#création-dune-assertion)
  * [Utilisation d'une assertion](#utilisation-dune-assertion)
<!-- TOC -->

## Mise en place d'une assertion

### Déclaration d'une assertion

Une assertion est un service qui hérite de la classe `UnicaenPrivilege\Assertion\AbstractAssertion` et il doit être déclaré dans la configuration du `ServiceManager`. Il est recommandé d'utiliser `UnicaenPrivilege\Assertion\AssertionFactory` pour effectuer cette déclararion.

```php
use UnicaenPrivilege\Assertion\AbstractAssertion;
use Application\Assertion\StructureAssertion;

 'service_manager' => [
    'factories' => [
        StructureAssertion::class => AbstractAssertion::class,
    ],
],
```

### Création d'une assertion

La classe `UnicaenPrivilege\Assertion\AbstractAssertion` fournit l'accès à :
* au gestionnaire d'événements MVC `Laminas\Mvc\MvcEvent` : il permet de récupérer des informations sur les paramètres d'URL, de routage,..
* à l'ACL courante `AbstractAssertion::getAcl()`
* au rôle courant `AbstractAssertion::getRole()`

Cette classe va faciliter la mise en place d'une assertion en déterminant si la ressource associée à l'assertion est un privilège, une action de contrôleur, une entité ou autre chose et en appelant la bonne méthode associée au type de la ressource. Il faut donc implémenter ces méthodes si nécessaire et fournir une réponse `true` ou `false` (elles renvoient `true` par défaut).

```php
<?php

namespace UnicaenPrivilege\Assertion;

use Laminas\Permissions\Acl\Assertion\AssertionInterface;

abstract class AbstractAssertion implements AssertionInterface
{
    /**
     * @param string $privilege
     * @param string $subPrivilege
     * @return boolean
     */
    protected function assertPrivilege($privilege, $subPrivilege = null)
    {
        return true;
    }

    /**
     * @param string $controller
     * @param string $action
     * @param string $privilege
     * @return boolean
     */
    protected function assertController($controller, $action = null, $privilege = null)
    {
        return true;
    }

    /**
     * @param ResourceInterface $entity
     * @param string            $privilege
     * @return boolean
     */
    protected function assertEntity(ResourceInterface $entity, $privilege = null)
    {
        return true;
    }

    /**
     * @param ResourceInterface $resource
     * @param string            $privilege
     * @return boolean
     */
    protected function assertOther(ResourceInterface $resource = null, $privilege = null)
    {
        return true;
    }
}
```

Exemple :

```php
<?php

namespace Application\Assertion;

use Application\Entity\Db\Structure;
use Application\Provider\Privilege\StructurePrivileges;
use Laminas\Permissions\Acl\Role\RoleInterface;
use Laminas\Permissions\Acl\Resource\ResourceInterface;
use UnicaenApp\Service\EntityManagerAwareTrait;
use UnicaenPrivilege\Assertion\AbstractAssertion;

class StructureAssertion extends AbstractAssertion
{
    use EntityManagerAwareTrait;
    
    /**
     * @var array
     */
    private $repositories = [];
    
    /**
     * @var Structure
     */
    private $structure;
    

    protected function assertEntity(ResourceInterface $entity = null, $privilege = null)
    {
        $role = $this->getRole();

        // si le rôle n'est pas renseigné
        if (!$role instanceof RoleInterface) return false;

        // si le rôle ne possède pas le privilège
        if (!parent::assertEntity($entity, $privilege)) {
            return false;
        }

        /** @var Structure $entity */
        switch ($privilege) {
            case StructurePrivileges::EDITION:
            case StructurePrivileges::FERMETURE:
            case StructurePrivileges::ANNUAIRE_EDITION:
                return $this->assertStructureEdition($entity);
            case StructurePrivileges::SUPPRESSION:
                return $this->assertStructureSuppression($entity);
        }

        return false;
    }

    protected function assertController($controller, $action = null, $privilege = null)
    {
        $role = $this->getRole();

        // si le rôle n'est pas renseigné
        if (!$role instanceof RoleInterface) return false;

        // récupération de la structure selon l'action
        $structure = $this->getStructure();

        switch ($action) {
            case 'afficher':
            case 'afficher-logo':
                return $this->assertStructureConsultation($structure);

            case 'modifier':
            case 'modifier-logo':
            case 'modifier-localisation':
            case 'supprimer-localisation':
                return $this->assertStructureEdition($structure);

            case 'fermer':
                return $this->assertStructureEdition($structure)
                    && !$structure->getDateFermeture();

            case 'ouvrir':
                return $this->assertStructureEdition($structure)
                    && $structure->getDateFermeture();

            case 'supprimer':
                return $this->assertStructureSuppression($structure);
        }

        return false;
    }

    protected function assertStructureConsultation(Structure $structure)
    {
        return $structure ? $structure->estNonHistorise() : true;
    }

    protected function assertStructureEdition(Structure $structure = null)
    {
        return $structure ? $structure->estNonHistorise() : true;
    }

    protected function assertStructureSuppression(Structure $structure = null)
    {
        if(!$structure) {
            return true;
        }

        // on teste si la structure possède une structure fille non historisée
        foreach ($structure->getFils() as $fils) {
            if($fils->estNonHistorise()) {
                return false;
            }
        }

        return $structure->estNonHistorise();
    }

    protected function getStructure()
    {
        if (null === $this->structure) {
            $this->structure = $this->fetchEntityParam('structure');
        }
        return $this->structure;
    }
    
     /**
     * @return RouteMatch
     */
    private function getRouteMatch()
    {
        return $this->getMvcEvent()->getRouteMatch();
    }

    /**
     * @param string $name nom du paramètre
     * @return mixed
     */
    protected function getParam($name)
    {
        return $this->getRouteMatch()->getParam($name);
    }

    /**
     * @param string $name Nom du paramètre
     * @return mixed
     */
    protected function fetchEntityParam($name)
    {
        $repository = $this->getRepository($name);
        if ($repository === null) {
            return null;
        }

        // identifiant de l'entité
        $id = $this->getParam($name);

        if (!$id) {
            return null;
        }

        $entity = $repository->find($id);

        return $entity;
    }

    /**
     * @param string $name
     * @return EntityRepository
     */
    protected function getRepository($name)
    {
        if (! isset($this->repositories[$name])) {
            $fqcn = $this->getFullQualifiedClassName($name);
            if (!class_exists($fqcn)) {
                return null;
            }
            $this->repositories[$name] = $this->getEntityManager()->getRepository($fqcn);
        }

        return $this->repositories[$name];
    }

    /**
     * @param $name
     * @return string
     */
    private function getFullQualifiedClassName($name)
    {
        $namespace = 'Application\\Entity\\Db\\';
        return $namespace . sprintf('%s', ucfirst($name));
    }
}
```
### Utilisation d'une assertion

L'assertion est un service qui hérite de `UnicaenPrivilege\Assertion\AbstractAssertion` et qui peut être appelé dans la configuration des `guards` du module `BjyAuthorize` à l'aide de la clé `assertion`.

```php
use Application\Controller\StructureController;
use Application\Provider\Privilege\StructurePrivileges;
use UnicaenPrivilege\Guard\PrivilegeController;

'bjyauthorize' => [
    'guards' => [
        PrivilegeController::class => [
            [
                'controller' => StructureController::class,
                'action' => [
                    'afficher',
                    'afficher-logo',
                ],
                'privileges' => [
                    StructurePrivileges::CONSULTATION,
                ],
                'assertion' => 'StructureAssertion',
            ],
```

L'assertion peut être également utilisée au niveau des `rules`.

```php
use Application\Entity\Db\Structure;
use Application\Provider\Privilege\StructurePrivileges;
use UnicaenPrivilege\Provider\Rule\PrivilegeRuleProvider;

'rule_providers' => [
    PrivilegeRuleProvider::class => [
        'allow' => [
             [
                'privileges' => [
                    StructurePrivileges::EDITION,
                    StructurePrivileges::FERMETURE,
                ],
                'resources' => [Structure::RESOURCE_ID],
                'assertion' => 'StructureAssertion',
            ],
```

Elle peut être utilisée au niveau de la navigation pour afficher ou non un élément.
Il suffit d'affecter une resource à un élément de navigation et l'accès à cette ressource va conditionner la visibilité de l'élement.

```php
use Application\Controller\StructureController;
use UnicaenPrivilege\Guard\PrivilegeController;

'navigation' => [
    'default' => [
        'home' => [
            'pages' => [
                [
                    'label' => "Rechercher une structure",
                    'title' => "Rechercher une structure",
                    'route' => 'structure/rechercher',
                    'resource' => PrivilegeController::getResourceId(StructureController::class, 'rechercher'),
                ],
```

On peut également l'utiliser avec la fonction `isAllowed($resource, $privilege = null)` dans un contrôleur (plugin) ou dans une vue (view helper).

```php
use Application\Controller\StructureController;
use Application\Provider\Privilege\StructurePrivileges;
use UnicaenPrivilege\Guard\PrivilegeController;
use UnicaenPrivilege\Provider\Privilege\Privileges;

// Avec un privilège
$this->isAllowed(Privileges::getResourceId(StructurePrivileges::EDITION))

// Avec une action de contrôleur
$this->isAllowed(PrivilegeController::getResourceId(StructureController::class, 'editer'));

// Avec une entité
$this->isAllowed($structure, StructurePrivileges::EDITION);
```

Cette fonction peut-être également appelée depuis une aide de vue.

```php
$this->getView()->isAllowed(Privileges::getResourceId(StructurePrivileges::EDITION));
```

Et si on est ni dans une vue, ni dans une aide de vue, ni dans un contrôleur ?
La fonction `isAllowed` est une méthode publique du service `Authorize` de `BjyAuthorize` qui peut être utilisée en récupérant le service.

```php
$authorizeService = $container->get('BjyAuthorize\Service\Authorize');

$canDoThat = $authorizeService->isAllowed();
```

doc/img/img_1.png

0 → 100644
+17.5 KiB
Loading image diff...

doc/img/img_2.png

0 → 100644
+15.5 KiB
Loading image diff...

doc/img/img_3.png

0 → 100644
+24.2 KiB
Loading image diff...

doc/img/img_4.png

0 → 100644
+65.3 KiB
Loading image diff...
Loading