Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge branch 'master' of git://git.zendframework.com/zf
Browse files Browse the repository at this point in the history
  • Loading branch information
Shahar Evron committed Oct 6, 2011
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 144 deletions.
81 changes: 45 additions & 36 deletions src/Adapter/Http.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@

use Zend\Authentication\Adapter as AuthenticationAdapter,
Zend\Authentication,
Zend\Controller\Request\Http as HTTPRequest,
Zend\Controller\Response\Http as HTTPResponse;
Zend\Http\Request as HTTPRequest,
Zend\Http\Response as HTTPResponse,
Zend\Uri\UriFactory;

/**
* HTTP Authentication Adapter
Expand All @@ -50,14 +51,14 @@ class Http implements AuthenticationAdapter
/**
* Reference to the HTTP Request object
*
* @var Zend_Controller_Request_Http
* @var HTTPRequest
*/
protected $_request;

/**
* Reference to the HTTP Response object
*
* @var Zend_Controller_Response_Http
* @var HTTPResponse
*/
protected $_response;

Expand Down Expand Up @@ -292,8 +293,8 @@ public function getDigestResolver()
/**
* Setter for the Request object
*
* @param Zend_Controller_Request_Http $request
* @return Zend\Authentication\Adapter\Http Provides a fluent interface
* @param HTTPRequest $request
* @return Http Provides a fluent interface
*/
public function setRequest(HTTPRequest $request)
{
Expand All @@ -305,7 +306,7 @@ public function setRequest(HTTPRequest $request)
/**
* Getter for the Request object
*
* @return Zend_Controller_Request_Http
* @return HTTPRequest
*/
public function getRequest()
{
Expand All @@ -315,8 +316,8 @@ public function getRequest()
/**
* Setter for the Response object
*
* @param Zend_Controller_Response_Http $response
* @return Zend\Authentication\Adapter\Http Provides a fluent interface
* @param HTTPResponse $response
* @return Http Provides a fluent interface
*/
public function setResponse(HTTPResponse $response)
{
Expand All @@ -328,7 +329,7 @@ public function setResponse(HTTPResponse $response)
/**
* Getter for the Response object
*
* @return Zend_Controller_Response_Http
* @return HTTPResponse
*/
public function getResponse()
{
Expand All @@ -343,8 +344,7 @@ public function getResponse()
*/
public function authenticate()
{
if (empty($this->_request) ||
empty($this->_response)) {
if (empty($this->_request) || empty($this->_response)) {
throw new Exception\RuntimeException('Request and Response objects must be set before calling '
. 'authenticate()');
}
Expand All @@ -355,7 +355,11 @@ public function authenticate()
$getHeader = 'Authorization';
}

$authHeader = $this->_request->getHeader($getHeader);
$headers = $this->_request->headers();
if (!$headers->has($getHeader)) {
return $this->_challengeClient();
}
$authHeader = $headers->get($getHeader)->getFieldValue();
if (!$authHeader) {
return $this->_challengeClient();
}
Expand All @@ -366,7 +370,7 @@ public function authenticate()
// The server can issue multiple challenges, but the client should
// answer with only the selected auth scheme.
if (!in_array($clientScheme, $this->_supportedSchemes)) {
$this->_response->setHttpResponseCode(400);
$this->_response->setStatusCode(400);
return new Authentication\Result(
Authentication\Result::FAILURE_UNCATEGORIZED,
array(),
Expand Down Expand Up @@ -412,14 +416,15 @@ protected function _challengeClient()
$headerName = 'WWW-Authenticate';
}

$this->_response->setHttpResponseCode($statusCode);
$this->_response->setStatusCode($statusCode);

// Send a challenge in each acceptable authentication scheme
$headers = $this->_response->headers();
if (in_array('basic', $this->_acceptSchemes)) {
$this->_response->setHeader($headerName, $this->_basicHeader());
$headers->addHeaderLine($headerName, $this->_basicHeader());
}
if (in_array('digest', $this->_acceptSchemes)) {
$this->_response->setHeader($headerName, $this->_digestHeader());
$headers->addHeaderLine($headerName, $this->_digestHeader());
}
return new Authentication\Result(
Authentication\Result::FAILURE_CREDENTIAL_INVALID,
Expand Down Expand Up @@ -525,7 +530,7 @@ protected function _digestAuth($header)

$data = $this->_parseDigestAuth($header);
if ($data === false) {
$this->_response->setHttpResponseCode(400);
$this->_response->setStatusCode(400);
return new Authentication\Result(
Authentication\Result::FAILURE_UNCATEGORIZED,
array(),
Expand Down Expand Up @@ -615,7 +620,7 @@ protected function _calcNonce()
// would be surprising if the user just logged in.
$timeout = ceil(time() / $this->_nonceTimeout) * $this->_nonceTimeout;

$nonce = hash('md5', $timeout . ':' . $this->_request->getServer('HTTP_USER_AGENT') . ':' . __CLASS__);
$nonce = hash('md5', $timeout . ':' . $this->_request->server()->get('HTTP_USER_AGENT') . ':' . __CLASS__);
return $nonce;
}

Expand Down Expand Up @@ -689,21 +694,19 @@ protected function _parseDigestAuth($header)
// Section 3.2.2.5 in RFC 2617 says the authenticating server must
// verify that the URI field in the Authorization header is for the
// same resource requested in the Request Line.
$rUri = @parse_url($this->_request->getRequestUri());
$cUri = @parse_url($temp[1]);
if (false === $rUri || false === $cUri) {
$rUri = $this->_request->uri();
$cUri = UriFactory::factory($temp[1]);

// Make sure the path portion of both URIs is the same
if ($rUri->getPath() != $cUri->getPath()) {
return false;
} else {
// Make sure the path portion of both URIs is the same
if ($rUri['path'] != $cUri['path']) {
return false;
}
// Section 3.2.2.5 seems to suggest that the value of the URI
// Authorization field should be made into an absolute URI if the
// Request URI is absolute, but it's vague, and that's a bunch of
// code I don't want to write right now.
$data['uri'] = $temp[1];
}

// Section 3.2.2.5 seems to suggest that the value of the URI
// Authorization field should be made into an absolute URI if the
// Request URI is absolute, but it's vague, and that's a bunch of
// code I don't want to write right now.
$data['uri'] = $temp[1];
$temp = null;

$ret = preg_match('/response="([^"]+)"/', $header, $temp);
Expand Down Expand Up @@ -747,13 +750,19 @@ protected function _parseDigestAuth($header)
if (!$ret || empty($temp[1])) {

// Big surprise: IE isn't RFC 2617-compliant.
if (false !== strpos($this->_request->getHeader('User-Agent'), 'MSIE')) {
$temp[1] = '';
$this->_ieNoOpaque = true;
} else {
$headers = $this->_request->headers();
if (!$headers->has('User-Agent')) {
return false;
}
$userAgent = $headers->get('User-Agent')->getFieldValue();
if (false === strpos($userAgent, 'MSIE')) {
return false;
}

$temp[1] = '';
$this->_ieNoOpaque = true;
}

// This implementation only sends MD5 hex strings in the opaque value
if (!$this->_ieNoOpaque &&
(32 != strlen($temp[1]) || !ctype_xdigit($temp[1]))) {
Expand Down
105 changes: 64 additions & 41 deletions test/Adapter/Http/AuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
namespace ZendTest\Auth\Adapter\Http;

use Zend\Authentication\Adapter\Http,
Zend\Controller\Response\Http as HTTPResponse;
Zend\Http\Headers,
Zend\Http\Request,
Zend\Http\Response,
Zend\Stdlib\Parameters;

/**
* @category Zend
Expand Down Expand Up @@ -114,7 +117,10 @@ public function testBasicChallenge()
// false result.

// The expected Basic Www-Authenticate header value
$basic = 'Basic realm="' . $this->_bothConfig['realm'] . '"';
$basic = array(
'type' => 'Basic ',
'realm' => 'realm="' . $this->_bothConfig['realm'] . '"',
);

$data = $this->_doAuth('', 'basic');
$this->_checkUnauthorized($data, $basic);
Expand Down Expand Up @@ -152,12 +158,23 @@ public function testBothChallenges()

// Verify the status code and the presence of both challenges
$this->assertEquals(401, $status);
$this->assertEquals('Www-Authenticate', $headers[0]['name']);
$this->assertEquals('Www-Authenticate', $headers[1]['name']);
$this->assertTrue($headers->has('Www-Authenticate'));
$wwwAuthenticate = $headers->get('Www-Authenticate');
$this->assertEquals(2, count($wwwAuthenticate));

// Check to see if the expected challenges match the actual
$this->assertEquals($basic, $headers[0]['value']);
$this->assertEquals($digest, $headers[1]['value']);
$basicFound = $digestFound = false;
foreach ($wwwAuthenticate as $header) {
$value = $header->getFieldValue();
if (preg_match('/^Basic/', $value)) {
$basicFound = true;
}
if (preg_match('/^Digest/', $value)) {
$digestFound = true;
}
}
$this->assertTrue($basicFound);
$this->assertTrue($digestFound);
}

public function testBasicAuthValidCreds()
Expand All @@ -174,7 +191,10 @@ public function testBasicAuthBadCreds()
// a bad username or password.

// The expected Basic Www-Authenticate header value
$basic = 'Basic realm="' . $this->_basicConfig['realm'] . '"';
$basic = array(
'type' => 'Basic ',
'realm' => 'realm="' . $this->_basicConfig['realm'] . '"',
);

$data = $this->_doAuth('Basic ' . base64_encode("Bad\tChars:In:Creds"), 'basic');
$this->_checkUnauthorized($data, $basic);
Expand All @@ -186,7 +206,10 @@ public function testBasicAuthBadUser()
// password

// The expected Basic Www-Authenticate header value
$basic = 'Basic realm="' . $this->_basicConfig['realm'] . '"';
$basic = array(
'type' => 'Basic ',
'realm' => 'realm="' . $this->_basicConfig['realm'] . '"',
);

$data = $this->_doAuth('Basic ' . base64_encode('Nobody:NotValid'), 'basic');
$this->_checkUnauthorized($data, $basic);
Expand All @@ -198,7 +221,10 @@ public function testBasicAuthBadPassword()
// password

// The expected Basic Www-Authenticate header value
$basic = 'Basic realm="' . $this->_basicConfig['realm'] . '"';
$basic = array(
'type' => 'Basic ',
'realm' => 'realm="' . $this->_basicConfig['realm'] . '"',
);

$data = $this->_doAuth('Basic ' . base64_encode('Bryce:Invalid'), 'basic');
$this->_checkUnauthorized($data, $basic);
Expand Down Expand Up @@ -310,24 +336,16 @@ public function testBadDigestRequest()
protected function _doAuth($clientHeader, $scheme)
{
// Set up stub request and response objects
$request = $this->getMock('Zend\Controller\Request\Http');
$response = new HTTPResponse;
$response->setHttpResponseCode(200);
$response->headersSentThrowsException = false;
$request = new Request;
$response = new Response;
$response->setStatusCode(200);

// Set stub method return values
$request->expects($this->any())
->method('getRequestUri')
->will($this->returnValue('/'));
$request->expects($this->any())
->method('getMethod')
->will($this->returnValue('GET'));
$request->expects($this->any())
->method('getServer')
->will($this->returnValue('PHPUnit'));
$request->expects($this->any())
->method('getHeader')
->will($this->returnValue($clientHeader));
$request->setUri('http://localhost/');
$request->setMethod('GET');
$request->setServer(new Parameters(array('HTTP_USER_AGENT' => 'PHPUnit')));
$headers = $request->headers();
$headers->addHeaderLine('Authorization', $clientHeader);

// Select an Authentication scheme
switch ($scheme) {
Expand All @@ -354,8 +372,8 @@ protected function _doAuth($clientHeader, $scheme)

$return = array(
'result' => $result,
'status' => $response->getHttpResponseCode(),
'headers' => $response->getHeaders()
'status' => $response->getStatusCode(),
'headers' => $response->headers(),
);
return $return;
}
Expand All @@ -367,18 +385,11 @@ protected function _doAuth($clientHeader, $scheme)
*/
protected function _digestChallenge()
{
$timeout = ceil(time() / 300) * 300;
$nonce = md5($timeout . ':PHPUnit:Zend\\Authentication\\Adapter\\Http');
$opaque = md5('Opaque Data:Zend\\Authentication\\Adapter\\Http');
$wwwauth = 'Digest '
. 'realm="' . $this->_digestConfig['realm'] . '", '
. 'domain="' . $this->_digestConfig['digest_domains'] . '", '
. 'nonce="' . $nonce . '", '
. 'opaque="' . $opaque . '", '
. 'algorithm="MD5", '
. 'qop="auth"';

return $wwwauth;
return array(
'type' => 'Digest ',
'realm' => 'realm="' . $this->_digestConfig['realm'] . '"',
'domain' => 'domain="' . $this->_bothConfig['digest_domains'] . '"',
);
}

/**
Expand Down Expand Up @@ -427,10 +438,22 @@ protected function _checkUnauthorized($data, $expected)

// Verify the status code and the presence of the challenge
$this->assertEquals(401, $status);
$this->assertEquals('Www-Authenticate', $headers[0]['name']);
$this->assertTrue($headers->has('Www-Authenticate'));

// Check to see if the expected challenge matches the actual
$this->assertEquals($expected, $headers[0]['value']);
$headers = $headers->get('Www-Authenticate');
$this->assertTrue($headers instanceof \ArrayIterator);
$this->assertEquals(1, count($headers));
$header = $headers[0]->getFieldValue();
$this->assertContains($expected['type'], $header, $header);
$this->assertContains($expected['realm'], $header, $header);
if (isset($expected['domain'])) {
$this->assertContains($expected['domain'], $header, $header);
$this->assertContains('algorithm="MD5"', $header, $header);
$this->assertContains('qop="auth"', $header, $header);
$this->assertRegExp('/nonce="[a-fA-F0-9]{32}"/', $header, $header);
$this->assertRegExp('/opaque="[a-fA-F0-9]{32}"/', $header, $header);
}
}

/**
Expand Down
Loading

0 comments on commit 55972fd

Please sign in to comment.