vendor/ezsystems/ezpublish-kernel/eZ/Publish/Core/Persistence/Cache/UserHandler.php line 388

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) eZ Systems AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. namespace eZ\Publish\Core\Persistence\Cache;
  7. use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct;
  8. use eZ\Publish\SPI\Persistence\User\Handler as UserHandlerInterface;
  9. use eZ\Publish\SPI\Persistence\User;
  10. use eZ\Publish\SPI\Persistence\User\Role;
  11. use eZ\Publish\SPI\Persistence\User\RoleAssignment;
  12. use eZ\Publish\SPI\Persistence\User\RoleCreateStruct;
  13. use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct;
  14. use eZ\Publish\SPI\Persistence\User\Policy;
  15. /**
  16.  * Cache handler for user module.
  17.  */
  18. class UserHandler extends AbstractInMemoryPersistenceHandler implements UserHandlerInterface
  19. {
  20.     /** @var callable */
  21.     private $getUserTags;
  22.     /** @var callable */
  23.     private $getUserKeys;
  24.     /** @var callable */
  25.     private $getRoleTags;
  26.     /** @var callable */
  27.     private $getRoleKeys;
  28.     /** @var callable */
  29.     private $getRoleAssignmentTags;
  30.     /** @var callable */
  31.     private $getRoleAssignmentKeys;
  32.     /**
  33.      * Set callback functions for use in cache retrival.
  34.      */
  35.     public function init(): void
  36.     {
  37.         $this->getUserTags = static function (User $user) {
  38.             return ['content-' $user->id'user-' $user->id];
  39.         };
  40.         $this->getUserKeys = function (User $user) {
  41.             return [
  42.                 'ez-user-' $user->id,
  43.                 'ez-user-' $this->escapeForCacheKey($user->login) . '-by-login',
  44.                 //'ez-user-' . $hash . '-by-account-key',
  45.             ];
  46.         };
  47.         $this->getRoleTags = static function (Role $role) {
  48.             return ['role-' $role->id];
  49.         };
  50.         $this->getRoleKeys = function (Role $role) {
  51.             return [
  52.                 'ez-role-' $role->id,
  53.                 'ez-role-' $this->escapeForCacheKey($role->identifier) . '-by-identifier',
  54.             ];
  55.         };
  56.         $this->getRoleAssignmentTags = static function (RoleAssignment $roleAssignment) {
  57.             return [
  58.                 'role-assignment-' $roleAssignment->id,
  59.                 'role-assignment-group-list-' $roleAssignment->contentId,
  60.                 'role-assignment-role-list-' $roleAssignment->roleId,
  61.             ];
  62.         };
  63.         $this->getRoleAssignmentKeys = static function (RoleAssignment $roleAssignment) {
  64.             return [
  65.                 'ez-role-assignment-' $roleAssignment->id,
  66.             ];
  67.         };
  68.     }
  69.     /**
  70.      * {@inheritdoc}
  71.      */
  72.     public function create(User $user)
  73.     {
  74.         $this->logger->logCall(__METHOD__, ['struct' => $user]);
  75.         $return $this->persistenceHandler->userHandler()->create($user);
  76.         // Clear corresponding content cache as creation of the User changes it's external data
  77.         $this->cache->invalidateTags(['content-' $user->id]);
  78.         $this->cache->deleteItems([
  79.             'ez-user-' $user->id,
  80.             'ez-user-' $this->escapeForCacheKey($user->login) . '-by-login',
  81.             'ez-user-' $this->escapeForCacheKey($user->email) . '-by-email',
  82.         ]);
  83.         return $return;
  84.     }
  85.     /**
  86.      * {@inheritdoc}
  87.      */
  88.     public function load($userId)
  89.     {
  90.         return $this->getCacheValue(
  91.             $userId,
  92.             'ez-user-',
  93.             function ($userId) {
  94.                 return $this->persistenceHandler->userHandler()->load($userId);
  95.             },
  96.             $this->getUserTags,
  97.             $this->getUserKeys
  98.         );
  99.     }
  100.     /**
  101.      * {@inheritdoc}
  102.      */
  103.     public function loadByLogin($login)
  104.     {
  105.         return $this->getCacheValue(
  106.             $this->escapeForCacheKey($login),
  107.             'ez-user-',
  108.             function () use ($login) {
  109.                 return $this->persistenceHandler->userHandler()->loadByLogin($login);
  110.             },
  111.             $this->getUserTags,
  112.             $this->getUserKeys,
  113.             '-by-login'
  114.         );
  115.     }
  116.     /**
  117.      * {@inheritdoc}
  118.      */
  119.     public function loadByEmail($email)
  120.     {
  121.         // As load by email can return several items we threat it like a list here.
  122.         return $this->getListCacheValue(
  123.             'ez-user-' $this->escapeForCacheKey($email) . '-by-email',
  124.             function () use ($email) {
  125.                 return $this->persistenceHandler->userHandler()->loadByEmail($email);
  126.             },
  127.             $this->getUserTags,
  128.             $this->getUserKeys
  129.         );
  130.     }
  131.     /**
  132.      * {@inheritdoc}
  133.      */
  134.     public function loadUserByToken($hash)
  135.     {
  136.         $getUserKeysFn $this->getUserKeys;
  137.         $getUserTagsFn $this->getUserTags;
  138.         return $this->getCacheValue(
  139.             $hash,
  140.             'ez-user-',
  141.             function ($hash) {
  142.                 return $this->persistenceHandler->userHandler()->loadUserByToken($hash);
  143.             },
  144.             static function (User $user) use ($getUserTagsFn) {
  145.                 $tags $getUserTagsFn($user);
  146.                 // See updateUserToken()
  147.                 $tags[] = 'user-' $user->id '-account-key';
  148.                 return $tags;
  149.             },
  150.             static function (User $user) use ($hash$getUserKeysFn) {
  151.                 $keys $getUserKeysFn($user);
  152.                 $keys[] = 'ez-user-' $hash '-by-account-key';
  153.                 return $keys;
  154.             },
  155.             '-by-account-key'
  156.         );
  157.     }
  158.     /**
  159.      * {@inheritdoc}
  160.      */
  161.     public function update(User $user)
  162.     {
  163.         $this->logger->logCall(__METHOD__, ['struct' => $user]);
  164.         $return $this->persistenceHandler->userHandler()->update($user);
  165.         // Clear corresponding content cache as update of the User changes it's external data
  166.         $this->cache->invalidateTags(['content-' $user->id'user-' $user->id]);
  167.         // Clear especially by email key as it might already be cached and this might represent change to email
  168.         $this->cache->deleteItems(['ez-user-' $this->escapeForCacheKey($user->email) . '-by-email']);
  169.         return $return;
  170.     }
  171.     /**
  172.      * {@inheritdoc}
  173.      */
  174.     public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct)
  175.     {
  176.         $this->logger->logCall(__METHOD__, ['struct' => $userTokenUpdateStruct]);
  177.         $return $this->persistenceHandler->userHandler()->updateUserToken($userTokenUpdateStruct);
  178.         // As we 1. don't know original hash, and 2. hash is not guaranteed to be unique, we do it like this for now
  179.         $this->cache->invalidateTags(['user-' $userTokenUpdateStruct->userId '-account-key']);
  180.         $this->cache->deleteItems(['ez-user-' $userTokenUpdateStruct->hashKey '-by-account-key']);
  181.         return $return;
  182.     }
  183.     /**
  184.      * {@inheritdoc}
  185.      */
  186.     public function expireUserToken($hash)
  187.     {
  188.         $this->logger->logCall(__METHOD__, ['hash' => $hash]);
  189.         $return $this->persistenceHandler->userHandler()->expireUserToken($hash);
  190.         $this->cache->deleteItems(['ez-user-' $hash '-by-account-key']);
  191.         return $return;
  192.     }
  193.     /**
  194.      * {@inheritdoc}
  195.      */
  196.     public function delete($userId)
  197.     {
  198.         $this->logger->logCall(__METHOD__, ['user' => $userId]);
  199.         $return $this->persistenceHandler->userHandler()->delete($userId);
  200.         // user id == content id == group id
  201.         $this->cache->invalidateTags(['content-' $userId'user-' $userId]);
  202.         return $return;
  203.     }
  204.     /**
  205.      * {@inheritdoc}
  206.      */
  207.     public function createRole(RoleCreateStruct $createStruct)
  208.     {
  209.         $this->logger->logCall(__METHOD__, ['struct' => $createStruct]);
  210.         return $this->persistenceHandler->userHandler()->createRole($createStruct);
  211.     }
  212.     /**
  213.      * {@inheritdoc}
  214.      */
  215.     public function createRoleDraft($roleId)
  216.     {
  217.         $this->logger->logCall(__METHOD__, ['role' => $roleId]);
  218.         return $this->persistenceHandler->userHandler()->createRoleDraft($roleId);
  219.     }
  220.     /**
  221.      * {@inheritdoc}
  222.      */
  223.     public function loadRole($roleId$status Role::STATUS_DEFINED)
  224.     {
  225.         if ($status !== Role::STATUS_DEFINED) {
  226.             $this->logger->logCall(__METHOD__, ['role' => $roleId]);
  227.             return $this->persistenceHandler->userHandler()->loadRole($roleId$status);
  228.         }
  229.         return $this->getCacheValue(
  230.             $roleId,
  231.             'ez-role-',
  232.             function ($roleId) {
  233.                 return $this->persistenceHandler->userHandler()->loadRole($roleId);
  234.             },
  235.             $this->getRoleTags,
  236.             $this->getRoleKeys
  237.         );
  238.     }
  239.     /**
  240.      * {@inheritdoc}
  241.      */
  242.     public function loadRoleByIdentifier($identifier$status Role::STATUS_DEFINED)
  243.     {
  244.         if ($status !== Role::STATUS_DEFINED) {
  245.             $this->logger->logCall(__METHOD__, ['role' => $identifier]);
  246.             return $this->persistenceHandler->userHandler()->loadRoleByIdentifier($identifier$status);
  247.         }
  248.         return $this->getCacheValue(
  249.             $this->escapeForCacheKey($identifier),
  250.             'ez-role-',
  251.             function () use ($identifier) {
  252.                 return $this->persistenceHandler->userHandler()->loadRoleByIdentifier($identifier);
  253.             },
  254.             $this->getRoleTags,
  255.             $this->getRoleKeys,
  256.             '-by-identifier'
  257.         );
  258.     }
  259.     /**
  260.      * {@inheritdoc}
  261.      */
  262.     public function loadRoleDraftByRoleId($roleId)
  263.     {
  264.         $this->logger->logCall(__METHOD__, ['role' => $roleId]);
  265.         return $this->persistenceHandler->userHandler()->loadRoleDraftByRoleId($roleId);
  266.     }
  267.     /**
  268.      * {@inheritdoc}
  269.      */
  270.     public function loadRoles()
  271.     {
  272.         $this->logger->logCall(__METHOD__);
  273.         return $this->persistenceHandler->userHandler()->loadRoles();
  274.     }
  275.     /**
  276.      * {@inheritdoc}
  277.      */
  278.     public function loadRoleAssignment($roleAssignmentId)
  279.     {
  280.         return $this->getCacheValue(
  281.             $roleAssignmentId,
  282.             'ez-role-assignment-',
  283.             function ($roleAssignmentId) {
  284.                 return $this->persistenceHandler->userHandler()->loadRoleAssignment($roleAssignmentId);
  285.             },
  286.             $this->getRoleAssignmentTags,
  287.             $this->getRoleAssignmentKeys
  288.         );
  289.     }
  290.     /**
  291.      * {@inheritdoc}
  292.      */
  293.     public function loadRoleAssignmentsByRoleId($roleId)
  294.     {
  295.         return $this->getListCacheValue(
  296.             "ez-role-assignment-${roleId}-by-role",
  297.             function () use ($roleId) {
  298.                 return $this->persistenceHandler->userHandler()->loadRoleAssignmentsByRoleId($roleId);
  299.             },
  300.             $this->getRoleAssignmentTags,
  301.             $this->getRoleAssignmentKeys,
  302.             /* Role update (policies) changes role assignment id, also need list tag in case of empty result */
  303.             static function () use ($roleId) {
  304.                 return ['role-assignment-role-list-' $roleId'role-' $roleId];
  305.             },
  306.             [$roleId]
  307.         );
  308.     }
  309.     /**
  310.      * {@inheritdoc}
  311.      */
  312.     public function loadRoleAssignmentsByGroupId($groupId$inherit false)
  313.     {
  314.         $innerHandler $this->persistenceHandler;
  315.         if ($inherit) {
  316.             $key "ez-role-assignment-${groupId}-by-group-inherited";
  317.         } else {
  318.             $key "ez-role-assignment-${groupId}-by-group";
  319.         }
  320.         return $this->getListCacheValue(
  321.             $key,
  322.             function () use ($groupId$inherit) {
  323.                 return $this->persistenceHandler->userHandler()->loadRoleAssignmentsByGroupId($groupId$inherit);
  324.             },
  325.             $this->getRoleAssignmentTags,
  326.             $this->getRoleAssignmentKeys,
  327.             static function () use ($groupId$innerHandler) {
  328.                 // Tag needed for empty results, if not empty will alse be added by getRoleAssignmentTags().
  329.                 $cacheTags = ['role-assignment-group-list-' $groupId];
  330.                 // To make sure tree operations affecting this can clear the permission cache
  331.                 $locations $innerHandler->locationHandler()->loadLocationsByContent($groupId);
  332.                 foreach ($locations as $location) {
  333.                     foreach (explode('/'trim($location->pathString'/')) as $pathId) {
  334.                         $cacheTags[] = 'location-path-' $pathId;
  335.                     }
  336.                 }
  337.                 return $cacheTags;
  338.             },
  339.             [$groupId$inherit]
  340.         );
  341.     }
  342.     /**
  343.      * {@inheritdoc}
  344.      */
  345.     public function updateRole(RoleUpdateStruct $struct)
  346.     {
  347.         $this->logger->logCall(__METHOD__, ['struct' => $struct]);
  348.         $this->persistenceHandler->userHandler()->updateRole($struct);
  349.         $this->cache->invalidateTags(['role-' $struct->id]);
  350.     }
  351.     /**
  352.      * {@inheritdoc}
  353.      */
  354.     public function deleteRole($roleId$status Role::STATUS_DEFINED)
  355.     {
  356.         $this->logger->logCall(__METHOD__, ['role' => $roleId]);
  357.         $return $this->persistenceHandler->userHandler()->deleteRole($roleId$status);
  358.         if ($status === Role::STATUS_DEFINED) {
  359.             $this->cache->invalidateTags(['role-' $roleId'role-assignment-role-list-' $roleId]);
  360.         }
  361.         return $return;
  362.     }
  363.     /**
  364.      * {@inheritdoc}
  365.      */
  366.     public function publishRoleDraft($roleDraftId)
  367.     {
  368.         $this->logger->logCall(__METHOD__, ['role' => $roleDraftId]);
  369.         $userHandler $this->persistenceHandler->userHandler();
  370.         $roleDraft $userHandler->loadRole($roleDraftIdRole::STATUS_DRAFT);
  371.         $return $userHandler->publishRoleDraft($roleDraftId);
  372.         // If there was a original role for the draft, then we clean cache for it
  373.         if ($roleDraft->originalId > -1) {
  374.             $this->cache->invalidateTags(['role-' $roleDraft->originalId]);
  375.         }
  376.         return $return;
  377.     }
  378.     /**
  379.      * {@inheritdoc}
  380.      */
  381.     public function addPolicyByRoleDraft($roleIdPolicy $policy)
  382.     {
  383.         $this->logger->logCall(__METHOD__, ['role' => $roleId'struct' => $policy]);
  384.         return $this->persistenceHandler->userHandler()->addPolicyByRoleDraft($roleId$policy);
  385.     }
  386.     /**
  387.      * {@inheritdoc}
  388.      */
  389.     public function addPolicy($roleIdPolicy $policy)
  390.     {
  391.         $this->logger->logCall(__METHOD__, ['role' => $roleId'struct' => $policy]);
  392.         $return $this->persistenceHandler->userHandler()->addPolicy($roleId$policy);
  393.         $this->cache->invalidateTags(['role-' $roleId]);
  394.         return $return;
  395.     }
  396.     /**
  397.      * {@inheritdoc}
  398.      */
  399.     public function updatePolicy(Policy $policy)
  400.     {
  401.         $this->logger->logCall(__METHOD__, ['struct' => $policy]);
  402.         $return $this->persistenceHandler->userHandler()->updatePolicy($policy);
  403.         $this->cache->invalidateTags(['policy-' $policy->id'role-' $policy->roleId]);
  404.         return $return;
  405.     }
  406.     /**
  407.      * {@inheritdoc}
  408.      */
  409.     public function deletePolicy($policyId$roleId)
  410.     {
  411.         $this->logger->logCall(__METHOD__, ['policy' => $policyId]);
  412.         $this->persistenceHandler->userHandler()->deletePolicy($policyId$roleId);
  413.         $this->cache->invalidateTags(['policy-' $policyId'role-' $roleId]);
  414.     }
  415.     /**
  416.      * {@inheritdoc}
  417.      */
  418.     public function loadPoliciesByUserId($userId)
  419.     {
  420.         $this->logger->logCall(__METHOD__, ['user' => $userId]);
  421.         return $this->persistenceHandler->userHandler()->loadPoliciesByUserId($userId);
  422.     }
  423.     /**
  424.      * {@inheritdoc}
  425.      */
  426.     public function assignRole($contentId$roleId, array $limitation null)
  427.     {
  428.         $this->logger->logCall(__METHOD__, ['group' => $contentId'role' => $roleId'limitation' => $limitation]);
  429.         $return $this->persistenceHandler->userHandler()->assignRole($contentId$roleId$limitation);
  430.         $tags = ['role-assignment-group-list-' $contentId'role-assignment-role-list-' $roleId];
  431.         $locations $this->persistenceHandler->locationHandler()->loadLocationsByContent($contentId);
  432.         foreach ($locations as $location) {
  433.             $tags[] = 'location-path-' $location->id;
  434.         }
  435.         $this->cache->invalidateTags($tags);
  436.         return $return;
  437.     }
  438.     /**
  439.      * {@inheritdoc}
  440.      */
  441.     public function unassignRole($contentId$roleId)
  442.     {
  443.         $this->logger->logCall(__METHOD__, ['group' => $contentId'role' => $roleId]);
  444.         $return $this->persistenceHandler->userHandler()->unassignRole($contentId$roleId);
  445.         $this->cache->invalidateTags(['role-assignment-group-list-' $contentId'role-assignment-role-list-' $roleId]);
  446.         return $return;
  447.     }
  448.     /**
  449.      * {@inheritdoc}
  450.      */
  451.     public function removeRoleAssignment($roleAssignmentId)
  452.     {
  453.         $this->logger->logCall(__METHOD__, ['assignment' => $roleAssignmentId]);
  454.         $return $this->persistenceHandler->userHandler()->removeRoleAssignment($roleAssignmentId);
  455.         $this->cache->invalidateTags(['role-assignment-' $roleAssignmentId]);
  456.         return $return;
  457.     }
  458. }