diff --git a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php index 646a69438..e87e8e36d 100644 --- a/src/Bundle/ChillMainBundle/Controller/WorkflowController.php +++ b/src/Bundle/ChillMainBundle/Controller/WorkflowController.php @@ -11,8 +11,10 @@ declare(strict_types=1); namespace Chill\MainBundle\Controller; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Entity\Workflow\EntityWorkflowComment; +use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep; use Chill\MainBundle\Form\EntityWorkflowCommentType; use Chill\MainBundle\Form\WorkflowStepType; use Chill\MainBundle\Pagination\PaginatorFactory; @@ -24,10 +26,12 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\TransitionBlocker; @@ -84,8 +88,7 @@ class WorkflowController extends AbstractController ->setRelatedEntityId($request->query->getInt('entityId')) ->setWorkflowName($request->query->get('workflow')) ->addSubscriberToStep($this->getUser()) - ->addSubscriberToFinal($this->getUser()) - ; + ->addSubscriberToFinal($this->getUser()); $errors = $this->validator->validate($entityWorkflow, null, ['creation']); @@ -136,6 +139,37 @@ class WorkflowController extends AbstractController ]); } + /** + * @Route("/{_locale}/main/workflow-step/{id}/access_key", name="chill_main_workflow_grant_access_by_key") + */ + public function getAccessByAccessKey(EntityWorkflowStep $entityWorkflowStep, Request $request): Response + { + if (null === $accessKey = $request->query->get('accessKey', null)) { + throw new BadRequestException('accessKey is missing'); + } + + if (!$this->getUser() instanceof User) { + throw new AccessDeniedException('Not a valid user'); + } + + dump($accessKey); + dump($entityWorkflowStep->getAccessKey()); + + if ($entityWorkflowStep->getAccessKey() !== $accessKey) { + throw new AccessDeniedException('Access key is invalid'); + } + + if (!$entityWorkflowStep->isWaitingForTransition()) { + $this->addFlash('error', $this->translator->trans('workflow.Steps is not waiting for transition. Maybe someone apply the transition before you ?')); + } else { + $entityWorkflowStep->addDestUserByAccessKey($this->getUser()); + $this->entityManager->flush(); + $this->addFlash('success', $this->translator->trans('workflow.You get access to this step')); + } + + return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflowStep->getEntityWorkflow()->getId()]); + } + /** * @Route("/{_locale}/main/workflow/list/dest", name="chill_main_workflow_list_dest") */ diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php index bde2f28ac..590380749 100644 --- a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php +++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php @@ -27,6 +27,11 @@ use function in_array; */ class EntityWorkflowStep { + /** + * @ORM\Column(type="text", nullable=false) + */ + private string $accessKey; + /** * @ORM\Column(type="text", options={"default": ""}) */ @@ -48,6 +53,12 @@ class EntityWorkflowStep */ private Collection $destUser; + /** + * @ORM\ManyToMany(targetEntity=User::class) + * @ORM\JoinTable(name="chill_main_workflow_entity_step_user_by_accesskey") + */ + private Collection $destUserByAccessKey; + /** * @ORM\ManyToOne(targetEntity=EntityWorkflow::class, inversedBy="steps") */ @@ -101,14 +112,10 @@ class EntityWorkflowStep */ private ?string $transitionByEmail = null; - /** - * @ORM\Column(type="text", nullable=false) - */ - private string $accessKey; - public function __construct() { $this->destUser = new ArrayCollection(); + $this->destUserByAccessKey = new ArrayCollection(); $this->accessKey = bin2hex(openssl_random_pseudo_bytes(32)); } @@ -133,6 +140,37 @@ class EntityWorkflowStep return $this; } + public function addDestUserByAccessKey(User $user): self + { + if (!$this->destUserByAccessKey->contains($user)) { + $this->destUserByAccessKey[] = $user; + $this->getEntityWorkflow() + ->addSubscriberToFinal($user) + ->addSubscriberToStep($user); + } + + return $this; + } + + public function getAccessKey(): string + { + return $this->accessKey; + } + + /** + * get all the users which are allowed to apply a transition: those added manually, and + * those added automatically bu using an access key. + */ + public function getAllDestUser(): Collection + { + return new ArrayCollection( + [ + ...$this->getDestUser()->toArray(), + ...$this->getDestUserByAccessKey()->toArray(), + ] + ); + } + public function getComment(): string { return $this->comment; @@ -149,13 +187,21 @@ class EntityWorkflowStep } /** - * @return ArrayCollection|Collection + * get dest users added by the creator. + * + * You should **not** rely on this method to get all users which are able to + * apply a transition on this step. Use @see{EntityWorkflowStep::getAllDestUser} instead. */ - public function getDestUser() + public function getDestUser(): collection { return $this->destUser; } + public function getDestUserByAccessKey(): Collection + { + return $this->destUserByAccessKey; + } + public function getEntityWorkflow(): ?EntityWorkflow { return $this->entityWorkflow; @@ -206,6 +252,19 @@ class EntityWorkflowStep return $this->freezeAfter; } + public function isWaitingForTransition(): bool + { + if (null !== $this->transitionAfter) { + return false; + } + + if ($this->isFinal()) { + return false; + } + + return true; + } + public function removeDestEmail(string $email): self { $this->destEmail = array_filter($this->destEmail, static function (string $existing) use ($email) { @@ -222,6 +281,13 @@ class EntityWorkflowStep return $this; } + public function removeDestUserByAccessKey(User $user): self + { + $this->destUserByAccessKey->removeElement($user); + + return $this; + } + public function setComment(?string $comment): EntityWorkflowStep { $this->comment = (string) $comment; diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig index 64482c067..5ae454359 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/_decision.html.twig @@ -82,13 +82,23 @@
{{ 'workflow.This workflow is finalized'|trans }}
{% else %}{{ 'workflow.You are not allowed to apply a transition on this workflow'|trans }}
-{{ 'workflow.Only those users are allowed'|trans }}:
+ {% if entity_workflow.currentStep.destUser|length > 0 %} +{{ 'workflow.Only those users are allowed'|trans }} :
+{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }} :
+{{ 'workflow.Users allowed to apply transition'|trans }} :
-{{ 'workflow.Users allowed to apply transition'|trans }} :
+{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }} :
+