diff --git a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php index 5c529dab02..8567a0005c 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]; + } } } @@ -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..cd64f44e94 --- /dev/null +++ b/tests/Symfony/Tests/Component/Security/Http/Firewall/DigestDataTest.php @@ -0,0 +1,110 @@ + + * + * 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 setUp() + { + class_exists('Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener', true); + } + + 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 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() + { + $username = 'user'; + $realm = 'Welcome, robot!'; + $password = 'pass,word=password'; + $time = microtime(true); + $key = 'ThisIsAKey'; + $nonce = base64_encode($time . ':' . md5($time . ':' . $key)); + $nc = '00000001'; + $cnonce = 'MDIwODkz'; + $qop = 'auth'; + $method = 'GET'; + $uri = '/path/info?p1=5&p2=5'; + + $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)); + } + + 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()); + } +} \ No newline at end of file