Commit 417dc57f authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Réorganisation des assertions entre modules These, Doctorant et Depot

parent 40d38555
Loading
Loading
Loading
Loading
+86 −1
Original line number Diff line number Diff line
@@ -2,16 +2,32 @@

namespace Application\Assertion;

use Application\RouteMatch;
use Application\Service\UserContextService;
use Application\Service\UserContextServiceAwareTrait;
use Depot\Acl\WfEtapeResource;
use Depot\Service\Workflow\WorkflowServiceAwareInterface;
use Depot\Service\Workflow\WorkflowServiceAwareTrait;
use Doctorant\Entity\Db\Doctorant;
use UnicaenApp\Service\MessageCollectorAwareTrait;
use UnicaenAuth\Provider\Privilege\Privileges;
use Laminas\Permissions\Acl\Resource\ResourceInterface;

/**
 * Class AbstractAssertion
 *
 * @package Application\Assertion
 * @method UserContextService getServiceUserContext()
 */
abstract class AbstractAssertion extends \UnicaenAuth\Assertion\AbstractAssertion
    implements WorkflowServiceAwareInterface
{
    use UserContextServiceAwareTrait;
    use WorkflowServiceAwareTrait;
    use MessageCollectorAwareTrait;

    protected ?string $controller = null;
    protected ?string $action = null;

    protected function assertEntity(ResourceInterface $entity, $privilege = null)
    {
        // Patch pour corriger le fonctionnement aberrant suivant :
@@ -22,4 +38,73 @@ abstract class AbstractAssertion extends \UnicaenAuth\Assertion\AbstractAssertio

        return true;
    }

    protected function assertController($controller, $action = null, $privilege = null)
    {
        if (! parent::assertController($controller, $action, $privilege)) {
            return false;
        }

        $this->controller = $controller;
        $this->action = $action;

        return true;
    }

    /**
     * @param string $expectedController
     * @param string $expectedAction
     * @return bool
     */
    protected function actionIs(string $expectedController, string $expectedAction): bool
    {
        return $this->controller === $expectedController && $this->action === $expectedAction;
    }

    /**
     * @return bool
     */
    protected function selectedRoleIsDoctorant(): bool
    {
        return (bool) $this->userContextService->getSelectedRoleDoctorant();
    }

    protected ?Doctorant $identityDoctorant = null;

    protected function getIdentityDoctorant(): ?Doctorant
    {
        if (null === $this->identityDoctorant) {
            $this->identityDoctorant = $this->userContextService->getIdentityDoctorant();
        }

        return $this->identityDoctorant;
    }

    public function isAllowed($resource, $privilege = null): bool
    {
        $allowed = parent::isAllowed($resource, $privilege);

        if (! $allowed) {
            switch (true) {
                case $resource instanceof WfEtapeResource:
                    $etape = $this->workflowService->getEtapeRepository()->findOneBy(['code' => $resource->getEtape()]);
                    $this->getServiceMessageCollector()->addMessage(
                        sprintf("L'étape « %s » n'est pas encore accessible.", $etape->getLibelleAutres()),
                        $etape->getCode());
                    break;
                default:
                    break;
            }
        }

        return $allowed;
    }

    protected function getRouteMatch()
    {
        /** @var \Application\RouteMatch $rm */
        $rm = $this->getMvcEvent()->getRouteMatch();

        return $rm;
    }
}
 No newline at end of file
+1 −132
Original line number Diff line number Diff line
@@ -4,23 +4,14 @@ namespace Depot\Assertion\These;

use Application\Assertion\AbstractAssertion;
use Application\Assertion\Exception\FailedAssertionException;
use Application\Entity\Db\Role;
use Application\RouteMatch;
use Application\Service\UserContextService;
use Application\Service\UserContextServiceAwareTrait;
use Depot\Acl\WfEtapeResource;
use Depot\Entity\Db\WfEtape;
use Depot\Provider\Privilege\DepotPrivileges;
use Depot\Provider\Privilege\ValidationPrivileges;
use Depot\Service\Workflow\WorkflowServiceAwareInterface;
use Depot\Service\Workflow\WorkflowServiceAwareTrait;
use Doctorant\Entity\Db\Doctorant;
use Laminas\Permissions\Acl\Resource\ResourceInterface;
use These\Controller\TheseController;
use These\Entity\Db\These;
use These\Provider\Privilege\ThesePrivileges;
use UnicaenApp\Exception\RuntimeException;
use UnicaenApp\Service\MessageCollectorAwareTrait;

