From 47d829d72d7f6944df32d8b2321ad0faeada9486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 6 Dec 2023 22:46:50 +0100 Subject: [PATCH] Filter confidential courses from list of accompanying periods --- .../Export/Export/ListAccompanyingPeriod.php | 77 ++++++++++++++++--- .../Export/ListAccompanyingPeriodTest.php | 51 +++++++++++- 2 files changed, 115 insertions(+), 13 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php index 881a38db6..ef729642d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php @@ -11,11 +11,14 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Export; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\AccompanyingCourseExportHelper; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Form\Type\PickRollingDateType; +use Chill\MainBundle\Repository\CenterRepositoryInterface; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface; use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; @@ -23,11 +26,13 @@ use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter; use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Security\Core\Security; final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExportInterface { @@ -37,6 +42,9 @@ final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExp private EntityManagerInterface $entityManager, private RollingDateConverterInterface $rollingDateConverter, private ListAccompanyingPeriodHelper $listAccompanyingPeriodHelper, + private Security $security, + private CenterRepositoryInterface $centerRepository, + private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser, ParameterBagInterface $parameterBag, ) { $this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center']; @@ -101,7 +109,16 @@ final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExp public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { - $centers = array_map(static fn ($el) => $el['center'], $acl); + $centers = match ($this->filterStatsByCenters) { + true => array_map(static fn ($el) => $el['center'], $acl), + false => $this->centerRepository->findAll(), + }; + + $user = $this->security->getUser(); + + if (!$user instanceof User) { + throw new \RuntimeException('only a regular user can run this export'); + } $qb = $this->entityManager->createQueryBuilder(); @@ -110,19 +127,57 @@ final readonly class ListAccompanyingPeriod implements ListInterface, GroupedExp ->andWhere('acp.step != :list_acp_step') ->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT); - if ($this->filterStatsByCenters) { - $qb - ->andWhere( + // add filtering on confidential accompanying period. The confidential is applyed on the current status of + // the accompanying period (we do not use the 'calc_date' here + $aclConditionsOrX = $qb->expr()->orX( + // either the current user is the refferer for the course + 'acp.user = :list_acp_current_user', + ); + $qb->setParameter('list_acp_current_user', $user); + + $i = 0; + foreach ($centers as $center) { + $scopes = $this->authorizationHelperForCurrentUser->getReachableScopes(AccompanyingPeriodVoter::SEE_DETAILS, $center); + $scopesConfidential = + $this->authorizationHelperForCurrentUser->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, $center); + $orScopes = $qb->expr()->orX(); + + foreach ($scopes as $scope) { + $scopeCondition = match (in_array($scope, $scopesConfidential, true)) { + true => ":scope_{$i} MEMBER OF acp.scopes", + false => $qb->expr()->andX( + 'acp.confidential = FALSE', + ":scope_{$i} MEMBER OF acp.scopes", + ), + }; + + $orScopes->add($scopeCondition); + $qb->setParameter("scope_{$i}", $scope); + ++$i; + } + + if ($this->filterStatsByCenters) { + $andX = $qb->expr()->andX( $qb->expr()->exists( - 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part - JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) - WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) - ' - ) - ) - ->setParameter('authorized_centers', $centers); + 'SELECT 1 FROM '.AccompanyingPeriodParticipation::class." acl_count_part_{$i} + JOIN ".PersonCenterHistory::class." acl_count_person_history_{$i} WITH IDENTITY(acl_count_person_history_{$i}.person) = IDENTITY(acl_count_part_{$i}.person) + WHERE acl_count_part_{$i}.accompanyingPeriod = acp.id AND acl_count_person_history_{$i}.center IN (:authorized_center_{$i}) + AND acl_count_person_history_{$i}.startDate <= CURRENT_DATE() AND (acl_count_person_history_{$i}.endDate IS NULL or acl_count_person_history_{$i}.endDate > CURRENT_DATE()) + " + ), + $orScopes, + ); + $qb->setParameter('authorized_center_'.$i, $center); + $aclConditionsOrX->add($andX); + } else { + $aclConditionsOrX->add($orScopes); + } + + ++$i; } + $qb->andWhere($aclConditionsOrX); + $this->listAccompanyingPeriodHelper->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date'])); AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb); diff --git a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodTest.php b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodTest.php index a74822584..234f9096d 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Export/Export/ListAccompanyingPeriodTest.php @@ -11,14 +11,22 @@ declare(strict_types=1); namespace Chill\PersonBundle\Tests\Export\Export; +use Chill\MainBundle\Entity\Center; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Repository\CenterRepositoryInterface; +use Chill\MainBundle\Repository\ScopeRepositoryInterface; +use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface; use Chill\MainBundle\Service\RollingDate\RollingDate; use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface; use Chill\MainBundle\Test\Export\AbstractExportTest; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Export\ListAccompanyingPeriod; use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper; +use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter; use Doctrine\ORM\EntityManagerInterface; +use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; +use Symfony\Component\Security\Core\Security; /** * @internal @@ -27,6 +35,8 @@ use Doctrine\ORM\EntityManagerInterface; */ class ListAccompanyingPeriodTest extends AbstractExportTest { + use ProphecyTrait; + private readonly ListAccompanyingPeriod $listAccompanyingPeriod; private readonly CenterRepositoryInterface $centerRepository; @@ -39,12 +49,49 @@ class ListAccompanyingPeriodTest extends AbstractExportTest public function getExport() { + /** @var EntityManagerInterface::class $em */ $em = self::$container->get(EntityManagerInterface::class); $rollingDateConverter = self::$container->get(RollingDateConverterInterface::class); $listAccompanyingPeriodHelper = self::$container->get(ListAccompanyingPeriodHelper::class); + $centerRepository = self::$container->get(CenterRepositoryInterface::class); + $scopeRepository = self::$container->get(ScopeRepositoryInterface::class); - yield new ListAccompanyingPeriod($em, $rollingDateConverter, $listAccompanyingPeriodHelper, $this->getParameters(true)); - yield new ListAccompanyingPeriod($em, $rollingDateConverter, $listAccompanyingPeriodHelper, $this->getParameters(false)); + // mock security + $user = $em->createQuery('SELECT u FROM '.User::class.' u') + ->setMaxResults(1)->getSingleResult(); + if (null === $user) { + throw new \RuntimeException('no user found'); + } + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn($user); + + // mock authorization helper + $scopes = $scopeRepository->findAll(); + $scopesConfidentials = [] !== $scopes ? [$scopes[0]] : []; + $authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class); + $authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_DETAILS, Argument::type(Center::class)) + ->willReturn($scopes); + $authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, Argument::type(Center::class)) + ->willReturn($scopesConfidentials); + + yield new ListAccompanyingPeriod( + $em, + $rollingDateConverter, + $listAccompanyingPeriodHelper, + $security->reveal(), + $centerRepository, + $authorizationHelper->reveal(), + $this->getParameters(true) + ); + yield new ListAccompanyingPeriod( + $em, + $rollingDateConverter, + $listAccompanyingPeriodHelper, + $security->reveal(), + $centerRepository, + $authorizationHelper->reveal(), + $this->getParameters(false) + ); } public function getFormData()