diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php
index b963efe801..513d4de3f5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php
@@ -57,15 +57,15 @@ class RedirectController extends ContainerAware
* In case the path is empty, the status code will be 404 when permanent is false
* and 410 otherwise.
*
- * @param string $path The absolute path or URL to redirect to
- * @param Boolean $permanent Whether the redirection is permanent
- * @param Boolean $scheme The URL scheme (null to keep the current one)
- * @param integer $httpPort The HTTP port
- * @param integer $httpsPort The HTTPS port
+ * @param string $path The absolute path or URL to redirect to
+ * @param Boolean $permanent Whether the redirect is permanent or not
+ * @param string|null $scheme The URL scheme (null to keep the current one)
+ * @param integer|null $httpPort The HTTP port (null to keep the current one for the same scheme or the configured port in the container)
+ * @param integer|null $httpsPort The HTTPS port (null to keep the current one for the same scheme or the configured port in the container)
*
* @return Response A Response instance
*/
- public function urlRedirectAction($path, $permanent = false, $scheme = null, $httpPort = 80, $httpsPort = 443)
+ public function urlRedirectAction($path, $permanent = false, $scheme = null, $httpPort = null, $httpsPort = null)
{
if ('' == $path) {
return new Response(null, $permanent ? 410 : 404);
@@ -89,10 +89,30 @@ class RedirectController extends ContainerAware
}
$port = '';
- if ('http' === $scheme && 80 != $httpPort) {
- $port = ':'.$httpPort;
- } elseif ('https' === $scheme && 443 != $httpsPort) {
- $port = ':'.$httpsPort;
+ if ('http' === $scheme) {
+ if (null === $httpPort) {
+ if ('http' === $request->getScheme()) {
+ $httpPort = $request->getPort();
+ } elseif ($this->container->hasParameter('request_listener.http_port')) {
+ $httpPort = $this->container->getParameter('request_listener.http_port');
+ }
+ }
+
+ if (null !== $httpPort && 80 != $httpPort) {
+ $port = ":$httpPort";
+ }
+ } elseif ('https' === $scheme) {
+ if (null === $httpsPort) {
+ if ('https' === $request->getScheme()) {
+ $httpsPort = $request->getPort();
+ } elseif ($this->container->hasParameter('request_listener.https_port')) {
+ $httpsPort = $this->container->getParameter('request_listener.https_port');
+ }
+ }
+
+ if (null !== $httpsPort && 443 != $httpsPort) {
+ $port = ":$httpsPort";
+ }
}
$url = $scheme.'://'.$request->getHost().$port.$request->getBaseUrl().$path.$qs;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php
index 04763e069f..171116585b 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php
@@ -87,9 +87,7 @@ class RedirectControllerTest extends TestCase
$returnResponse = $controller->redirectAction($route, $permanent);
- $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $returnResponse);
-
- $this->assertTrue($returnResponse->isRedirect($url));
+ $this->assertRedirectUrl($returnResponse, $url);
$this->assertEquals($expectedCode, $returnResponse->getStatusCode());
}
@@ -119,9 +117,143 @@ class RedirectControllerTest extends TestCase
$controller = new RedirectController();
$returnResponse = $controller->urlRedirectAction('http://foo.bar/');
- $this->assertInstanceOf('\Symfony\Component\HttpFoundation\Response', $returnResponse);
-
- $this->assertEquals('http://foo.bar/', $returnResponse->headers->get('Location'));
+ $this->assertRedirectUrl($returnResponse, 'http://foo.bar/');
$this->assertEquals(302, $returnResponse->getStatusCode());
}
+
+ public function testUrlRedirectDefaultPortParameters()
+ {
+ $host = 'www.example.com';
+ $baseUrl = '/base';
+ $path = '/redirect-path';
+ $httpPort = 1080;
+ $httpsPort = 1443;
+
+ $expectedUrl = "https://$host:$httpsPort$baseUrl$path";
+ $request = $this->createRequestObject('http', $host, $httpPort, $baseUrl);
+ $controller = $this->createRedirectController($request, null, $httpsPort);
+ $returnValue = $controller->urlRedirectAction($path, false, 'https');
+ $this->assertRedirectUrl($returnValue, $expectedUrl);
+
+ $expectedUrl = "http://$host:$httpPort$baseUrl$path";
+ $request = $this->createRequestObject('https', $host, $httpPort, $baseUrl);
+ $controller = $this->createRedirectController($request, $httpPort);
+ $returnValue = $controller->urlRedirectAction($path, false, 'http');
+ $this->assertRedirectUrl($returnValue, $expectedUrl);
+ }
+
+ public function urlRedirectProvider()
+ {
+ return array(
+ // Standard ports
+ array('http', null, null, 'http', 80, ""),
+ array('http', 80, null, 'http', 80, ""),
+ array('https', null, null, 'http', 80, ""),
+ array('https', 80, null, 'http', 80, ""),
+
+ array('http', null, null, 'https', 443, ""),
+ array('http', null, 443, 'https', 443, ""),
+ array('https', null, null, 'https', 443, ""),
+ array('https', null, 443, 'https', 443, ""),
+
+ // Non-standard ports
+ array('http', null, null, 'http', 8080, ":8080"),
+ array('http', 4080, null, 'http', 8080, ":4080"),
+ array('http', 80, null, 'http', 8080, ""),
+ array('https', null, null, 'http', 8080, ""),
+ array('https', null, 8443, 'http', 8080, ":8443"),
+ array('https', null, 443, 'http', 8080, ""),
+
+ array('https', null, null, 'https', 8443, ":8443"),
+ array('https', null, 4443, 'https', 8443, ":4443"),
+ array('https', null, 443, 'https', 8443, ""),
+ array('http', null, null, 'https', 8443, ""),
+ array('http', 8080, 4443, 'https', 8443, ":8080"),
+ array('http', 80, 4443, 'https', 8443, ""),
+ );
+ }
+
+ /**
+ * @dataProvider urlRedirectProvider
+ */
+ public function testUrlRedirect($scheme, $httpPort, $httpsPort, $requestScheme, $requestPort, $expectedPort)
+ {
+ $host = 'www.example.com';
+ $baseUrl = '/base';
+ $path = '/redirect-path';
+ $expectedUrl = "$scheme://$host$expectedPort$baseUrl$path";
+
+ $request = $this->createRequestObject($requestScheme, $host, $requestPort, $baseUrl);
+ $controller = $this->createRedirectController($request);
+
+ $returnValue = $controller->urlRedirectAction($path, false, $scheme, $httpPort, $httpsPort);
+ $this->assertRedirectUrl($returnValue, $expectedUrl);
+ }
+
+ private function createRequestObject($scheme, $host, $port, $baseUrl)
+ {
+ $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
+ $request
+ ->expects($this->any())
+ ->method('getScheme')
+ ->will($this->returnValue($scheme));
+ $request
+ ->expects($this->any())
+ ->method('getHost')
+ ->will($this->returnValue($host));
+ $request
+ ->expects($this->any())
+ ->method('getPort')
+ ->will($this->returnValue($port));
+ $request
+ ->expects($this->any())
+ ->method('getBaseUrl')
+ ->will($this->returnValue($baseUrl));
+
+ return $request;
+ }
+
+ private function createRedirectController(Request $request, $httpPort = null, $httpsPort = null)
+ {
+ $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+ $container
+ ->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo('request'))
+ ->will($this->returnValue($request));
+ if (null !== $httpPort) {
+ $container
+ ->expects($this->once())
+ ->method('hasParameter')
+ ->with($this->equalTo('request_listener.http_port'))
+ ->will($this->returnValue(true));
+ $container
+ ->expects($this->once())
+ ->method('getParameter')
+ ->with($this->equalTo('request_listener.http_port'))
+ ->will($this->returnValue($httpPort));
+ }
+ if (null !== $httpsPort) {
+ $container
+ ->expects($this->once())
+ ->method('hasParameter')
+ ->with($this->equalTo('request_listener.https_port'))
+ ->will($this->returnValue(true));
+ $container
+ ->expects($this->once())
+ ->method('getParameter')
+ ->with($this->equalTo('request_listener.https_port'))
+ ->will($this->returnValue($httpsPort));
+ }
+
+ $controller = new RedirectController();
+ $controller->setContainer($container);
+
+ return $controller;
+ }
+
+ public function assertRedirectUrl(Response $returnResponse, $expectedUrl)
+ {
+ $this->assertTrue($returnResponse->isRedirect($expectedUrl), "Expected: $expectedUrl\nGot: ".$returnResponse->headers->get('Location'));
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index de1001aae6..21664dc5b1 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -182,7 +182,7 @@ class SecurityExtension extends Extension
$container,
$access['path'],
$access['host'],
- count($access['methods']) === 0 ? null : $access['methods'],
+ $access['methods'],
$access['ip']
);
@@ -574,7 +574,7 @@ class SecurityExtension extends Extension
return $switchUserListenerId;
}
- private function createRequestMatcher($container, $path = null, $host = null, $methods = null, $ip = null, array $attributes = array())
+ private function createRequestMatcher($container, $path = null, $host = null, $methods = array(), $ip = null, array $attributes = array())
{
$serialized = serialize(array($path, $host, $methods, $ip, $attributes));
$id = 'security.request_matcher.'.md5($serialized).sha1($serialized);
@@ -583,6 +583,10 @@ class SecurityExtension extends Extension
return $this->requestMatchers[$id];
}
+ if ($methods) {
+ $methods = array_map('strtoupper', (array) $methods);
+ }
+
// only add arguments that are necessary
$arguments = array($path, $host, $methods, $ip, $attributes);
while (count($arguments) > 0 && !end($arguments)) {
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php
index 8652333b85..235d324d78 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php
@@ -63,7 +63,7 @@ $container->loadFromExtension('security', array(
),
'access_control' => array(
- array('path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https'),
+ array('path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => array('get', 'POST')),
array('path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'),
),
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml
index 20c376075a..ff8cfc0ad3 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml
@@ -57,7 +57,7 @@
ROLE_USER,ROLE_ADMIN,ROLE_ALLOWED_TO_SWITCH
ROLE_USER,ROLE_ADMIN
-
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml
index ade174fe6f..4c3738f081 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml
@@ -51,7 +51,7 @@ security:
ROLE_REMOTE: ROLE_USER,ROLE_ADMIN
access_control:
- - { path: /blog/524, role: ROLE_USER, requires_channel: https }
+ - { path: /blog/524, role: ROLE_USER, requires_channel: https, methods: [get, POST]}
-
path: /blog/.*
role: IS_AUTHENTICATED_ANONYMOUSLY
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
index 60e1778822..ae5fadbed6 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php
@@ -102,6 +102,7 @@ abstract class SecurityExtensionTest extends \PHPUnit_Framework_TestCase
$matcherIds = array();
foreach ($rules as $rule) {
list($matcherId, $roles, $channel) = $rule;
+ $requestMatcher = $container->getDefinition($matcherId);
$this->assertFalse(isset($matcherIds[$matcherId]));
$matcherIds[$matcherId] = true;
@@ -110,9 +111,17 @@ abstract class SecurityExtensionTest extends \PHPUnit_Framework_TestCase
if (1 === $i) {
$this->assertEquals(array('ROLE_USER'), $roles);
$this->assertEquals('https', $channel);
+ $this->assertEquals(
+ array('/blog/524', null, array('GET', 'POST')),
+ $requestMatcher->getArguments()
+ );
} elseif (2 === $i) {
$this->assertEquals(array('IS_AUTHENTICATED_ANONYMOUSLY'), $roles);
$this->assertNull($channel);
+ $this->assertEquals(
+ array('/blog/.*'),
+ $requestMatcher->getArguments()
+ );
}
}
}
diff --git a/src/Symfony/Component/DomCrawler/Form.php b/src/Symfony/Component/DomCrawler/Form.php
index dbd9d21346..160bd44292 100644
--- a/src/Symfony/Component/DomCrawler/Form.php
+++ b/src/Symfony/Component/DomCrawler/Form.php
@@ -359,7 +359,7 @@ class Form extends Link implements \ArrayAccess
$xpath = new \DOMXPath($document);
foreach ($xpath->query('descendant::input | descendant::button | descendant::textarea | descendant::select', $root) as $node) {
- if (!$node->hasAttribute('name')) {
+ if (!$node->hasAttribute('name') || !$node->getAttribute('name')) {
continue;
}
diff --git a/src/Symfony/Component/DomCrawler/Tests/FormTest.php b/src/Symfony/Component/DomCrawler/Tests/FormTest.php
index b53a8309ca..89a0d3c24c 100644
--- a/src/Symfony/Component/DomCrawler/Tests/FormTest.php
+++ b/src/Symfony/Component/DomCrawler/Tests/FormTest.php
@@ -126,6 +126,12 @@ class FormTest extends \PHPUnit_Framework_TestCase
',
array(),
),
+ array(
+ 'does not take into account input fields with an empty name attribute value',
+ '
+ ',
+ array(),
+ ),
array(
'takes into account disabled input fields',
'
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php
index 282a5247f4..96ece87f30 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php
@@ -96,8 +96,8 @@ class NumberToLocalizedStringTransformerTest extends LocalizedTestCase
public function testDecimalSeparatorMayBeDotIfGroupingSeparatorIsNotDot()
{
- if ($this->isLowerThanIcuVersion('4.5')) {
- $this->markTestSkipped('Please upgrade ICU version to 4.5+');
+ if ($this->isLowerThanIcuVersion('4.7')) {
+ $this->markTestSkipped('Please upgrade ICU version to 4.7+');
}
\Locale::setDefault('fr');
@@ -117,8 +117,8 @@ class NumberToLocalizedStringTransformerTest extends LocalizedTestCase
*/
public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDot()
{
- if ($this->isLowerThanIcuVersion('4.5')) {
- $this->markTestSkipped('Please upgrade ICU version to 4.5+');
+ if ($this->isLowerThanIcuVersion('4.7')) {
+ $this->markTestSkipped('Please upgrade ICU version to 4.7+');
}
$transformer = new NumberToLocalizedStringTransformer(null, true);
@@ -131,8 +131,8 @@ class NumberToLocalizedStringTransformerTest extends LocalizedTestCase
*/
public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDot_noGroupSep()
{
- if ($this->isLowerThanIcuVersion('4.5')) {
- $this->markTestSkipped('Please upgrade ICU version to 4.5+');
+ if ($this->isLowerThanIcuVersion('4.7')) {
+ $this->markTestSkipped('Please upgrade ICU version to 4.7+');
}
$transformer = new NumberToLocalizedStringTransformer(null, true);
@@ -151,8 +151,8 @@ class NumberToLocalizedStringTransformerTest extends LocalizedTestCase
public function testDecimalSeparatorMayBeCommaIfGroupingSeparatorIsNotComma()
{
- if ($this->isLowerThanIcuVersion('4.5')) {
- $this->markTestSkipped('Please upgrade ICU version to 4.5+');
+ if ($this->isLowerThanIcuVersion('4.7')) {
+ $this->markTestSkipped('Please upgrade ICU version to 4.7+');
}
\Locale::setDefault('ak');
@@ -172,8 +172,8 @@ class NumberToLocalizedStringTransformerTest extends LocalizedTestCase
*/
public function testDecimalSeparatorMayNotBeCommaIfGroupingSeparatorIsComma()
{
- if ($this->isLowerThanIcuVersion('4.5')) {
- $this->markTestSkipped('Please upgrade ICU version to 4.5+');
+ if ($this->isLowerThanIcuVersion('4.7')) {
+ $this->markTestSkipped('Please upgrade ICU version to 4.7+');
}
\Locale::setDefault('en');
@@ -187,8 +187,8 @@ class NumberToLocalizedStringTransformerTest extends LocalizedTestCase
*/
public function testDecimalSeparatorMayNotBeCommaIfGroupingSeparatorIsComma_noGroupSep()
{
- if ($this->isLowerThanIcuVersion('4.5')) {
- $this->markTestSkipped('Please upgrade ICU version to 4.5+');
+ if ($this->isLowerThanIcuVersion('4.7')) {
+ $this->markTestSkipped('Please upgrade ICU version to 4.7+');
}
\Locale::setDefault('en');
diff --git a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php
index 5c529dab02..2bc4aa550f 100644
--- a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php
@@ -141,11 +141,12 @@ class DigestData
public function __construct($header)
{
$this->header = $header;
- $parts = preg_split('/, /', $header);
+ preg_match_all('/(\w+)=("((?:[^"\\\\]|\\\\.)+)"|([^\s,$]+))/', $header, $matches, PREG_SET_ORDER);
$this->elements = array();
- foreach ($parts as $part) {
- list($key, $value) = explode('=', $part);
- $this->elements[$key] = '"' === $value[0] ? substr($value, 1, -1) : $value;
+ foreach ($matches as $match) {
+ if (isset($match[1]) && isset($match[3])) {
+ $this->elements[$match[1]] = isset($match[4]) ? $match[4] : $match[3];
+ }
}
}
@@ -156,7 +157,7 @@ class DigestData
public function getUsername()
{
- return $this->elements['username'];
+ return strtr($this->elements['username'], array("\\\"" => "\"", "\\\\" => "\\"));
}
public function validateAndDecode($entryPointKey, $expectedRealm)
@@ -188,7 +189,7 @@ class DigestData
$this->nonceExpiryTime = $nonceTokens[0];
if (md5($this->nonceExpiryTime.':'.$entryPointKey) !== $nonceTokens[1]) {
- new BadCredentialsException(sprintf('Nonce token compromised "%s".', $nonceAsPlainText));
+ throw new BadCredentialsException(sprintf('Nonce token compromised "%s".', $nonceAsPlainText));
}
}
diff --git a/tests/Symfony/Tests/Component/Security/Http/Firewall/DigestDataTest.php b/tests/Symfony/Tests/Component/Security/Http/Firewall/DigestDataTest.php
new file mode 100644
index 0000000000..cfb929cacc
--- /dev/null
+++ b/tests/Symfony/Tests/Component/Security/Http/Firewall/DigestDataTest.php
@@ -0,0 +1,181 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Security\Tests\Http\Firewall;
+
+use Symfony\Component\Security\Http\Firewall\DigestData;
+
+class DigestDataTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetResponse()
+ {
+ $digestAuth = new DigestData(
+ 'username="user", realm="Welcome, robot!", ' .
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('b52938fc9e6d7c01be7702ece9031b42', $digestAuth->getResponse());
+ }
+
+ public function testGetUsername()
+ {
+ $digestAuth = new DigestData(
+ 'username="user", realm="Welcome, robot!", ' .
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('user', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithQuote()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"user\"", realm="Welcome, robot!", ' .
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"user"', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithQuoteAndEscape()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"u\\\\\"ser\"", realm="Welcome, robot!", ' .
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"u\\"ser"', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithSingleQuote()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"u\'ser\"", realm="Welcome, robot!", ' .
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"u\'ser"', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithSingleQuoteAndEscape()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"u\\\'ser\"", realm="Welcome, robot!", ' .
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"u\\\'ser"', $digestAuth->getUsername());
+ }
+
+ public function testGetUsernameWithEscape()
+ {
+ $digestAuth = new DigestData(
+ 'username="\"u\\ser\"", realm="Welcome, robot!", ' .
+ 'nonce="MTM0NzMyMTgyMy42NzkzOmRlZjM4NmIzOGNjMjE0OWJiNDU0MDAxNzJmYmM1MmZl", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $this->assertEquals('"u\\ser"', $digestAuth->getUsername());
+ }
+
+ public function testValidateAndDecode()
+ {
+ $time = microtime(true);
+ $key = 'ThisIsAKey';
+ $nonce = base64_encode($time . ':' . md5($time . ':' . $key));
+
+ $digestAuth = new DigestData(
+ 'username="user", realm="Welcome, robot!", nonce="' . $nonce . '", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ try {
+ $digestAuth->validateAndDecode($key, 'Welcome, robot!');
+ } catch (\Exception $e) {
+ $this->fail(sprintf('testValidateAndDecode fail with message: %s', $e->getMessage()));
+ }
+ }
+
+ public function testCalculateServerDigest()
+ {
+ $this->calculateServerDigest('user', 'Welcome, robot!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ }
+
+ public function testCalculateServerDigestWithQuote()
+ {
+ $this->calculateServerDigest('\"user\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ }
+
+ public function testCalculateServerDigestWithQuoteAndEscape()
+ {
+ $this->calculateServerDigest('\"u\\\\\"ser\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ }
+
+ public function testCalculateServerDigestEscape()
+ {
+ $this->calculateServerDigest('\"u\\ser\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ $this->calculateServerDigest('\"u\\ser\\\\\"', 'Welcome, \"robot\"!', 'pass,word=password', 'ThisIsAKey', '00000001', 'MDIwODkz', 'auth', 'GET', '/path/info?p1=5&p2=5');
+ }
+
+ public function testIsNonceExpired()
+ {
+ $time = microtime(true) + 10;
+ $key = 'ThisIsAKey';
+ $nonce = base64_encode($time . ':' . md5($time . ':' . $key));
+
+ $digestAuth = new DigestData(
+ 'username="user", realm="Welcome, robot!", nonce="' . $nonce . '", ' .
+ 'uri="/path/info?p1=5&p2=5", cnonce="MDIwODkz", nc=00000001, qop="auth", ' .
+ 'response="b52938fc9e6d7c01be7702ece9031b42"'
+ );
+
+ $digestAuth->validateAndDecode($key, 'Welcome, robot!');
+
+ $this->assertFalse($digestAuth->isNonceExpired());
+ }
+
+ protected function setUp()
+ {
+ class_exists('Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener', true);
+ }
+
+ private function calculateServerDigest($username, $realm, $password, $key, $nc, $cnonce, $qop, $method, $uri)
+ {
+ $time = microtime(true);
+ $nonce = base64_encode($time . ':' . md5($time . ':' . $key));
+
+ $response = md5(
+ md5($username . ':' . $realm . ':' . $password) . ':' . $nonce . ':' . $nc . ':' . $cnonce . ':' . $qop . ':' . md5($method . ':' . $uri)
+ );
+
+ $digest = sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop="%s", response="%s"',
+ $username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response
+ );
+
+ $digestAuth = new DigestData($digest);
+
+ $this->assertEquals($digestAuth->getResponse(), $digestAuth->calculateServerDigest($password, $method));
+ }
+}