accompanyingPeriodRepository = self::$container->get(AccompanyingPeriodRepository::class); $this->centerRepository = self::$container->get(CenterRepositoryInterface::class); $this->centerResolverManager = self::$container->get(CenterResolverManagerInterface::class); $this->entityManager = self::$container->get(EntityManagerInterface::class); $this->scopeRepository = self::$container->get(ScopeRepositoryInterface::class); $this->registry = self::$container->get(Registry::class); } public static function tearDownAfterClass(): void { self::bootKernel(); $em = self::$container->get(EntityManagerInterface::class); $repository = self::$container->get(AccompanyingPeriodRepository::class); foreach (self::$periodsIdsToDelete as $id) { if (null === $period = $repository->find($id)) { throw new \RuntimeException("period not found while trying to delete it"); } foreach ($period->getParticipations() as $participation) { $em->remove($participation); } $em->remove($period); } //$em->flush(); } /** * @dataProvider provideDataFindByUserAndPostalCodesOpenedAccompanyingPeriod * @param list, scopeCanSeeConfidential: list}> $centerScopes * @param list $expectedContains * @param list $expectedNotContains */ public function testFindByUserAndPostalCodesOpenedAccompanyingPeriod(User $user, User $searched, array $centerScopes, array $expectedContains, array $expectedNotContains, string $message): void { $security = $this->prophesize(Security::class); $security->getUser()->willReturn($user); $authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class); $centers = []; foreach ($centerScopes as ['center' => $center, 'scopeOnRole' => $scopes, 'scopeCanSeeConfidential' => $scopesCanSeeConfidential]) { $centers[spl_object_hash($center)] = $center; $authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE, $center) ->willReturn($scopes); $authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, $center) ->willReturn($scopesCanSeeConfidential); } $authorizationHelper->getReachableCenters(AccompanyingPeriodVoter::SEE)->willReturn(array_values($centers)); $repository = new AccompanyingPeriodACLAwareRepository( $this->accompanyingPeriodRepository, $security->reveal(), $authorizationHelper->reveal(), $this->centerResolverManager ); $actual = array_map( fn (AccompanyingPeriod $period) => $period->getId(), $repository->findByUserAndPostalCodesOpenedAccompanyingPeriod($searched, [], ['id' => 'DESC'], 20, 0) ); foreach ($expectedContains as $expected) { self::assertContains($expected->getId(), $actual, $message); } foreach ($expectedNotContains as $expected) { self::assertNotContains($expected->getId(), $actual, $message); } } public function provideDataFindByUserAndPostalCodesOpenedAccompanyingPeriod(): iterable { $this->setUp(); if (null === $user = $this->entityManager->createQuery("SELECT u FROM " . User::class . " u")->setMaxResults(1)->getSingleResult()) { throw new \RuntimeException("no user found"); } if (null === $anotherUser = $this->entityManager->createQuery("SELECT u FROM " . User::class . " u WHERE u.id != :uid")->setParameter('uid', $user->getId()) ->setMaxResults(1)->getSingleResult()) { throw new \RuntimeException("no user found"); } /** @var Person $person */ [$person, $anotherPerson, $person2, $person3] = $this->entityManager ->createQuery("SELECT p FROM " . Person::class . " p JOIN p.centerCurrent current_center") ->setMaxResults(4) ->getResult(); if (null === $person || null === $anotherPerson || null === $person2 || null === $person3) { throw new \RuntimeException("no person found"); } $scopes = $this->scopeRepository->findAll(); if (3 > count($scopes)) { throw new \RuntimeException("not enough scopes for this test"); } $scopesCanSee = [ $scopes[0] ]; $scopesGroup2 = [ $scopes[1] ]; $centers = $this->centerRepository->findActive(); $aCenterNotAssociatedToPerson = array_values(array_filter($centers, fn (Center $c) => $c !== $person->getCenter()))[0]; if (2 > count($centers)) { throw new \RuntimeException("not enough centers for this test"); } $period = $this->buildPeriod($person, $scopesCanSee, $user, true); $period->setUser($user); yield [ $anotherUser, $user, [ [ 'center' => $person->getCenter(), 'scopeOnRole' => $scopesCanSee, 'scopeCanSeeConfidential' => [], ], ], [$period], [], "period should be visible with expected scopes", ]; yield [ $anotherUser, $user, [ [ 'center' => $person->getCenter(), 'scopeOnRole' => $scopesGroup2, 'scopeCanSeeConfidential' => [], ], ], [], [$period], "period should not be visible without expected scopes", ]; yield [ $anotherUser, $user, [ [ 'center' => $person->getCenter(), 'scopeOnRole' => $scopesGroup2, 'scopeCanSeeConfidential' => [], ], [ 'center' => $aCenterNotAssociatedToPerson, 'scopeOnRole' => $scopesCanSee, 'scopeCanSeeConfidential' => [], ], ], [], [$period], "period should not be visible for user having right in another scope (with multiple centers)" ]; $period = $this->buildPeriod($person, $scopesCanSee, $user, true); $period->setUser($user); $period->setConfidential(true); yield [ $anotherUser, $user, [ [ 'center' => $person->getCenter(), 'scopeOnRole' => $scopesCanSee, 'scopeCanSeeConfidential' => [], ], ], [], [$period], "period confidential should not be visible", ]; yield [ $anotherUser, $user, [ [ 'center' => $person->getCenter(), 'scopeOnRole' => $scopesCanSee, 'scopeCanSeeConfidential' => $scopesCanSee, ], ], [$period], [], "period confidential be visible if user has required scopes", ]; $this->entityManager->flush(); } /** * @dataProvider provideDataFindByUndispatched * @param list, scopeCanSeeConfidential: list}> $centerScopes * @param list $expectedContains * @param list $expectedNotContains */ public function testFindByUndispatched(User $user, array $centerScopes, array $expectedContains, array $expectedNotContains, string $message): void { $security = $this->prophesize(Security::class); $security->getUser()->willReturn($user); $authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class); $centers = []; foreach ($centerScopes as ['center' => $center, 'scopeOnRole' => $scopes, 'scopeCanSeeConfidential' => $scopesCanSeeConfidential]) { $centers[spl_object_hash($center)] = $center; $authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE, $center) ->willReturn($scopes); $authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, $center) ->willReturn($scopesCanSeeConfidential); } $authorizationHelper->getReachableCenters(AccompanyingPeriodVoter::SEE)->willReturn(array_values($centers)); $repository = new AccompanyingPeriodACLAwareRepository( $this->accompanyingPeriodRepository, $security->reveal(), $authorizationHelper->reveal(), $this->centerResolverManager ); $actual = array_map( fn (AccompanyingPeriod $period) => $period->getId(), $repository->findByUnDispatched([], [], [], ['id' => 'DESC'], 20, 0) ); foreach ($expectedContains as $expected) { self::assertContains($expected->getId(), $actual, $message); } foreach ($expectedNotContains as $expected) { self::assertNotContains($expected->getId(), $actual, $message); } } public function provideDataFindByUndispatched(): iterable { $this->setUp(); if (null === $user = $this->entityManager->createQuery("SELECT u FROM " . User::class . " u")->setMaxResults(1)->getSingleResult()) { throw new \RuntimeException("no user found"); } if (null === $anotherUser = $this->entityManager->createQuery("SELECT u FROM " . User::class . " u WHERE u.id != :uid")->setParameter('uid', $user->getId()) ->setMaxResults(1)->getSingleResult()) { throw new \RuntimeException("no user found"); } /** @var Person $person */ [$person, $anotherPerson, $person2, $person3] = $this->entityManager ->createQuery("SELECT p FROM " . Person::class . " p ") ->setMaxResults(4) ->getResult(); if (null === $person || null === $anotherPerson || null === $person2 || null === $person3) { throw new \RuntimeException("no person found"); } $scopes = $this->scopeRepository->findAll(); if (3 > count($scopes)) { throw new \RuntimeException("not enough scopes for this test"); } $scopesCanSee = [ $scopes[0] ]; $scopesGroup2 = [ $scopes[1] ]; $centers = $this->centerRepository->findActive(); if (2 > count($centers)) { throw new \RuntimeException("not enough centers for this test"); } $period = $this->buildPeriod($person, $scopesCanSee, $user, true); // expected scope: can see the period yield [ $anotherUser, [ [ 'center' => $person->getCenter(), 'scopeOnRole' => $scopesCanSee, 'scopeCanSeeConfidential' => [], ], ], [$period], [], "period should be visible with expected scopes", ]; // no scope visible yield [ $anotherUser, [ [ 'center' => $person->getCenter(), 'scopeOnRole' => $scopesGroup2, 'scopeCanSeeConfidential' => [], ], ], [], [$period], "period should not be visible without expected scopes", ]; // another center yield [ $anotherUser, [ [ 'center' => $person->getCenter(), 'scopeOnRole' => $scopesGroup2, 'scopeCanSeeConfidential' => [], ], [ 'center' => array_values(array_filter($centers, fn (Center $c) => $c !== $person->getCenter()))[0], 'scopeOnRole' => $scopesCanSee, 'scopeCanSeeConfidential' => [], ], ], [], [$period], "period should not be visible for user having right in another scope (with multiple centers)" ]; $this->entityManager->flush(); } /** * For testing this method, we mock the authorization helper to return different Scope that a user * can see, or that a user can see confidential periods. * * @param array $scopeUserCanSee * @param array $scopeUserCanSeeConfidential * @param array $expectedPeriod * @dataProvider provideDataForFindByPerson */ public function testFindByPersonTestUser(User $user, Person $person, array $scopeUserCanSee, array $scopeUserCanSeeConfidential, array $expectedPeriod, string $message): void { $security = $this->prophesize(Security::class); $security->getUser()->willReturn($user); $authorizationHelper = $this->prophesize(AuthorizationHelperForCurrentUserInterface::class); $authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE, Argument::any()) ->willReturn($scopeUserCanSee); $authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, Argument::any()) ->willReturn($scopeUserCanSeeConfidential); $repository = new AccompanyingPeriodACLAwareRepository( $this->accompanyingPeriodRepository, $security->reveal(), $authorizationHelper->reveal(), $this->centerResolverManager ); $actuals = $repository->findByPerson($person, AccompanyingPeriodVoter::SEE); $expectedIds = array_map(fn (AccompanyingPeriod $period) => $period->getId(), $expectedPeriod); self::assertCount(count($expectedPeriod), $actuals, $message); foreach ($actuals as $actual) { self::assertContains($actual->getId(), $expectedIds); } } public function provideDataForFindByPerson(): iterable { $this->setUp(); if (null === $user = $this->entityManager->createQuery("SELECT u FROM " . User::class . " u")->setMaxResults(1)->getSingleResult()) { throw new \RuntimeException("no user found"); } if (null === $anotherUser = $this->entityManager->createQuery("SELECT u FROM " . User::class . " u WHERE u.id != :uid")->setParameter('uid', $user->getId()) ->setMaxResults(1)->getSingleResult()) { throw new \RuntimeException("no user found"); } [$person, $anotherPerson, $person2, $person3] = $this->entityManager ->createQuery("SELECT p FROM " . Person::class . " p WHERE SIZE(p.accompanyingPeriodParticipations) = 0") ->setMaxResults(4) ->getResult(); if (null === $person || null === $anotherPerson || null === $person2 || null === $person3) { throw new \RuntimeException("no person found"); } $scopes = $this->scopeRepository->findAll(); if (3 > count($scopes)) { throw new \RuntimeException("not enough scopes for this test"); } $scopesCanSee = [ $scopes[0] ]; $scopesGroup2 = [ $scopes[1] ]; // case: a period is in draft state $period = $this->buildPeriod($person, $scopesCanSee, $user, false); yield [$user, $person, $scopesCanSee, [], [$period], "a user can see his period during draft state"]; // another user is not allowed to see this period, because it is in DRAFT state yield [$anotherUser, $person, $scopesCanSee, [], [], "another user is not allowed to see the period of someone else in draft state"]; // the period is confirmed $period = $this->buildPeriod($anotherPerson, $scopesCanSee, $user, true); // the other user can now see it yield [$user, $anotherPerson, $scopesCanSee, [], [$period], "a user see his period when confirmed"]; yield [$anotherUser, $anotherPerson, $scopesCanSee, [], [$period], "another user with required scopes is allowed to see the period when not draft"]; yield [$anotherUser, $anotherPerson, $scopesGroup2, [], [], "another user without the required scopes is not allowed to see the period when not draft"]; // this period will be confidential $period = $this->buildPeriod($person2, $scopesCanSee, $user, true); $period->setConfidential(true)->setUser($user, true); yield [$user, $person2, $scopesCanSee, [], [$period], "a user see his period when confirmed and confidential with required scopes"]; yield [$user, $person2, $scopesGroup2, [], [$period], "a user see his period when confirmed and confidential without required scopes"]; yield [$anotherUser, $person2, $scopesCanSee, [], [], "a user don't see a confidential period, even if he has required scopes"]; yield [$anotherUser, $person2, $scopesCanSee, $scopesCanSee, [$period], "a user see the period when confirmed and confidential if he has required scope to see the period"]; // period draft with creator = null $period = $this->buildPeriod($person3, $scopesCanSee, null, false); yield [$user, $person3, $scopesCanSee, [], [$period], "a user see a period when draft if no creator on the period"]; $this->entityManager->flush(); } /** * @param Person $person * @param array $scopes * @return AccompanyingPeriod */ private function buildPeriod(Person $person, array $scopes, User|null $creator, bool $confirm): AccompanyingPeriod { $period = new AccompanyingPeriod(); $period->addPerson($person); if (null !== $creator) { $period->setCreatedBy($creator); } foreach ($scopes as $scope) { $period->addScope($scope); } $this->entityManager->persist($period); self::$periodsIdsToDelete[] = $period->getId(); if ($confirm) { $workflow = $this->registry->get($period, 'accompanying_period_lifecycle'); $workflow->apply($period, 'confirm'); } return $period; } }