mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Implement context-specific voters for all current entities that can be linked to a document
For reusability an AbstractStoredObjectVoter was created and a StoredObjectVoterInterface. A WorkflowDocumentService checks whether the StoredObject is involved in a workflow.
This commit is contained in:
parent
73797b98f6
commit
1310d53589
@ -12,6 +12,8 @@ declare(strict_types=1);
|
|||||||
namespace Chill\ActivityBundle\Repository;
|
namespace Chill\ActivityBundle\Repository;
|
||||||
|
|
||||||
use Chill\ActivityBundle\Entity\Activity;
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
@ -23,7 +25,7 @@ use Doctrine\Persistence\ManagerRegistry;
|
|||||||
* @method Activity[] findAll()
|
* @method Activity[] findAll()
|
||||||
* @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
* @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
*/
|
*/
|
||||||
class ActivityRepository extends ServiceEntityRepository
|
class ActivityRepository extends ServiceEntityRepository implements AssociatedEntityToStoredObjectInterface
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
public function __construct(ManagerRegistry $registry)
|
||||||
{
|
{
|
||||||
@ -97,4 +99,17 @@ class ActivityRepository extends ServiceEntityRepository
|
|||||||
|
|
||||||
return $qb->getQuery()->getResult();
|
return $qb->getQuery()->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
|
||||||
|
{
|
||||||
|
$qb = $this->createQueryBuilder('a');
|
||||||
|
$query = $qb
|
||||||
|
->join('a.documents', 'ad')
|
||||||
|
->join('ad.storedObject', 'so')
|
||||||
|
->where('so.id = :storedObjectId')
|
||||||
|
->setParameter('storedObjectId', $storedObject->getId())
|
||||||
|
->getQuery();
|
||||||
|
|
||||||
|
return $query->getResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ use Doctrine\ORM\EntityRepository;
|
|||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use Doctrine\Persistence\ObjectRepository;
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
class AccompanyingCourseDocumentRepository implements ObjectRepository
|
class AccompanyingCourseDocumentRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private readonly EntityRepository $repository;
|
||||||
|
|
||||||
@ -46,8 +46,8 @@ class AccompanyingCourseDocumentRepository implements ObjectRepository
|
|||||||
return $qb->getQuery()->getSingleScalarResult();
|
return $qb->getQuery()->getSingleScalarResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findLinkedCourseDocument(StoredObject $storedObject): ?AccompanyingCourseDocument {
|
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
|
||||||
|
{
|
||||||
$qb = $this->repository->createQueryBuilder('d');
|
$qb = $this->repository->createQueryBuilder('d');
|
||||||
$query = $qb->where('d.storedObject = :storedObject')
|
$query = $qb->where('d.storedObject = :storedObject')
|
||||||
->setParameter('storedObject', $storedObject)
|
->setParameter('storedObject', $storedObject)
|
||||||
@ -66,7 +66,7 @@ class AccompanyingCourseDocumentRepository implements ObjectRepository
|
|||||||
return $this->repository->findAll();
|
return $this->repository->findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
|
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
|
||||||
{
|
{
|
||||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
}
|
}
|
||||||
@ -76,7 +76,7 @@ class AccompanyingCourseDocumentRepository implements ObjectRepository
|
|||||||
return $this->findOneBy($criteria);
|
return $this->findOneBy($criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getClassName()
|
public function getClassName(): string
|
||||||
{
|
{
|
||||||
return AccompanyingCourseDocument::class;
|
return AccompanyingCourseDocument::class;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
|
||||||
|
interface AssociatedEntityToStoredObjectInterface
|
||||||
|
{
|
||||||
|
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object;
|
||||||
|
}
|
@ -12,6 +12,7 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle\Repository;
|
namespace Chill\DocStoreBundle\Repository;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\Entity\PersonDocument;
|
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Doctrine\Persistence\ObjectRepository;
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
@ -19,7 +20,7 @@ use Doctrine\Persistence\ObjectRepository;
|
|||||||
/**
|
/**
|
||||||
* @template ObjectRepository<PersonDocument::class>
|
* @template ObjectRepository<PersonDocument::class>
|
||||||
*/
|
*/
|
||||||
readonly class PersonDocumentRepository implements ObjectRepository
|
readonly class PersonDocumentRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
|
||||||
{
|
{
|
||||||
private EntityRepository $repository;
|
private EntityRepository $repository;
|
||||||
|
|
||||||
@ -53,4 +54,14 @@ readonly class PersonDocumentRepository implements ObjectRepository
|
|||||||
{
|
{
|
||||||
return PersonDocument::class;
|
return PersonDocument::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
|
||||||
|
{
|
||||||
|
$qb = $this->repository->createQueryBuilder('d');
|
||||||
|
$query = $qb->where('d.storedObject = :storedObject')
|
||||||
|
->setParameter('storedObject', $storedObject)
|
||||||
|
->getQuery();
|
||||||
|
|
||||||
|
return $query->getResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace ChillDocStoreBundle\Security\Authorization;
|
|
||||||
|
|
||||||
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
|
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
|
||||||
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
|
|
||||||
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
|
|
||||||
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface;
|
|
||||||
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
|
|
||||||
use Chill\MainBundle\Entity\User;
|
|
||||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
|
||||||
|
|
||||||
class AccompanyingCourseDocumentStoredObjectVoter implements StoredObjectVoterInterface
|
|
||||||
{
|
|
||||||
final public const SEE_AND_EDIT = 'CHILL_ACCOMPANYING_COURSE_DOCUMENT_STORED_OBJECT_SEE_EDIT';
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private readonly AccompanyingCourseDocumentRepository $repository,
|
|
||||||
private readonly AccompanyingCourseDocumentVoter $accompanyingCourseDocumentVoter,
|
|
||||||
private readonly WorkflowDocumentService $workflowDocumentService
|
|
||||||
){
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(string $attribute, StoredObject $subject): bool
|
|
||||||
{
|
|
||||||
// check if the stored object is linked to an AccompanyingCourseDocument
|
|
||||||
return $this->repository->findLinkedCourseDocument($subject) instanceof AccompanyingCourseDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function voteOnAttribute(string $attribute, StoredObject $subject, TokenInterface $token): bool
|
|
||||||
{
|
|
||||||
if (!$token->getUser() instanceof User) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the related accompanying course document
|
|
||||||
$accompanyingCourseDocument = $this->repository->findLinkedCourseDocument($subject);
|
|
||||||
|
|
||||||
// Determine the attribute to pass to AccompanyingCourseDocumentVoter
|
|
||||||
$voterAttribute = match($attribute) {
|
|
||||||
self::SEE_AND_EDIT => AccompanyingCourseDocumentVoter::UPDATE,
|
|
||||||
default => AccompanyingCourseDocumentVoter::SEE_DETAILS,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check access using AccompanyingCourseDocumentVoter
|
|
||||||
if (false === $this->accompanyingCourseDocumentVoter->voteOnAttribute($voterAttribute, $accompanyingCourseDocument, $token)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if entity is related to a workflow, if so, check if user can apply transition
|
|
||||||
$relatedWorkflow = $this->workflowDocumentService->getRelatedWorkflow($accompanyingCourseDocument);
|
|
||||||
|
|
||||||
if ($relatedWorkflow instanceof EntityWorkflow){
|
|
||||||
return $this->workflowDocumentService->canApplyTransition($relatedWorkflow);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -48,15 +48,19 @@ class StoredObjectVoter extends Voter
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$attributeAsEnum = StoredObjectRoleEnum::from($attribute);
|
||||||
|
|
||||||
// Loop through context-specific voters
|
// Loop through context-specific voters
|
||||||
foreach ($this->storedObjectVoters as $storedObjectVoter) {
|
foreach ($this->storedObjectVoters as $storedObjectVoter) {
|
||||||
if ($storedObjectVoter->supports($attribute, $subject)) {
|
if ($storedObjectVoter->supports($attributeAsEnum, $subject)) {
|
||||||
return $storedObjectVoter->voteOnAttribute($attribute, $subject, $token);
|
return $storedObjectVoter->voteOnAttribute($attributeAsEnum, $subject, $token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// User role-based fallback
|
// User role-based fallback
|
||||||
if ($this->security->isGranted('ROLE_USER') || $this->security->isGranted('ROLE_ADMIN')) {
|
if ($this->security->isGranted('ROLE_USER') || $this->security->isGranted('ROLE_ADMIN')) {
|
||||||
|
// TODO: this maybe considered as a security issue, as all authenticated users can reach a stored object which
|
||||||
|
// is potentially detached from an existing entity.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,12 +12,13 @@ declare(strict_types=1);
|
|||||||
namespace Chill\DocStoreBundle\Security\Authorization;
|
namespace Chill\DocStoreBundle\Security\Authorization;
|
||||||
|
|
||||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
|
||||||
interface StoredObjectVoterInterface {
|
interface StoredObjectVoterInterface {
|
||||||
|
|
||||||
public function supports(string $attribute, StoredObject $subject): bool;
|
public function supports(StoredObjectRoleEnum $attribute, StoredObject $subject): bool;
|
||||||
|
|
||||||
public function voteOnAttribute(string $attribute, StoredObject $subject, TokenInterface $token): bool;
|
public function voteOnAttribute(StoredObjectRoleEnum $attribute, StoredObject $subject, TokenInterface $token): bool;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters;
|
||||||
|
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface;
|
||||||
|
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
abstract class AbstractStoredObjectVoter implements StoredObjectVoterInterface
|
||||||
|
{
|
||||||
|
abstract protected function getRepository(): AssociatedEntityToStoredObjectInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return class-string
|
||||||
|
*/
|
||||||
|
abstract protected function getClass(): string;
|
||||||
|
|
||||||
|
abstract protected function attributeToRole(StoredObjectRoleEnum $attribute): string;
|
||||||
|
|
||||||
|
abstract protected function canBeAssociatedWithWorkflow(): bool;
|
||||||
|
|
||||||
|
function __construct(
|
||||||
|
private readonly Security $security,
|
||||||
|
private readonly ?WorkflowDocumentService $workflowDocumentService = null,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports(StoredObjectRoleEnum $attribute, StoredObject $subject): bool
|
||||||
|
{
|
||||||
|
$class = $this->getClass();
|
||||||
|
return $this->getRepository()->findAssociatedEntityToStoredObject($subject) instanceof $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function voteOnAttribute(StoredObjectRoleEnum $attribute, StoredObject $subject, TokenInterface $token): bool
|
||||||
|
{
|
||||||
|
if (!$token->getUser() instanceof User) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the related accompanying course document
|
||||||
|
$entity = $this->getRepository()->findAssociatedEntityToStoredObject($subject);
|
||||||
|
|
||||||
|
// Determine the attribute to pass to AccompanyingCourseDocumentVoter
|
||||||
|
$voterAttribute = $this->attributeToRole($attribute);
|
||||||
|
|
||||||
|
if (false === $this->security->isGranted($voterAttribute, $entity)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->canBeAssociatedWithWorkflow()) {
|
||||||
|
if (null === $this->workflowDocumentService) {
|
||||||
|
throw new \LogicException("Provide a workflow document service");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->workflowDocumentService->notBlockedByWorkflow($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
|
||||||
|
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
|
||||||
|
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
final class AccompanyingCourseStoredObjectVoter extends AbstractStoredObjectVoter
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly AccompanyingCourseDocumentRepository $repository,
|
||||||
|
Security $security,
|
||||||
|
WorkflowDocumentService $workflowDocumentService
|
||||||
|
){
|
||||||
|
parent::__construct($security, $workflowDocumentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRepository(): AssociatedEntityToStoredObjectInterface
|
||||||
|
{
|
||||||
|
return $this->repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
|
||||||
|
{
|
||||||
|
return match ($attribute) {
|
||||||
|
StoredObjectRoleEnum::EDIT => AccompanyingCourseDocumentVoter::UPDATE,
|
||||||
|
StoredObjectRoleEnum::SEE => AccompanyingCourseDocumentVoter::SEE_DETAILS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getClass(): string
|
||||||
|
{
|
||||||
|
return AccompanyingCourseDocument::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canBeAssociatedWithWorkflow(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace src\Bundle\ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
|
||||||
|
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
|
||||||
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
||||||
|
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository;
|
||||||
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationDocumentVoter;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class AccompanyingPeriodWorkEvaluationStoredObjectVoter extends AbstractStoredObjectVoter
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly AccompanyingPeriodWorkEvaluationDocumentRepository $repository,
|
||||||
|
Security $security,
|
||||||
|
WorkflowDocumentService $workflowDocumentService
|
||||||
|
){
|
||||||
|
parent::__construct($security, $workflowDocumentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRepository(): AssociatedEntityToStoredObjectInterface
|
||||||
|
{
|
||||||
|
return $this->repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function getClass(): string
|
||||||
|
{
|
||||||
|
return AccompanyingPeriodWorkEvaluationDocument::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
|
||||||
|
{
|
||||||
|
//Question: there is no update/edit check in AccompanyingPeriodWorkEvaluationDocumentVoter, so for both SEE and EDIT of the
|
||||||
|
// stored object I check with SEE right in AccompanyingPeriodWorkEvaluationDocumentVoter, correct?
|
||||||
|
return match ($attribute) {
|
||||||
|
StoredObjectRoleEnum::SEE, StoredObjectRoleEnum::EDIT => AccompanyingPeriodWorkEvaluationDocumentVoter::SEE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canBeAssociatedWithWorkflow(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace src\Bundle\ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityDocumentACLAwareRepository;
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityRepository;
|
||||||
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
|
||||||
|
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class ActivityStoredObjectVoter extends AbstractStoredObjectVoter
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ActivityRepository $repository,
|
||||||
|
Security $security,
|
||||||
|
WorkflowDocumentService $workflowDocumentService
|
||||||
|
){
|
||||||
|
parent::__construct($security, $workflowDocumentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRepository(): AssociatedEntityToStoredObjectInterface
|
||||||
|
{
|
||||||
|
return $this->repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function getClass(): string
|
||||||
|
{
|
||||||
|
return Activity::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
|
||||||
|
{
|
||||||
|
return match ($attribute) {
|
||||||
|
StoredObjectRoleEnum::EDIT => ActivityVoter::UPDATE,
|
||||||
|
StoredObjectRoleEnum::SEE => ActivityVoter::SEE_DETAILS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canBeAssociatedWithWorkflow(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace src\Bundle\ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
|
||||||
|
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
|
||||||
|
use Chill\EventBundle\Entity\Event;
|
||||||
|
use Chill\EventBundle\Repository\EventRepository;
|
||||||
|
use Chill\EventBundle\Security\Authorization\EventVoter;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class EventStoredObjectVoter extends AbstractStoredObjectVoter
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly EventRepository $repository,
|
||||||
|
Security $security,
|
||||||
|
WorkflowDocumentService $workflowDocumentService
|
||||||
|
){
|
||||||
|
parent::__construct($security, $workflowDocumentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRepository(): AssociatedEntityToStoredObjectInterface
|
||||||
|
{
|
||||||
|
return $this->repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function getClass(): string
|
||||||
|
{
|
||||||
|
return Event::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
|
||||||
|
{
|
||||||
|
return match ($attribute) {
|
||||||
|
StoredObjectRoleEnum::EDIT => EventVoter::UPDATE,
|
||||||
|
StoredObjectRoleEnum::SEE => EventVoter::SEE_DETAILS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canBeAssociatedWithWorkflow(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace src\Bundle\ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\PersonDocument;
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
|
use Chill\DocStoreBundle\Repository\PersonDocumentRepository;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||||
|
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
|
||||||
|
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
class PersonStoredObjectVoter extends AbstractStoredObjectVoter
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly PersonDocumentRepository $repository,
|
||||||
|
Security $security,
|
||||||
|
WorkflowDocumentService $workflowDocumentService
|
||||||
|
){
|
||||||
|
parent::__construct($security, $workflowDocumentService);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRepository(): AssociatedEntityToStoredObjectInterface
|
||||||
|
{
|
||||||
|
return $this->repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function getClass(): string
|
||||||
|
{
|
||||||
|
return PersonDocument::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
|
||||||
|
{
|
||||||
|
return match ($attribute) {
|
||||||
|
StoredObjectRoleEnum::EDIT => PersonDocumentVoter::UPDATE,
|
||||||
|
StoredObjectRoleEnum::SEE => PersonDocumentVoter::SEE_DETAILS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canBeAssociatedWithWorkflow(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -13,19 +13,19 @@ class WorkflowDocumentService
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRelatedWorkflow($entity): ?EntityWorkflow
|
public function notBlockedByWorkflow($entity): bool
|
||||||
{
|
{
|
||||||
return $this->repository->findByRelatedEntity(get_class($entity), $entity->getId());
|
/**
|
||||||
}
|
* @var EntityWorkflow
|
||||||
|
*/
|
||||||
|
$workflow = $this->repository->findByRelatedEntity(get_class($entity), $entity->getId());
|
||||||
|
|
||||||
public function canApplyTransition(EntityWorkflow $entityWorkflow): bool
|
if ($workflow->isFinal()) {
|
||||||
{
|
|
||||||
if ($entityWorkflow->isFinal()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$currentUser = $this->security->getUser();
|
$currentUser = $this->security->getUser();
|
||||||
if ($entityWorkflow->getCurrentStep()->getAllDestUser()->contains($currentUser)) {
|
if ($workflow->getCurrentStep()->getAllDestUser()->contains($currentUser)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
/**
|
/**
|
||||||
* Class Event.
|
* Class Event.
|
||||||
*/
|
*/
|
||||||
#[ORM\Entity(repositoryClass: \Chill\EventBundle\Repository\EventRepository::class)]
|
#[ORM\Entity]
|
||||||
#[ORM\HasLifecycleCallbacks]
|
#[ORM\HasLifecycleCallbacks]
|
||||||
#[ORM\Table(name: 'chill_event_event')]
|
#[ORM\Table(name: 'chill_event_event')]
|
||||||
class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInterface, TrackUpdateInterface
|
class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInterface, TrackUpdateInterface
|
||||||
|
@ -11,17 +11,66 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\EventBundle\Repository;
|
namespace Chill\EventBundle\Repository;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
use Chill\EventBundle\Entity\Event;
|
use Chill\EventBundle\Entity\Event;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class EventRepository.
|
* Class EventRepository.
|
||||||
*/
|
*/
|
||||||
class EventRepository extends ServiceEntityRepository
|
class EventRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
private readonly EntityRepository $repository;
|
||||||
|
|
||||||
|
public function __construct(EntityManagerInterface $entityManager)
|
||||||
{
|
{
|
||||||
parent::__construct($registry, Event::class);
|
$this->repository = $entityManager->getRepository(Event::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
|
||||||
|
{
|
||||||
|
return $this->repository->createQueryBuilder($alias, $indexBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
|
||||||
|
{
|
||||||
|
$qb = $this->createQueryBuilder('e');
|
||||||
|
$query = $qb
|
||||||
|
->join('e.documents', 'ed')
|
||||||
|
->join('ed.storedObject', 'so')
|
||||||
|
->where('so.id = :storedObjectId')
|
||||||
|
->setParameter('storedObjectId', $storedObject->getId())
|
||||||
|
->getQuery();
|
||||||
|
|
||||||
|
return $query->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id)
|
||||||
|
{
|
||||||
|
return $this->repository->find($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->repository->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||||
|
{
|
||||||
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria)
|
||||||
|
{
|
||||||
|
return $this->repository->findOneBy($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassName(): string
|
||||||
|
{
|
||||||
|
return Event::class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,22 @@ class EntityWorkflowRepository implements ObjectRepository
|
|||||||
return $this->repository->findAll();
|
return $this->repository->findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByRelatedEntity($entityClass, $relatedEntityId): ?EntityWorkflow
|
||||||
|
{
|
||||||
|
$qb = $this->repository->createQueryBuilder('w');
|
||||||
|
|
||||||
|
$query = $qb->where(
|
||||||
|
$qb->expr()->andX(
|
||||||
|
$qb->expr()->eq('w.relatedEntityClass', ':entity_class'),
|
||||||
|
$qb->expr()->eq('w.relatedEntityId', ':entity_id'),
|
||||||
|
)
|
||||||
|
)->setParameter('entity_class', $entityClass)
|
||||||
|
->setParameter('entity_id', $relatedEntityId);
|
||||||
|
|
||||||
|
return $query->getQuery()->getResult();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed|null $limit
|
* @param mixed|null $limit
|
||||||
* @param mixed|null $offset
|
* @param mixed|null $offset
|
||||||
|
@ -11,12 +11,14 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
|
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
|
||||||
|
|
||||||
|
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||||
|
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Doctrine\Persistence\ObjectRepository;
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
|
||||||
class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectRepository
|
class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
|
||||||
{
|
{
|
||||||
private readonly EntityRepository $repository;
|
private readonly EntityRepository $repository;
|
||||||
|
|
||||||
@ -58,4 +60,14 @@ class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectReposi
|
|||||||
{
|
{
|
||||||
return AccompanyingPeriodWorkEvaluationDocument::class;
|
return AccompanyingPeriodWorkEvaluationDocument::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
|
||||||
|
{
|
||||||
|
$qb = $this->repository->createQueryBuilder('ed');
|
||||||
|
$query = $qb->where('ed.storedObject = :storedObject')
|
||||||
|
->setParameter('storedObject', $storedObject)
|
||||||
|
->getQuery();
|
||||||
|
|
||||||
|
return $query->getResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user