/**
 * Class TheseAssertion
@@ -28,42 +19,21 @@ use UnicaenApp\Service\MessageCollectorAwareTrait;
 * @package Application\Assertion
 * @method UserContextService getServiceUserContext()
 */
class TheseAssertion extends AbstractAssertion implements WorkflowServiceAwareInterface
class TheseAssertion extends AbstractAssertion
{
    use UserContextServiceAwareTrait;
    use WorkflowServiceAwareTrait;
    use MessageCollectorAwareTrait;

    const THESE_CONTROLLER = TheseController::class;
    const DOCTORANT_CONTROLLER = 'Application\Controller\Doctorant';

    private TheseEntityAssertion $theseEntityAssertion;
    private ?Doctorant $doctorant = null;
    private ?These $these = null;
    protected ?string $controller = null;
    protected ?string $action = null;

    /**
     * @param \These\Assertion\These\TheseEntityAssertion $theseEntityAssertion
     */
    public function setTheseEntityAssertion(TheseEntityAssertion $theseEntityAssertion)
    {
        $this->theseEntityAssertion = $theseEntityAssertion;
    }

    /**
     * @param array $page
     * @return bool
     */
    public function __invoke(array $page): bool
    {
        return $this->assertPage($page);
    }

    /**
     * @param array $page
     * @return bool
     */
    private function assertPage(array $page): bool
    {
        if ($this->getRouteMatch() === null) {
@@ -71,7 +41,6 @@ class TheseAssertion extends AbstractAssertion implements WorkflowServiceAwareIn
        }

        $this->these = $this->getRouteMatch()->getThese();
        $this->doctorant = $this->getRouteMatch()->getDoctorant();

        $etape = $page['etape'] ?? null;
        if (!$etape) {
@@ -103,45 +72,11 @@ class TheseAssertion extends AbstractAssertion implements WorkflowServiceAwareIn

        /** @var These $these */

        $role = $this->userContextService->getSelectedIdentityRole();
        $individu = $this->userContextService->getIdentityIndividu();
        switch (true) {
            case $privilege === DepotPrivileges::THESE_SAISIE_DESCRIPTION_VERSION_INITIALE:
                return ! $this->isAllowed(new WfEtapeResource(WfEtape::CODE_DEPOT_VERSION_ORIGINALE_CORRIGEE, $these));
                break;
            case $privilege === ValidationPrivileges::THESE_VALIDATION_RDV_BU:
                return $this->isAllowed(new WfEtapeResource(WfEtape::CODE_RDV_BU_VALIDATION_BU, $these));
                break;
            case $privilege === ThesePrivileges::THESE_CONSULTATION_SES_THESES:
            case $privilege === ThesePrivileges::THESE_MODIFICATION_SES_THESES:
                // doctorant
                if ($role->getCode() === Role::CODE_DOCTORANT) return $these->getDoctorant()->getIndividu() === $individu;
                // directeur
                if ($role->getCode() === Role::CODE_DIRECTEUR_THESE) {
                    $directeurs = $these->getActeursByRoleCode(Role::CODE_DIRECTEUR_THESE);
                    $individus = [];
                    foreach ($directeurs as $directeur) $individus[] = $directeur->getIndividu();
                    return (array_search($individu, $individus) !== false);
                }
                if ($role->getCode() === Role::CODE_CODIRECTEUR_THESE) {
                    $directeurs = $these->getActeursByRoleCode(Role::CODE_CODIRECTEUR_THESE);
                    $individus = [];
                    foreach ($directeurs as $directeur) $individus[] = $directeur->getIndividu();
                    return (array_search($individu, $individus) !== false);
                }
                // structure
                // todo : remplacer par $role->isStructureDependant() && $role->getTypeStructureDependant()->isEcoleDoctorale() :
                if (in_array($role->getCode(), [Role::CODE_RESP_ED, Role::CODE_GEST_ED])) {
                    return $these->getEcoleDoctorale()->getStructure() === $role->getStructure();
                }
                // todo : remplacer par $role->isStructureDependant() && $role->getTypeStructureDependant()->isUniteRecherche() :
                if (in_array($role->getCode(), [Role::CODE_RESP_UR, Role::CODE_GEST_UR])) {
                    return $these->getUniteRecherche()->getStructure() === $role->getStructure();
                }
                // todo : remplacer par $role->isStructureDependant() && $role->getTypeStructureDependant()->isEtablissement() :
                if ($role->getCode() === Role::CODE_ADMIN || $role->getCode() === Role::CODE_BDD || $role->getCode() === Role::CODE_BU)
                    return $these->getEtablissement()->getStructure() === $role->getStructure();
                break;
        }

        return true;
@@ -153,11 +88,7 @@ class TheseAssertion extends AbstractAssertion implements WorkflowServiceAwareIn
            return false;
        }

        $this->controller = $controller;
        $this->action = $action;

        $this->these = $this->getRouteMatch()->getThese();
        $this->doctorant = $this->getRouteMatch()->getDoctorant();

        switch (true) {
            case $this->selectedRoleIsDoctorant():
@@ -177,7 +108,6 @@ class TheseAssertion extends AbstractAssertion implements WorkflowServiceAwareIn
        switch (true) {
            case $privilege === ValidationPrivileges::THESE_VALIDATION_RDV_BU:
                return $this->isAllowed(new WfEtapeResource(WfEtape::CODE_RDV_BU_VALIDATION_BU, $this->these));
                break;
        }

        return true;
@@ -191,71 +121,10 @@ class TheseAssertion extends AbstractAssertion implements WorkflowServiceAwareIn
            throw new RuntimeException("Anomalie: le role doctorant est sélectionné mais aucune donnée d'identité doctorant n'est disponible");
        }

        switch (true) {
            case $this->actionIs(self::DOCTORANT_CONTROLLER, 'modifier-email-contact'):
                return $this->doctorant && $this->doctorant->getId() === $identityDoctorant->getId();
                break;
        }

        if ($this->these === null) {
            return true;
        }

        return $this->these->getDoctorant()->getId() === $identityDoctorant->getId();
    }

