vendor/ezsystems/ezplatform-kernel/eZ/Publish/Core/Repository/Permission/CachedPermissionService.php line 32

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. declare(strict_types=1);
  7. namespace eZ\Publish\Core\Repository\Permission;
  8. use eZ\Publish\API\Repository\PermissionResolver as APIPermissionResolver;
  9. use eZ\Publish\API\Repository\PermissionCriterionResolver as APIPermissionCriterionResolver;
  10. use eZ\Publish\API\Repository\PermissionService;
  11. use eZ\Publish\API\Repository\Repository as RepositoryInterface;
  12. use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
  13. use eZ\Publish\API\Repository\Values\User\LookupLimitationResult;
  14. use eZ\Publish\API\Repository\Values\User\UserReference;
  15. use eZ\Publish\API\Repository\Values\ValueObject;
  16. use Exception;
  17. /**
  18.  * Cache implementation of PermissionResolver and PermissionCriterionResolver interface.
  19.  *
  20.  * Implements both interfaces as the cached permission criterion lookup needs to be
  21.  * expired when a different user is set as current users in the system.
  22.  *
  23.  * Cache is only done for content/read policy, as that is the one needed by search service.
  24.  *
  25.  * The logic here uses a cache TTL of a few seconds, as this is in-memory cache we are not
  26.  * able to know if any other concurrent user might be changing permissions.
  27.  */
  28. class CachedPermissionService implements PermissionService
  29. {
  30.     /** @var \eZ\Publish\API\Repository\PermissionResolver */
  31.     private $innerPermissionResolver;
  32.     /** @var \eZ\Publish\API\Repository\PermissionCriterionResolver */
  33.     private $permissionCriterionResolver;
  34.     /** @var int */
  35.     private $cacheTTL;
  36.     /**
  37.      * Counter for the current sudo nesting level {@see sudo()}.
  38.      *
  39.      * @var int
  40.      */
  41.     private $sudoNestingLevel 0;
  42.     /**
  43.      * Cached value for current user's getCriterion() result.
  44.      *
  45.      * Value is null if not yet set or cleared.
  46.      *
  47.      * @var bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion
  48.      */
  49.     private $permissionCriterion;
  50.     /**
  51.      * Cache time stamp.
  52.      *
  53.      * @var int
  54.      */
  55.     private $permissionCriterionTs;
  56.     /**
  57.      * CachedPermissionService constructor.
  58.      *
  59.      * @param \eZ\Publish\API\Repository\PermissionResolver $innerPermissionResolver
  60.      * @param \eZ\Publish\API\Repository\PermissionCriterionResolver $permissionCriterionResolver
  61.      * @param int $cacheTTL By default set to 5 seconds, should be low to avoid to many permission exceptions on long running requests / processes (even if tolerant search service should handle that)
  62.      */
  63.     public function __construct(
  64.         APIPermissionResolver $innerPermissionResolver,
  65.         APIPermissionCriterionResolver $permissionCriterionResolver,
  66.         int $cacheTTL 5
  67.     ) {
  68.         $this->innerPermissionResolver $innerPermissionResolver;
  69.         $this->permissionCriterionResolver $permissionCriterionResolver;
  70.         $this->cacheTTL $cacheTTL;
  71.     }
  72.     public function getCurrentUserReference(): UserReference
  73.     {
  74.         return $this->innerPermissionResolver->getCurrentUserReference();
  75.     }
  76.     public function setCurrentUserReference(UserReference $userReference): void
  77.     {
  78.         $this->permissionCriterion null;
  79.         $this->innerPermissionResolver->setCurrentUserReference($userReference);
  80.     }
  81.     public function hasAccess(string $modulestring $function, ?UserReference $userReference null)
  82.     {
  83.         return $this->innerPermissionResolver->hasAccess($module$function$userReference);
  84.     }
  85.     public function canUser(string $modulestring $functionValueObject $object, array $targets = []): bool
  86.     {
  87.         return $this->innerPermissionResolver->canUser($module$function$object$targets);
  88.     }
  89.     /**
  90.      * {@inheritdoc}
  91.      */
  92.     public function lookupLimitations(
  93.         string $module,
  94.         string $function,
  95.         ValueObject $object,
  96.         array $targets = [],
  97.         array $limitations = []
  98.     ): LookupLimitationResult {
  99.         return $this->innerPermissionResolver->lookupLimitations($module$function$object$targets$limitations);
  100.     }
  101.     public function getPermissionsCriterion(string $module 'content'string $function 'read', ?array $targets null)
  102.     {
  103.         // We only cache content/read lookup as those are the once frequently done, and it's only one we can safely
  104.         // do that won't harm the system if it becomes stale (but user might experience permissions exceptions if it do)
  105.         if ($module !== 'content' || $function !== 'read' || $this->sudoNestingLevel 0) {
  106.             return $this->permissionCriterionResolver->getPermissionsCriterion($module$function$targets);
  107.         }
  108.         if ($this->permissionCriterion !== null) {
  109.             // If we are still within the cache TTL, then return the cached value
  110.             if ((time() - $this->permissionCriterionTs) < $this->cacheTTL) {
  111.                 return $this->permissionCriterion;
  112.             }
  113.         }
  114.         $this->permissionCriterionTs time();
  115.         $this->permissionCriterion $this->permissionCriterionResolver->getPermissionsCriterion($module$function$targets);
  116.         return $this->permissionCriterion;
  117.     }
  118.     /**
  119.      * @internal For internal use only, do not depend on this method.
  120.      */
  121.     public function sudo(callable $callbackRepositoryInterface $outerRepository)
  122.     {
  123.         ++$this->sudoNestingLevel;
  124.         try {
  125.             $returnValue $this->innerPermissionResolver->sudo($callback$outerRepository);
  126.         } catch (Exception $e) {
  127.             --$this->sudoNestingLevel;
  128.             throw $e;
  129.         }
  130.         --$this->sudoNestingLevel;
  131.         return $returnValue;
  132.     }
  133.     public function getQueryPermissionsCriterion(): Criterion
  134.     {
  135.         return $this->permissionCriterionResolver->getQueryPermissionsCriterion();
  136.     }
  137. }