Skip to content

Commit

Permalink
dev: Move getAuthorizations from AppVoter to AuthorizationRepository
Browse files Browse the repository at this point in the history
  • Loading branch information
marien-probesys committed Mar 14, 2024
1 parent b30603e commit fec907a
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 53 deletions.
53 changes: 53 additions & 0 deletions src/Repository/AuthorizationRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
use Doctrine\Persistence\ManagerRegistry;

/**
* @phpstan-type AuthorizationType 'orga'|'admin'
* @phpstan-type Scope 'any'|Organization
*
* @extends ServiceEntityRepository<Authorization>
*
* @method Authorization|null find($id, $lockMode = null, $lockVersion = null)
Expand All @@ -30,6 +33,9 @@ class AuthorizationRepository extends ServiceEntityRepository implements UidGene
{
use UidGeneratorTrait;

/** @var array<string, Authorization[]> */
private array $cacheAuthorizations = [];

public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Authorization::class);
Expand Down Expand Up @@ -112,6 +118,32 @@ public function grantTeamAuthorization(Team $team, TeamAuthorization $teamAuthor
$this->getEntityManager()->flush();
}

/**
* @param AuthorizationType $authorizationType
* @param ?Scope $scope
*
* @return Authorization[]
*/
public function getAuthorizations(string $authorizationType, User $user, mixed $scope): array
{
$cacheKey = self::getCacheKey($authorizationType, $user, $scope);
if (isset($this->cacheAuthorizations[$cacheKey])) {
return $this->cacheAuthorizations[$cacheKey];
}

if ($authorizationType === 'orga' && $scope !== null) {
$authorizations = $this->getOrgaAuthorizationsFor($user, $scope);
$this->cacheAuthorizations[$cacheKey] = $authorizations;
} elseif ($authorizationType === 'admin' && $scope === null) {
$authorizations = $this->getAdminAuthorizationsFor($user);
$this->cacheAuthorizations[$cacheKey] = $authorizations;
} else {
throw new \DomainException('Given authorization type and scope are not supported together');
}

return $authorizations;
}

/**
* @return Authorization[]
*/
Expand Down Expand Up @@ -156,4 +188,25 @@ public function getOrgaAuthorizationsFor(User $user, mixed $scope): array
$query = $queryBuilder->getQuery();
return $query->getResult();
}

/**
* @param AuthorizationType $authorizationType
* @param ?Scope $scope
*/
private static function getCacheKey(string $authorizationType, User $user, mixed $scope): string
{
$baseKey = "{$authorizationType}.{$user->getId()}";

if ($scope === 'any') {
$baseKey .= '.any';
} elseif ($scope instanceof Organization) {
$baseKey .= ".{$scope->getId()}";
} elseif ($scope === null) {
$baseKey .= '.null';
} else {
throw new \DomainException('The given scope is not supported');
}

return hash('sha256', $baseKey);
}
}
55 changes: 2 additions & 53 deletions src/Security/AppVoter.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,14 @@
use Symfony\Component\Security\Core\Authorization\Voter\Voter;

/**
* @phpstan-type AuthorizationType 'orga'|'admin'
* @phpstan-type Scope 'any'|Organization
* @phpstan-import-type Scope from AuthorizationRepository
*
* @extends Voter<string, ?Scope>
*/
class AppVoter extends Voter
{
private AuthorizationRepository $authorizationRepo;

/** @var array<string, Authorization[]> */
private array $cacheAuthorizations = [];

public function __construct(AuthorizationRepository $authorizationRepo)
{
$this->authorizationRepo = $authorizationRepo;
Expand Down Expand Up @@ -59,7 +55,7 @@ protected function voteOnAttribute(string $attribute, mixed $scope, TokenInterfa
throw new \DomainException("Permission must start by 'orga:' or 'admin:' (got {$attribute})");
}

$authorizations = $this->getAuthorizations($authorizationType, $user, $scope);
$authorizations = $this->authorizationRepo->getAuthorizations($authorizationType, $user, $scope);

foreach ($authorizations as $authorization) {
if ($authorization->getRole()->hasPermission($attribute)) {
Expand All @@ -69,51 +65,4 @@ protected function voteOnAttribute(string $attribute, mixed $scope, TokenInterfa

return false;
}

/**
* @param AuthorizationType $authorizationType
* @param ?Scope $scope
*
* @return Authorization[]
*/
private function getAuthorizations(string $authorizationType, User $user, mixed $scope): array
{
$cacheKey = self::getCacheKey($authorizationType, $user, $scope);
if (isset($this->cacheAuthorizations[$cacheKey])) {
return $this->cacheAuthorizations[$cacheKey];
}

if ($authorizationType === 'orga' && $scope !== null) {
$authorizations = $this->authorizationRepo->getOrgaAuthorizationsFor($user, $scope);
$this->cacheAuthorizations[$cacheKey] = $authorizations;
} elseif ($authorizationType === 'admin' && $scope === null) {
$authorizations = $this->authorizationRepo->getAdminAuthorizationsFor($user);
$this->cacheAuthorizations[$cacheKey] = $authorizations;
} else {
throw new \DomainException('Given authorization type and scope are not supported together');
}

return $authorizations;
}

/**
* @param AuthorizationType $authorizationType
* @param ?Scope $scope
*/
private static function getCacheKey(string $authorizationType, User $user, mixed $scope): string
{
$baseKey = "{$authorizationType}.{$user->getId()}";

if ($scope === 'any') {
$baseKey .= '.any';
} elseif ($scope instanceof Organization) {
$baseKey .= ".{$scope->getId()}";
} elseif ($scope === null) {
$baseKey .= '.null';
} else {
throw new \DomainException('The given scope is not supported');
}

return hash('sha256', $baseKey);
}
}

0 comments on commit fec907a

Please sign in to comment.