    /**
     * @param string $expectedController
     * @param string $expectedAction
     * @return bool
     */
    private function actionIs(string $expectedController, string $expectedAction): bool
    {
        return $this->controller === $expectedController && $this->action === $expectedAction;
    }

    /**
     * @return bool
     */
    private function selectedRoleIsDoctorant(): bool
    {
        return (bool) $this->userContextService->getSelectedRoleDoctorant();
    }

    protected ?Doctorant $identityDoctorant = null;

    private function getIdentityDoctorant(): ?Doctorant
    {
        if (null === $this->identityDoctorant) {
            $this->identityDoctorant = $this->userContextService->getIdentityDoctorant();
        }

        return $this->identityDoctorant;
    }

    public function isAllowed($resource, $privilege = null): bool
    {
        $allowed = parent::isAllowed($resource, $privilege);

        if (! $allowed) {
            switch (true) {
                case $resource instanceof WfEtapeResource:
                    $etape = $this->workflowService->getEtapeRepository()->findOneBy(['code' => $resource->getEtape()]);
                    $this->getServiceMessageCollector()->addMessage(
                        sprintf("L'étape « %s » n'est pas encore accessible.", $etape->getLibelleAutres()),
                        $etape->getCode());
                    break;
                default:
                    break;
            }
        }

        return $allowed;
    }

    protected function getRouteMatch(): ?RouteMatch
    {
        /** @var \Application\RouteMatch $rm */
        return $this->getMvcEvent()->getRouteMatch();
    }
}
 No newline at end of file
+100 −0
Original line number Diff line number Diff line
<?php

namespace Doctorant\Assertion\These;

/**
 * Classe mère d'Assertion.
 *
 * Générée à partir du fichier
 * /home/gauthierb/workspace/sygal/module/Doctorant/data/TheseEntityAssertion.csv.
 *
 * @author Application\Assertion\Generator\AssertionGenerator
 * @date 29/11/2022 10:45:49
 */
