diff --git a/.gitattributes b/.gitattributes index ebb92870..84c7add0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ /Tests export-ignore /phpunit.xml.dist export-ignore +/.gitattributes export-ignore /.gitignore export-ignore diff --git a/Firewall/AccessListener.php b/Firewall/AccessListener.php index 00673f60..b2944568 100644 --- a/Firewall/AccessListener.php +++ b/Firewall/AccessListener.php @@ -87,15 +87,7 @@ public function authenticate(RequestEvent $event) $this->tokenStorage->setToken($token); } - $granted = false; - foreach ($attributes as $key => $value) { - if ($this->accessDecisionManager->decide($token, [$key => $value], $request)) { - $granted = true; - break; - } - } - - if (!$granted) { + if (!$this->accessDecisionManager->decide($token, $attributes, $request, true)) { $exception = new AccessDeniedException(); $exception->setAttributes($attributes); $exception->setSubject($request); diff --git a/Logout/CookieClearingLogoutHandler.php b/Logout/CookieClearingLogoutHandler.php index 2aa7c732..9367a62b 100644 --- a/Logout/CookieClearingLogoutHandler.php +++ b/Logout/CookieClearingLogoutHandler.php @@ -38,7 +38,7 @@ public function __construct(array $cookies) public function logout(Request $request, Response $response, TokenInterface $token) { foreach ($this->cookies as $cookieName => $cookieData) { - $response->headers->clearCookie($cookieName, $cookieData['path'], $cookieData['domain']); + $response->headers->clearCookie($cookieName, $cookieData['path'], $cookieData['domain'], isset($cookieData['secure']) ? $cookieData['secure'] : false, true, isset($cookieData['samesite']) ? $cookieData['samesite'] : null); } } } diff --git a/Tests/Firewall/AccessListenerTest.php b/Tests/Firewall/AccessListenerTest.php index 168e2564..75798d05 100644 --- a/Tests/Firewall/AccessListenerTest.php +++ b/Tests/Firewall/AccessListenerTest.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Http\AccessMapInterface; @@ -227,4 +228,44 @@ public function testHandleWhenTheSecurityTokenStorageHasNoToken() $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST)); } + + public function testHandleMWithultipleAttributesShouldBeHandledAsAnd() + { + $request = new Request(); + + $accessMap = $this->getMockBuilder('Symfony\Component\Security\Http\AccessMapInterface')->getMock(); + $accessMap + ->expects($this->any()) + ->method('getPatterns') + ->with($this->equalTo($request)) + ->willReturn([['foo' => 'bar', 'bar' => 'baz'], null]) + ; + + $authenticatedToken = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + $authenticatedToken + ->expects($this->any()) + ->method('isAuthenticated') + ->willReturn(true) + ; + + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($authenticatedToken); + + $accessDecisionManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface')->getMock(); + $accessDecisionManager + ->expects($this->once()) + ->method('decide') + ->with($this->equalTo($authenticatedToken), $this->equalTo(['foo' => 'bar', 'bar' => 'baz']), $this->equalTo($request), true) + ->willReturn(true) + ; + + $listener = new AccessListener( + $tokenStorage, + $accessDecisionManager, + $accessMap, + $this->createMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface') + ); + + $listener(new RequestEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST)); + } } diff --git a/Tests/Firewall/SwitchUserListenerTest.php b/Tests/Firewall/SwitchUserListenerTest.php index 62d0da69..c188727b 100644 --- a/Tests/Firewall/SwitchUserListenerTest.php +++ b/Tests/Firewall/SwitchUserListenerTest.php @@ -372,7 +372,7 @@ public function testSwitchUserWithReplacedToken() $this->assertSame($replacedToken, $this->tokenStorage->getToken()); } - public function testSwitchtUserThrowsAuthenticationExceptionIfNoCurrentToken() + public function testSwitchUserThrowsAuthenticationExceptionIfNoCurrentToken() { $this->expectException('Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException'); $this->tokenStorage->setToken(null); diff --git a/Tests/Logout/CookieClearingLogoutHandlerTest.php b/Tests/Logout/CookieClearingLogoutHandlerTest.php index 8dcc1033..f2407fcb 100644 --- a/Tests/Logout/CookieClearingLogoutHandlerTest.php +++ b/Tests/Logout/CookieClearingLogoutHandlerTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Http\Tests\Logout; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\ResponseHeaderBag; @@ -25,7 +26,7 @@ public function testLogout() $response = new Response(); $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); - $handler = new CookieClearingLogoutHandler(['foo' => ['path' => '/foo', 'domain' => 'foo.foo'], 'foo2' => ['path' => null, 'domain' => null]]); + $handler = new CookieClearingLogoutHandler(['foo' => ['path' => '/foo', 'domain' => 'foo.foo', 'secure' => true, 'samesite' => Cookie::SAMESITE_STRICT], 'foo2' => ['path' => null, 'domain' => null]]); $cookies = $response->headers->getCookies(); $this->assertCount(0, $cookies); @@ -39,12 +40,16 @@ public function testLogout() $this->assertEquals('foo', $cookie->getName()); $this->assertEquals('/foo', $cookie->getPath()); $this->assertEquals('foo.foo', $cookie->getDomain()); + $this->assertEquals(Cookie::SAMESITE_STRICT, $cookie->getSameSite()); + $this->assertTrue($cookie->isSecure()); $this->assertTrue($cookie->isCleared()); $cookie = $cookies['']['/']['foo2']; $this->assertStringStartsWith('foo2', $cookie->getName()); $this->assertEquals('/', $cookie->getPath()); $this->assertNull($cookie->getDomain()); + $this->assertNull($cookie->getSameSite()); + $this->assertFalse($cookie->isSecure()); $this->assertTrue($cookie->isCleared()); } } diff --git a/composer.json b/composer.json index 686b2b9f..699ffcf7 100644 --- a/composer.json +++ b/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": "^7.1.3", - "symfony/security-core": "^4.4", - "symfony/http-foundation": "^3.4|^4.0|^5.0", + "symfony/security-core": "^4.4.7", + "symfony/http-foundation": "^3.4.40|^4.4.7|^5.0.7", "symfony/http-kernel": "^4.4", "symfony/property-access": "^3.4|^4.0|^5.0" },