diff --git a/spec/DependencyInjection/Compiler/KernelPassSpec.php b/spec/DependencyInjection/Compiler/KernelPassSpec.php index fdfc2677..2108ec6b 100644 --- a/spec/DependencyInjection/Compiler/KernelPassSpec.php +++ b/spec/DependencyInjection/Compiler/KernelPassSpec.php @@ -6,6 +6,7 @@ use PhpSpec\ObjectBehavior; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; class KernelPassSpec extends ObjectBehavior { @@ -14,9 +15,10 @@ function it_is_initializable() $this->shouldHaveType(KernelPass::class); } - function it_disables_the_kernels_httpcache_services(ContainerBuilder $container, Definition $cacheClearer) + function it_disables_the_kernels_httpcache_services(ContainerBuilder $container, Definition $cacheClearer, Definition $hashGenerator) { $container->getAlias('ezpublish.http_cache.purge_client')->willReturn('some_random_id'); + $container->hasAlias('ezpublish.http_cache.purger')->willReturn(true); $container->getAlias('ezpublish.http_cache.purger')->willReturn('some_random_id'); $container->getDefinitions()->willReturn([ 'ezpublish.http_cache.witness_service' => new Definition(), @@ -52,6 +54,23 @@ function it_disables_the_kernels_httpcache_services(ContainerBuilder $container, ] ])->shouldBeCalled(); + $container->hasDefinition('ezpublish.user.identity_definer.role_id')->willReturn(true); + $container->removeDefinition('ezpublish.user.identity_definer.role_id')->willReturn(true); + $container->getDefinition('fos_http_cache.user_context.hash_generator')->willReturn($hashGenerator); + $hashGenerator->getArguments()->willReturn([ + [ + $ref1 = new Reference('ezplatform.http_cache.user_context_provider.role_identify'), + $ref2 = new Reference('ezpublish.user.hash_generator'), + new Reference('ezpublish.user.identity_definer.role_id'), + ] + ]); + $hashGenerator->setArguments([ + [ + $ref1, + $ref2, + ] + ])->shouldBeCalled(); + $container->getParameter('ezpublish.http_cache.purge_type')->shouldBeCalled(); $container->setParameter('ezplatform.http_cache.purge_type', null)->shouldBeCalled(); diff --git a/src/ContextProvider/RoleIdentify.php b/src/ContextProvider/RoleIdentify.php new file mode 100644 index 00000000..ff409c18 --- /dev/null +++ b/src/ContextProvider/RoleIdentify.php @@ -0,0 +1,65 @@ +repository = $repository; + } + + public function updateUserContext(UserContext $context) + { + $user = $this->repository->getCurrentUser(); + /** @var \eZ\Publish\API\Repository\Values\User\RoleAssignment[] $roleAssignments */ + $roleAssignments = $this->repository->sudo( + function (Repository $repository) use ($user) { + return $repository->getRoleService()->getRoleAssignmentsForUser($user, true); + } + ); + + $roleIds = array(); + $limitationValues = array(); + /** @var \eZ\Publish\API\Repository\Values\User\UserRoleAssignment $roleAssignment */ + foreach ($roleAssignments as $roleAssignment) { + $roleId = $roleAssignment->getRole()->id; + $roleIds[] = $roleId; + $limitation = $roleAssignment->getRoleLimitation(); + // If a limitation is present, store the limitation values by roleId + if ($limitation !== null) { + $limitationValuesKey = sprintf('%s-%s', $roleId, $limitation->getIdentifier()); + $limitationValues[$limitationValuesKey] = array(); + foreach ($limitation->limitationValues as $value) { + $limitationValues[$limitationValuesKey][] = $value; + } + } + } + + $context->addParameter('roleIdList', $roleIds); + $context->addParameter('roleLimitationList', $limitationValues); + } +} diff --git a/src/DependencyInjection/Compiler/KernelPass.php b/src/DependencyInjection/Compiler/KernelPass.php index 89383e3f..52d48ffa 100644 --- a/src/DependencyInjection/Compiler/KernelPass.php +++ b/src/DependencyInjection/Compiler/KernelPass.php @@ -7,6 +7,7 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; /** * Disables some of the http-cache services declared by the kernel so that @@ -25,8 +26,13 @@ public function process(ContainerBuilder $container) $container->removeDefinition($id); } } - $container->removeAlias('ezpublish.http_cache.purger'); + + if ($container->hasAlias('ezpublish.http_cache.purger')) { + $container->removeAlias('ezpublish.http_cache.purger'); + } + $this->symfonyPre34BC($container); + $this->removeKernelRoleIdContextProvider($container); // Let's re-export purge_type setting so that driver's don't have to depend on kernel in order to acquire it $container->setParameter('ezplatform.http_cache.purge_type', $container->getParameter('ezpublish.http_cache.purge_type')); @@ -54,6 +60,31 @@ protected function symfonyPre34BC(ContainerBuilder $container) $container->getDefinition('cache_clearer')->setArguments($arguments); } + /** + * @param ContainerBuilder $container + */ + protected function removeKernelRoleIdContextProvider(ContainerBuilder $container) + { + if (!$container->hasDefinition('ezpublish.user.identity_definer.role_id')) { + return; + } + + // As we set role identify ourselves here we remove varant from kernel if it is there. + // We don't touch ezpublish.user.hash_generator, as it's deprecated extension point by kernel + $container->removeDefinition('ezpublish.user.identity_definer.role_id'); + + // Also remove from arguments already passed to FOSHttpCache via compiler pass there. + $arguments = $container->getDefinition('fos_http_cache.user_context.hash_generator')->getArguments(); + $arguments[0] = array_values(array_filter($arguments[0], function (Reference $argument) { + if ((string)$argument === 'ezpublish.user.identity_definer.role_id') { + return false; + } + + return true; + })); + $container->getDefinition('fos_http_cache.user_context.hash_generator')->setArguments($arguments); + } + /** * @param string $id * diff --git a/src/PurgeClient/PurgeClientInterface.php b/src/PurgeClient/PurgeClientInterface.php index 20605951..f8411bd4 100644 --- a/src/PurgeClient/PurgeClientInterface.php +++ b/src/PurgeClient/PurgeClientInterface.php @@ -1,19 +1,15 @@ httpCacheClearer = $httpCacheClearer; - } - - public function receive(Signal $signal) - { - if (!$this->supports($signal)) { - return; - } - - $this->purgeHttpCache($signal); - } - - /** - * Checks if $signal is supported by this handler. - * - * @param \eZ\Publish\Core\SignalSlot\Signal $signal - * - * @return bool - */ - abstract protected function supports(Signal $signal); - - /** - * Purges the HTTP cache for $signal. - * - * @param \eZ\Publish\Core\SignalSlot\Signal $signal - * - * @return mixed - */ - abstract protected function purgeHttpCache(Signal $signal); -} diff --git a/src/SignalSlot/PublishVersionSlot.php b/src/SignalSlot/PublishVersionSlot.php index 448fea72..c3b632b5 100644 --- a/src/SignalSlot/PublishVersionSlot.php +++ b/src/SignalSlot/PublishVersionSlot.php @@ -8,7 +8,7 @@ */ namespace EzSystems\PlatformHttpCacheBundle\SignalSlot; -use eZ\Publish\Core\MVC\Symfony\Cache\PurgeClientInterface; +use EzSystems\PlatformHttpCacheBundle\PurgeClient\PurgeClientInterface; use eZ\Publish\Core\SignalSlot\Signal; use eZ\Publish\SPI\Persistence\Content\Location\Handler; @@ -23,7 +23,7 @@ class PublishVersionSlot extends AbstractContentSlot private $locationHandler; /** - * @param \eZ\Publish\Core\MVC\Symfony\Cache\PurgeClientInterface $purgeClient + * @param \EzSystems\PlatformHttpCacheBundle\PurgeClient\PurgeClientInterface $purgeClient * @param \eZ\Publish\SPI\Persistence\Content\Location\Handler $spiLocationHandler */ public function __construct(PurgeClientInterface $purgeClient, Handler $spiLocationHandler) diff --git a/src/SignalSlot/PurgeAllHttpCacheSlot.php b/src/SignalSlot/PurgeAllHttpCacheSlot.php deleted file mode 100644 index 3f75a07d..00000000 --- a/src/SignalSlot/PurgeAllHttpCacheSlot.php +++ /dev/null @@ -1,32 +0,0 @@ -httpCacheClearer->purgeAll(); - } -} diff --git a/src/SignalSlot/PurgeForContentHttpCacheSlot.php b/src/SignalSlot/PurgeForContentHttpCacheSlot.php deleted file mode 100644 index 9e11f6de..00000000 --- a/src/SignalSlot/PurgeForContentHttpCacheSlot.php +++ /dev/null @@ -1,67 +0,0 @@ -httpCacheClearer->purgeForContent($this->extractContentId($signal), $this->extractLocationIds($signal)); - } - - /** - * Default implementation that returns the contentId property's value. - * - * @param \eZ\Publish\Core\SignalSlot\Signal $signal - * - * @return mixed Content ID - */ - protected function extractContentId(Signal $signal) - { - return $signal->contentId; - } - - /** - * Default implementation that returns the signal location property values. - * - * This is extracted and provided to purgeForContent in case content is trashed where affected location is no longer returned by API. - * - * @param \eZ\Publish\Core\SignalSlot\Signal $signal - * - * @return array Location ID's - */ - protected function extractLocationIds(Signal $signal) - { - $locationIds = []; - if (isset($signal->locationId)) { - $locationIds[] = $signal->locationId; - } - - if (isset($signal->parentLocationId)) { - $locationIds[] = $signal->parentLocationId; - } - - return $locationIds; - } -} diff --git a/tests/ContextProvider/RoleIdentifyTest.php b/tests/ContextProvider/RoleIdentifyTest.php new file mode 100644 index 00000000..57e2a9d5 --- /dev/null +++ b/tests/ContextProvider/RoleIdentifyTest.php @@ -0,0 +1,184 @@ +repositoryMock = $this + ->getMockBuilder(Repository::class) + ->disableOriginalConstructor() + ->setMethods(array('getRoleService', 'getCurrentUser', 'getPermissionResolver')) + ->getMock(); + + $this->roleServiceMock = $this->createMock(RoleService::class); + + $this->repositoryMock + ->expects($this->any()) + ->method('getRoleService') + ->will($this->returnValue($this->roleServiceMock)); + $this->repositoryMock + ->expects($this->any()) + ->method('getPermissionResolver') + ->will($this->returnValue($this->getPermissionResolverMock())); + } + + public function testSetIdentity() + { + $user = $this->createMock(APIUser::class); + $userContext = new UserContext(); + + $this->repositoryMock + ->expects($this->once()) + ->method('getCurrentUser') + ->will($this->returnValue($user)); + + $roleId1 = 123; + $roleId2 = 456; + $roleId3 = 789; + $limitationForRole2 = $this->generateLimitationMock( + array( + 'limitationValues' => array('/1/2', '/1/2/43'), + ) + ); + $limitationForRole3 = $this->generateLimitationMock( + array( + 'limitationValues' => array('foo', 'bar'), + ) + ); + $returnedRoleAssignments = array( + $this->generateRoleAssignmentMock( + array( + 'role' => $this->generateRoleMock( + array( + 'id' => $roleId1, + ) + ), + ) + ), + $this->generateRoleAssignmentMock( + array( + 'role' => $this->generateRoleMock( + array( + 'id' => $roleId2, + ) + ), + 'limitation' => $limitationForRole2, + ) + ), + $this->generateRoleAssignmentMock( + array( + 'role' => $this->generateRoleMock( + array( + 'id' => $roleId3, + ) + ), + 'limitation' => $limitationForRole3, + ) + ), + ); + + $this->roleServiceMock + ->expects($this->once()) + ->method('getRoleAssignmentsForUser') + ->with($user, true) + ->will($this->returnValue($returnedRoleAssignments)); + + $this->assertSame(array(), $userContext->getParameters()); + $contextProvider = new RoleIdentify($this->repositoryMock); + $contextProvider->updateUserContext($userContext); + $userContextParams = $userContext->getParameters(); + $this->assertArrayHasKey('roleIdList', $userContextParams); + $this->assertSame(array($roleId1, $roleId2, $roleId3), $userContextParams['roleIdList']); + $this->assertArrayHasKey('roleLimitationList', $userContextParams); + $limitationIdentifierForRole2 = get_class($limitationForRole2); + $limitationIdentifierForRole3 = get_class($limitationForRole3); + $this->assertSame( + array( + "$roleId2-$limitationIdentifierForRole2" => array('/1/2', '/1/2/43'), + "$roleId3-$limitationIdentifierForRole3" => array('foo', 'bar'), + ), + $userContextParams['roleLimitationList'] + ); + } + + private function generateRoleAssignmentMock(array $properties = array()) + { + return $this + ->getMockBuilder(UserRoleAssignment::class) + ->setConstructorArgs(array($properties)) + ->getMockForAbstractClass(); + } + + private function generateRoleMock(array $properties = array()) + { + return $this + ->getMockBuilder(Role::class) + ->setConstructorArgs(array($properties)) + ->getMockForAbstractClass(); + } + + private function generateLimitationMock(array $properties = array()) + { + $limitationMock = $this + ->getMockBuilder(RoleLimitation::class) + ->setConstructorArgs(array($properties)) + ->getMockForAbstractClass(); + $limitationMock + ->expects($this->any()) + ->method('getIdentifier') + ->will($this->returnValue(get_class($limitationMock))); + + return $limitationMock; + } + + protected function getPermissionResolverMock() + { + return $this + ->getMockBuilder(PermissionResolver::class) + ->setMethods(null) + ->setConstructorArgs( + [ + $this->createMock(RoleDomainMapper::class), + $this->createMock(LimitationService::class), + $this->createMock(SPIUserHandler::class), + $this->createMock(UserReference::class), + ] + ) + ->getMock(); + } +}