abstract class GeneratedTheseEntityAssertion
{

    protected $failureMessage;

    protected $linesTrace = [
        
    ];

    /**
     * Retourne true si le privilège spécifié est accordé ; false sinon.
     *
     * @param string $privilege
     * @return bool
     */
    public function assertAsBoolean($privilege)
    {
        $this->failureMessage = null;

        if ($privilege === \Doctorant\Provider\Privilege\DoctorantPrivileges::DOCTORANT_AFFICHER_EMAIL_CONTACT) {
        //--------------------------------------------------------------------------------------
            /* line 1 */
            $this->linesTrace[] = '/* line 1 */';
            if ($this->isRoleDoctorantSelected() /* test 1 */ &&
                ! $this->isUtilisateurEstAuteurDeLaThese() /* test 4 */) {
                $this->failureMessage = "Vous ne pouvez pas visualiser l’adresse de contact car vous n’êtes pas l’auteur de la thèse";
                return false;
            }
            /* line 2 */
            $this->linesTrace[] = '/* line 2 */';
            return true;
        }

        if ($privilege === \Doctorant\Provider\Privilege\DoctorantPrivileges::DOCTORANT_MODIFIER_EMAIL_CONTACT) {
        //--------------------------------------------------------------------------------------
            /* line 3 */
            $this->linesTrace[] = '/* line 3 */';
            if ($this->isRoleDoctorantSelected() /* test 1 */ && 
                ! $this->isUtilisateurEstAuteurDeLaThese() /* test 4 */) {
                $this->failureMessage = "Vous ne pouvez pas modifier l’adresse de contact car vous n’êtes pas l’auteur de la thèse";
                return false;
            }
            /* line 4 */
            $this->linesTrace[] = '/* line 4 */';
            return true;
        }

        throw new \Application\Assertion\Exception\UnexpectedPrivilegeException(
            "Le privilège spécifié n'est pas couvert par l'assertion: $privilege. Trace : " . PHP_EOL . implode(PHP_EOL, $this->linesTrace));
    }

    /**
     * @return bool
     */
    abstract protected function isRoleDoctorantSelected();
    /**
     * @return bool
     */
    abstract protected function isStructureDuRoleRespectee();
    /**
     * @return bool
     */
    abstract protected function isTheseEnCours();
    /**
     * @return bool
     */
    abstract protected function isUtilisateurEstAuteurDeLaThese();
    /**
     * Retourne le contenu du fichier CSV à partir duquel a été générée cette
     * classe.
     *
     * @return string
     */
    public function loadedFileContent()
    {
        return <<<'EOT'
class;Doctorant\Assertion\These\GeneratedTheseEntityAssertion;;1;2;3;4;;;
line;enabled;privilege;isRoleDoctorantSelected;isStructureDuRoleRespectee;isTheseEnCours;isUtilisateurEstAuteurDeLaThese;;return;message
1;1;\Doctorant\Provider\Privilege\DoctorantPrivileges::DOCTORANT_AFFICHER_EMAIL_CONTACT;1:1;;;2:0;;0;Vous ne pouvez pas visualiser l’adresse de contact car vous n’êtes pas l’auteur de la thèse
2;1;\Doctorant\Provider\Privilege\DoctorantPrivileges::DOCTORANT_AFFICHER_EMAIL_CONTACT;;;;;;1;
3;1;\Doctorant\Provider\Privilege\DoctorantPrivileges::DOCTORANT_MODIFIER_EMAIL_CONTACT;1:1;;;2:0;;0;Vous ne pouvez pas modifier l’adresse de contact car vous n’êtes pas l’auteur de la thèse
4;1;\Doctorant\Provider\Privilege\DoctorantPrivileges::DOCTORANT_MODIFIER_EMAIL_CONTACT;;;;;;1;
EOT;
    }


}
+109 −0
Original line number Diff line number Diff line
<?php

namespace Doctorant\Assertion\These;

use Application\Assertion\AbstractAssertion;
use Application\Assertion\Exception\FailedAssertionException;
use Depot\Acl\WfEtapeResource;
use Doctorant\Entity\Db\Doctorant;
use Laminas\Permissions\Acl\Resource\ResourceInterface;
use These\Entity\Db\These;
use UnicaenApp\Exception\RuntimeException;

