handle right to see confidential course on regulation list

This commit is contained in:
2023-07-05 16:23:14 +02:00
parent a56370d851
commit a990591e0c
4 changed files with 229 additions and 37 deletions

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Repository;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
@@ -26,6 +27,8 @@ use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use DateTime;
use DateTimeImmutable;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Repository\AccompanyingPeriodACLAwareRepositoryTest;
@@ -107,9 +110,16 @@ final readonly class AccompanyingPeriodACLAwareRepository implements Accompanyin
return $qb;
}
/**
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function countByUnDispatched(array $jobs, array $services, array $administrativeLocations): int
{
$qb = $this->addACLByUnDispatched($this->buildQueryUnDispatched($jobs, $services, $administrativeLocations));
$qb = $this->addACLByUnDispatched(
$this->buildQueryUnDispatched($jobs, $services, $administrativeLocations),
$this->buildCenterOnScope()
);
$qb->select('COUNT(ap)');
@@ -194,6 +204,8 @@ final readonly class AccompanyingPeriodACLAwareRepository implements Accompanyin
}
/**
* Add clause for scope on a query, based on no
*
* @param QueryBuilder $qb where the accompanying period have the `ap` alias
* @param array<Scope> $scopesCanSee
* @param array<Scope> $scopesCanSeeConfidential
@@ -252,19 +264,27 @@ final readonly class AccompanyingPeriodACLAwareRepository implements Accompanyin
return $qb;
}
public function findByUnDispatched(array $jobs, array $services, array $administrativeLocations, ?int $limit = null, ?int $offset = null): array
public function buildCenterOnScope(): array
{
$qb = $this->addACLByUnDispatched($this->buildQueryUnDispatched($jobs, $services, $administrativeLocations));
$centerOnScopes = [];
foreach ($this->authorizationHelper->getReachableCenters(AccompanyingPeriodVoter::SEE) as $center) {
$centerOnScopes[] = [
'center' => $center,
'scopeOnRole' => $this->authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE, $center),
'scopeCanSeeConfidential' => $this->authorizationHelper->getReachableScopes(AccompanyingPeriodVoter::SEE_CONFIDENTIAL_ALL, $center),
];
}
return $centerOnScopes;
}
public function findByUnDispatched(array $jobs, array $services, array $administrativeAdministrativeLocations, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
{
$qb = $this->buildQueryUnDispatched($jobs, $services, $administrativeAdministrativeLocations);
$qb->select('ap');
if (null !== $limit) {
$qb->setMaxResults($limit);
}
if (null !== $offset) {
$qb->setFirstResult($offset);
}
$qb = $this->addACLByUnDispatched($qb, $this->buildCenterOnScope(), false);
$qb = $this->addOrderLimitClauses($qb, $orderBy, $limit, $offset);
return $qb->getQuery()->getResult();
}
@@ -305,41 +325,73 @@ final readonly class AccompanyingPeriodACLAwareRepository implements Accompanyin
return $qb->getQuery()->getResult();
}
private function addACLByUnDispatched(QueryBuilder $qb): QueryBuilder
/**
* @param QueryBuilder $qb
* @param list<array{center: Center, scopeOnRole: list<Scope>, scopeCanSeeConfidential: list<Scope>}> $centerScopes
* @param bool $allowNoCenter if true, will allow to see the periods linked to person which does not have any center. Very few edge case when some Person are not associated to a center.
* @return QueryBuilder
*/
public function addACLByUnDispatched(QueryBuilder $qb, array $centerScopes, bool $allowNoCenter = false): QueryBuilder
{
$centers = $this->authorizationHelper->getReachableCenters(
AccompanyingPeriodVoter::SEE
);
$user = $this->security->getUser();
$orX = $qb->expr()->orX();
if (0 === count($centers)) {
if (0 === count($centerScopes) || !$user instanceof User) {
return $qb->andWhere("'FALSE' = 'TRUE'");
}
foreach ($centers as $key => $center) {
$scopes = $this->authorizationHelper
->getReachableScopes(
AccompanyingPeriodVoter::SEE,
$center
);
$orX = $qb->expr()->orX();
$idx = 0;
foreach ($centerScopes as ['center' => $center, 'scopeOnRole' => $scopes, 'scopeCanSeeConfidential' => $scopesCanSeeConfidential]) {
$and = $qb->expr()->andX(
$qb->expr()->exists('SELECT part FROM ' . AccompanyingPeriodParticipation::class . ' part ' .
"JOIN part.person p WHERE part.accompanyingPeriod = ap.id AND p.center = :center_{$key}")
$qb->expr()->exists(
'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . " part_{$idx} " .
"JOIN part_{$idx}.person p{$idx} LEFT JOIN p{$idx}.centerCurrent centerCurrent_{$idx} " .
"WHERE part_{$idx}.accompanyingPeriod = ap.id AND (centerCurrent_{$idx}.center = :center_{$idx}"
. ($allowNoCenter ? " OR centerCurrent_{$idx}.id IS NULL)" : ")")
)
);
$qb->setParameter('center_' . $key, $center);
$orScope = $qb->expr()->orX();
$qb->setParameter('center_' . $idx, $center);
foreach ($scopes as $skey => $scope) {
$orScope->add(
$qb->expr()->isMemberOf(':scope_' . $key . '_' . $skey, 'ap.scopes')
$orScopeInsideCenter = $qb->expr()->orX(
// even if the scope is not in one authorized, the user can see the course if it is in DRAFT state
$qb->expr()->eq('ap.step', ':draft')
);
$idx++;
foreach ($scopes as $scope) {
// for each scope:
// - either the user is the referrer of the course
// - or the accompanying course is one of the reachable scopes
// - and the parcours is not confidential OR the user is the referrer OR the user can see the confidential course
$orOnScope = $qb->expr()->orX(
$qb->expr()->isMemberOf(':scope_' . $idx, 'ap.scopes'),
$qb->expr()->eq('ap.user', ':user')
);
$qb->setParameter('scope_' . $key . '_' . $skey, $scope);
$qb->setParameter('user', $user);
if (in_array($scope, $scopesCanSeeConfidential, true)) {
$orScopeInsideCenter->add($orOnScope);
} else {
// we must add a condition: the course is not confidential or the user is the referrer
$andXOnScope = $qb->expr()->andX(
$orOnScope,
$qb->expr()->orX(
'ap.confidential = FALSE',
$qb->expr()->eq('ap.user', ':user')
)
);
$orScopeInsideCenter->add($andXOnScope);
}
$qb->setParameter('scope_' . $idx, $scope);
$idx++;
}
$and->add($orScope);
$and->add($orScopeInsideCenter);
$orX->add($and);
$idx++;
}
return $qb->andWhere($orX);
@@ -350,7 +402,7 @@ final readonly class AccompanyingPeriodACLAwareRepository implements Accompanyin
* @param array|Scope[] $services
* @param array|Location[] $locations
*/
private function buildQueryUnDispatched(array $jobs, array $services, array $locations): QueryBuilder
public function buildQueryUnDispatched(array $jobs, array $services, array $locations): QueryBuilder
{
$qb = $this->accompanyingPeriodRepository->createQueryBuilder('ap');
@@ -378,8 +430,8 @@ final readonly class AccompanyingPeriodACLAwareRepository implements Accompanyin
$or = $qb->expr()->orX();
foreach ($services as $key => $service) {
$or->add($qb->expr()->isMemberOf(':scope_' . $key, 'ap.scopes'));
$qb->setParameter('scope_' . $key, $service);
$or->add($qb->expr()->isMemberOf(':scopef_' . $key, 'ap.scopes'));
$qb->setParameter('scopef_' . $key, $service);
}
$qb->andWhere($or);
}