From 730fb7af0238bb46b4ca30687e5aa5a08fffaed5 Mon Sep 17 00:00:00 2001 From: Gunnstein Lye Date: Thu, 16 Nov 2017 13:42:17 +0100 Subject: [PATCH] EZP-27996: "Remember me" doesn't retrieve the current user from Repository (#2106) * EZP-27996: "Remember me" doesn't retrieve the current user from Repository * fixup! EZP-27996: "Remember me" doesn't retrieve the current user from Repository * Throw rather than return null * fixup! Throw rather than return null * fixup! fixup! Throw rather than return null --- .../Compiler/SecurityPass.php | 10 +- .../Resources/config/security.yml | 1 + .../Compiler/SecurityPassTest.php | 6 + ...mberMeRepositoryAuthenticationProvider.php | 44 +++++ ...MeRepositoryAuthenticationProviderTest.php | 165 ++++++++++++++++++ 5 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 eZ/Publish/Core/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php create mode 100644 eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RememberMeRepositoryAuthenticationProviderTest.php diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SecurityPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SecurityPass.php index 0277af6d21b..b97a8d307e8 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SecurityPass.php +++ b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SecurityPass.php @@ -20,7 +20,9 @@ class SecurityPass implements CompilerPassInterface { public function process(ContainerBuilder $container) { - if (!($container->hasDefinition('security.authentication.provider.dao') && $container->hasDefinition('security.authentication.provider.anonymous'))) { + if (!($container->hasDefinition('security.authentication.provider.dao') && + $container->hasDefinition('security.authentication.provider.rememberme') && + $container->hasDefinition('security.authentication.provider.anonymous'))) { return; } @@ -34,6 +36,12 @@ public function process(ContainerBuilder $container) array($repositoryReference) ); + $rememberMeAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.rememberme'); + $rememberMeAuthenticationProviderDef->addMethodCall( + 'setRepository', + array($repositoryReference) + ); + $anonymousAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.anonymous'); $anonymousAuthenticationProviderDef->addMethodCall( 'setRepository', diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml index a2094332a73..b8846942189 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml +++ b/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml @@ -1,5 +1,6 @@ parameters: security.authentication.provider.dao.class: eZ\Publish\Core\MVC\Symfony\Security\Authentication\RepositoryAuthenticationProvider + security.authentication.provider.rememberme.class: eZ\Publish\Core\MVC\Symfony\Security\Authentication\RememberMeRepositoryAuthenticationProvider security.authentication.provider.anonymous.class: eZ\Publish\Core\MVC\Symfony\Security\Authentication\AnonymousAuthenticationProvider security.authentication.success_handler.class: eZ\Publish\Core\MVC\Symfony\Security\Authentication\DefaultAuthenticationSuccessHandler ezpublish.security.user_provider.class: eZ\Publish\Core\MVC\Symfony\Security\User\Provider diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SecurityPassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SecurityPassTest.php index 67e4eb4eace..66a364f80ad 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SecurityPassTest.php +++ b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SecurityPassTest.php @@ -20,6 +20,7 @@ protected function setUp() { parent::setUp(); $this->setDefinition('security.authentication.provider.dao', new Definition()); + $this->setDefinition('security.authentication.provider.rememberme', new Definition()); $this->setDefinition('security.authentication.provider.anonymous', new Definition()); $this->setDefinition('security.http_utils', new Definition()); $this->setDefinition('security.authentication.success_handler', new Definition()); @@ -38,6 +39,11 @@ public function testAlteredDaoAuthenticationProvider() 'setRepository', array(new Reference('ezpublish.api.repository')) ); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.authentication.provider.rememberme', + 'setRepository', + array(new Reference('ezpublish.api.repository')) + ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'security.authentication.provider.anonymous', 'setRepository', diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php b/eZ/Publish/Core/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php new file mode 100644 index 00000000000..c95c87d5a99 --- /dev/null +++ b/eZ/Publish/Core/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php @@ -0,0 +1,44 @@ +repository = $repository; + } + + /** + * {@inheritdoc} + */ + public function authenticate(TokenInterface $token) + { + $authenticatedToken = parent::authenticate($token); + if (empty($authenticatedToken)) { + throw new AuthenticationException('The token is not supported by this authentication provider.'); + } + + $this->repository->getPermissionResolver()->setCurrentUserReference( + $authenticatedToken->getUser()->getAPIUser() + ); + + return $authenticatedToken; + } +} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RememberMeRepositoryAuthenticationProviderTest.php b/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RememberMeRepositoryAuthenticationProviderTest.php new file mode 100644 index 00000000000..3f1ec1c8ed3 --- /dev/null +++ b/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RememberMeRepositoryAuthenticationProviderTest.php @@ -0,0 +1,165 @@ +repository = $this->getMock(Repository::class); + $this->authProvider = new RememberMeRepositoryAuthenticationProvider( + $this->getMock(UserCheckerInterface::class), + 'my secret', + 'my provider secret' + ); + $this->authProvider->setRepository($this->repository); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage The token is not supported by this authentication provider. + */ + public function testAuthenticateUnsupportedToken() + { + $anonymousToken = $this + ->getMockBuilder(AnonymousToken::class) + ->setConstructorArgs(['secret', $this->getMock(UserInterface::class)]) + ->getMock(); + $this->authProvider->authenticate($anonymousToken); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage The token is not supported by this authentication provider. + */ + public function testAuthenticateWrongProviderKey() + { + $user = $this->getMock(UserInterface::class); + $user + ->expects($this->any()) + ->method('getRoles') + ->will($this->returnValue([])); + + $rememberMeToken = $this + ->getMockBuilder(RememberMeToken::class) + ->setConstructorArgs([$user, 'wrong provider secret', 'my secret']) + ->getMock(); + $rememberMeToken + ->expects($this->any()) + ->method('getProviderKey') + ->will($this->returnValue('wrong provider secret')); + + $this->authProvider->authenticate($rememberMeToken); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testAuthenticateWrongSecret() + { + $user = $this->getMock(UserInterface::class); + $user + ->expects($this->any()) + ->method('getRoles') + ->will($this->returnValue([])); + + $rememberMeToken = $this + ->getMockBuilder(RememberMeToken::class) + ->setConstructorArgs([$user, 'my provider secret', 'the wrong secret']) + ->getMock(); + $rememberMeToken + ->expects($this->any()) + ->method('getProviderKey') + ->will($this->returnValue('my provider secret')); + $rememberMeToken + ->expects($this->any()) + ->method('getSecret') + ->will($this->returnValue('the wrong secret')); + + $this->authProvider->authenticate($rememberMeToken); + } + + public function testAuthenticate() + { + $this->repository + ->expects($this->once()) + ->method('getPermissionResolver') + ->will($this->returnValue($this->getPermissionResolverMock())); + + $apiUser = $this->getMock(ApiUser::class); + $apiUser + ->expects($this->any()) + ->method('getUserId') + ->will($this->returnValue(42)); + + $tokenUser = new User($apiUser); + $rememberMeToken = new RememberMeToken($tokenUser, 'my provider secret', 'my secret'); + + $authenticatedToken = $this->authProvider->authenticate($rememberMeToken); + $this->assertEquals( + [$rememberMeToken->getProviderKey(), $rememberMeToken->getSecret(), $rememberMeToken->getUsername()], + [$authenticatedToken->getProviderKey(), $authenticatedToken->getSecret(), $authenticatedToken->getUsername()] + ); + } + + /** + * @return \eZ\Publish\Core\Repository\Permission\PermissionResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private function getPermissionResolverMock() + { + return $this + ->getMockBuilder(PermissionResolver::class) + ->setMethods(null) + ->setConstructorArgs( + [ + $this + ->getMockBuilder(RoleDomainMapper::class) + ->disableOriginalConstructor() + ->getMock(), + $this + ->getMockBuilder(LimitationService::class) + ->getMock(), + $this + ->getMockBuilder(UserHandler::class) + ->getMock(), + $this + ->getMockBuilder(UserReference::class) + ->getMock(), + ] + ) + ->getMock(); + } +}