class TheseAssertion extends AbstractAssertion
{
    const DOCTORANT_CONTROLLER = 'Application\Controller\Doctorant';

    private TheseEntityAssertion $theseEntityAssertion;
    private ?Doctorant $doctorant = null;
    private ?These $these = null;

    public function setTheseEntityAssertion(TheseEntityAssertion $theseEntityAssertion)
    {
        $this->theseEntityAssertion = $theseEntityAssertion;
    }

    public function __invoke(array $page): bool
    {
        return $this->assertPage($page);
    }

    private function assertPage(array $page): bool
    {
        if ($this->getRouteMatch() === null) {
            return false;
        }

        $this->these = $this->getRouteMatch()->getThese();
        $this->doctorant = $this->getRouteMatch()->getDoctorant();

        $etape = $page['etape'] ?? null;
        if (!$etape) {
            return true;
        }

        if ($this->these && ! $this->getServiceAuthorize()->isAllowed(new WfEtapeResource($etape, $this->these))) {
            return false;
        }

        return true;
    }

    protected function assertEntity(ResourceInterface $these, $privilege = null): bool
    {
        if (! parent::assertEntity($these, $privilege)) {
            return false;
        }

        $this->theseEntityAssertion->setContext(['these' => $these]);
        try {
            $this->theseEntityAssertion->assert($privilege);
        } catch (FailedAssertionException $e) {
            if ($e->getMessage()) {
                $this->getServiceMessageCollector()->addMessage($e->getMessage(), __CLASS__);
            }
            return false;
        }

        return true;
    }

    protected function assertController($controller, $action = null, $privilege = null): bool
    {
        if (! parent::assertController($controller, $action, $privilege)) {
            return false;
        }

        $this->these = $this->getRouteMatch()->getThese();
        $this->doctorant = $this->getRouteMatch()->getDoctorant();

        switch (true) {
            case $this->selectedRoleIsDoctorant():
                if (! $this->assertControllerAsDoctorant()) {
                    return false;
                }
        }

        return true;
    }

    protected function assertControllerAsDoctorant(): bool
    {
        $identityDoctorant = $this->getIdentityDoctorant();

        if ($identityDoctorant === null) {
            throw new RuntimeException("Anomalie: le role doctorant est sélectionné mais aucune donnée d'identité doctorant n'est disponible");
        }

        switch (true) {
            case $this->actionIs(self::DOCTORANT_CONTROLLER, 'modifier-email-contact'):
                return $this->doctorant && $this->doctorant->getId() === $identityDoctorant->getId();
        }

        if ($this->these === null) {
            return true;
        }

        return $this->these->getDoctorant()->getId() === $identityDoctorant->getId();
    }
}
 No newline at end of file
+46 −0
Original line number Diff line number Diff line
<?php

namespace Doctorant\Assertion\These;

use Application\Assertion\AbstractAssertion;
use Psr\Container\ContainerInterface;

class TheseAssertionFactory
{
    /**
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function __invoke(ContainerInterface $container): TheseAssertion
    {
        $assertion = new TheseAssertion();

        $userContext = $container->get('UnicaenAuth\Service\UserContext');
        $assertion->setUserContextService($userContext);

        $messageCollector = $container->get('MessageCollector');
        $assertion->setServiceMessageCollector($messageCollector);

        /** @var TheseEntityAssertion $theseEntityAssertion */
        $theseEntityAssertion = $container->get(TheseEntityAssertion::class);
        $assertion->setTheseEntityAssertion($theseEntityAssertion);

        $this->injectCommons($assertion, $container);

        return $assertion;
    }

    /**
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \Psr\Container\ContainerExceptionInterface
     */
    protected function injectCommons(AbstractAssertion $assertion, ContainerInterface $container)
    {
        /** @var \UnicaenAuth\Service\AuthorizeService $authorizeService */
        $authorizeService = $container->get('BjyAuthorize\Service\Authorize');
        $mvcEvent = $container->get('Application')->getMvcEvent();

        $assertion->setServiceAuthorize($authorizeService);
        $assertion->setMvcEvent($mvcEvent);
    }
}
 No newline at end of file
Loading