diff --git a/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php b/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php index 8ffc2bfbbd..b66014d849 100644 --- a/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php @@ -33,5 +33,7 @@ class UriSignerTest extends \PHPUnit_Framework_TestCase $this->assertTrue($signer->check($signer->sign('http://example.com/foo'))); $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar'))); + + $this->assertTrue($signer->sign('http://example.com/foo?foo=bar&bar=foo') === $signer->sign('http://example.com/foo?bar=foo&foo=bar')); } } diff --git a/src/Symfony/Component/HttpKernel/UriSigner.php b/src/Symfony/Component/HttpKernel/UriSigner.php index 7b7d452a13..c97c030d5f 100644 --- a/src/Symfony/Component/HttpKernel/UriSigner.php +++ b/src/Symfony/Component/HttpKernel/UriSigner.php @@ -42,6 +42,15 @@ class UriSigner */ public function sign($uri) { + $url = parse_url($uri); + if (isset($url['query'])) { + parse_str($url['query'], $params); + } else { + $params = array(); + } + + $uri = $this->buildUrl($url, $params); + return $uri.(false === (strpos($uri, '?')) ? '?' : '&').'_hash='.$this->computeHash($uri); } @@ -58,15 +67,43 @@ class UriSigner */ public function check($uri) { - if (!preg_match('/^(.*)(?:\?|&)_hash=(.+?)$/', $uri, $matches)) { + $url = parse_url($uri); + if (isset($url['query'])) { + parse_str($url['query'], $params); + } else { + $params = array(); + } + + if (empty($params['_hash'])) { return false; } - return $this->computeHash($matches[1]) === $matches[2]; + $hash = urlencode($params['_hash']); + unset($params['_hash']); + + return $this->computeHash($this->buildUrl($url, $params)) === $hash; } private function computeHash($uri) { return urlencode(base64_encode(hash_hmac('sha256', $uri, $this->secret, true))); } + + private function buildUrl(array $url, array $params = array()) + { + ksort($params); + $url['query'] = http_build_query($params); + + $scheme = isset($url['scheme']) ? $url['scheme'].'://' : ''; + $host = isset($url['host']) ? $url['host'] : ''; + $port = isset($url['port']) ? ':'.$url['port'] : ''; + $user = isset($url['user']) ? $url['user'] : ''; + $pass = isset($url['pass']) ? ':'.$url['pass'] : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = isset($url['path']) ? $url['path'] : ''; + $query = isset($url['query']) && $url['query'] ? '?'.$url['query'] : ''; + $fragment = isset($url['fragment']) ? '#'.$url['fragment'] : ''; + + return $scheme.$user.$pass.$host.$port.$path.$query.$fragment; + } } diff --git a/src/Symfony/Component/Security/Http/RememberMe/ResponseListener.php b/src/Symfony/Component/Security/Http/RememberMe/ResponseListener.php index 2253c5d163..ec5f00616f 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/ResponseListener.php +++ b/src/Symfony/Component/Security/Http/RememberMe/ResponseListener.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http\RememberMe; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -27,6 +28,10 @@ class ResponseListener implements EventSubscriberInterface */ public function onKernelResponse(FilterResponseEvent $event) { + if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + return; + } + $request = $event->getRequest(); $response = $event->getResponse(); diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php index 9bb84a2cfe..3dcca702f3 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Http\Tests\RememberMe; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Http\RememberMe\ResponseListener; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Component\HttpFoundation\Request; @@ -34,6 +35,21 @@ class ResponseListenerTest extends \PHPUnit_Framework_TestCase $listener->onKernelResponse($this->getEvent($request, $response)); } + public function testRememberMeCookieIsNotSendWithResponseForSubRequests() + { + $cookie = new Cookie('rememberme'); + + $request = $this->getRequest(array( + RememberMeServicesInterface::COOKIE_ATTR_NAME => $cookie, + )); + + $response = $this->getResponse(); + $response->headers->expects($this->never())->method('setCookie'); + + $listener = new ResponseListener(); + $listener->onKernelResponse($this->getEvent($request, $response, HttpKernelInterface::SUB_REQUEST)); + } + public function testRememberMeCookieIsNotSendWithResponse() { $request = $this->getRequest(); @@ -71,13 +87,14 @@ class ResponseListenerTest extends \PHPUnit_Framework_TestCase return $response; } - private function getEvent($request, $response) + private function getEvent($request, $response, $type = HttpKernelInterface::MASTER_REQUEST) { $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\FilterResponseEvent') ->disableOriginalConstructor() ->getMock(); $event->expects($this->any())->method('getRequest')->will($this->returnValue($request)); + $event->expects($this->any())->method('getRequestType')->will($this->returnValue($type)); $event->expects($this->any())->method('getResponse')->will($this->returnValue($response)); return $event;