mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2025-09-09 21:12:41 +00:00
Basics of DigestAuth are working.
This commit is contained in:
parent
945e49ad09
commit
705b3288e1
2 changed files with 222 additions and 45 deletions
|
@ -66,18 +66,19 @@ class DigestAuthenticate extends BaseAuthenticate {
|
|||
* @return mixed Either false on failure, or an array of user data on success.
|
||||
*/
|
||||
public function authenticate(CakeRequest $request, CakeResponse $response) {
|
||||
$username = env('PHP_AUTH_USER');
|
||||
$pass = env('PHP_AUTH_PW');
|
||||
$digest = $this->_getDigest();
|
||||
|
||||
if (empty($username) || empty($pass)) {
|
||||
if (empty($digest)) {
|
||||
$response->header($this->loginHeaders());
|
||||
$response->send();
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->_findUser($username, $pass);
|
||||
$result = $this->_findUser($digest['username'], null);
|
||||
$password = $result[$this->settings['fields']['password']];
|
||||
unset($result[$this->settings['fields']['password']]);
|
||||
|
||||
if (empty($result)) {
|
||||
if (empty($result) || $digest['response'] !== $this->generateResponseHash($digest, $password)) {
|
||||
$response->header($this->loginHeaders());
|
||||
$response->header('Location', Router::reverse($request));
|
||||
$response->statusCode(401);
|
||||
|
@ -87,6 +88,103 @@ class DigestAuthenticate extends BaseAuthenticate {
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a user record using the standard options.
|
||||
*
|
||||
* @param string $username The username/identifier.
|
||||
* @param string $password Unused password, digest doesn't require passwords.
|
||||
* @return Mixed Either false on failure, or an array of user data.
|
||||
*/
|
||||
protected function _findUser($username, $password) {
|
||||
$userModel = $this->settings['userModel'];
|
||||
list($plugin, $model) = pluginSplit($userModel);
|
||||
$fields = $this->settings['fields'];
|
||||
|
||||
$conditions = array(
|
||||
$model . '.' . $fields['username'] => $username,
|
||||
);
|
||||
if (!empty($this->settings['scope'])) {
|
||||
$conditions = array_merge($conditions, $this->settings['scope']);
|
||||
}
|
||||
$result = ClassRegistry::init($userModel)->find('first', array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => 0
|
||||
));
|
||||
if (empty($result) || empty($result[$model])) {
|
||||
return false;
|
||||
}
|
||||
return $result[$model];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the digest headers from the request/environment.
|
||||
*
|
||||
* @return array Array of digest information.
|
||||
*/
|
||||
protected function _getDigest() {
|
||||
$digest = env('PHP_AUTH_DIGEST');
|
||||
if (empty($digest) && function_exists('apache_request_headers')) {
|
||||
$headers = apache_request_headers();
|
||||
if (!empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') {
|
||||
$digest = substr($headers['Authorization'], 7);
|
||||
}
|
||||
}
|
||||
if (empty($digest)) {
|
||||
return false;
|
||||
}
|
||||
return $this->parseAuthData($digest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the digest authentication headers and split them up.
|
||||
*
|
||||
* @param string $digest The raw digest authentication headers.
|
||||
* @return array An array of digest authentication headers
|
||||
*/
|
||||
public function parseAuthData($digest) {
|
||||
if (substr($digest, 0, 7) == 'Digest ') {
|
||||
$digest = substr($digest, 7);
|
||||
}
|
||||
$keys = $match = array();
|
||||
$req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
|
||||
preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER);
|
||||
|
||||
foreach ($match as $i) {
|
||||
$keys[$i[1]] = $i[3];
|
||||
unset($req[$i[1]]);
|
||||
}
|
||||
|
||||
if (empty($req)) {
|
||||
return $keys;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the response hash for a given digest array.
|
||||
*
|
||||
* @param array $digest Digest information containing data from DigestAuthenticate::parseAuthData().
|
||||
* @param string $password The digest hash password generated with DigestAuthenticate::password()
|
||||
* @return string Response hash
|
||||
*/
|
||||
public function generateResponseHash($digest, $password) {
|
||||
return md5(
|
||||
$password .
|
||||
':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' .
|
||||
md5(env('REQUEST_METHOD') . ':' . $digest['uri'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an auth digest password hash to store
|
||||
*
|
||||
* @param string $password The unhashed password to make a digest hash for.
|
||||
* @return string the hashed password that can later be used with Digest authentication.
|
||||
*/
|
||||
public static function password($username, $realm, $password) {
|
||||
return md5($username . ':' . $realm . ':' . $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the login headers
|
||||
*
|
||||
|
|
|
@ -46,7 +46,7 @@ class DigestAuthenticateTest extends CakeTestCase {
|
|||
'opaque' => '123abc'
|
||||
));
|
||||
|
||||
$password = Security::hash('password', null, true);
|
||||
$password = DigestAuthenticate::password('mariano', 'localhost', 'cake');
|
||||
ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"'));
|
||||
$this->server = $_SERVER;
|
||||
$this->response = $this->getMock('CakeResponse');
|
||||
|
@ -99,44 +99,36 @@ class DigestAuthenticateTest extends CakeTestCase {
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
function testAuthenticateNoUsername() {
|
||||
$request = new CakeRequest('posts/index', false);
|
||||
$_SERVER['PHP_AUTH_PW'] = 'foobar';
|
||||
|
||||
$this->response->expects($this->once())
|
||||
->method('header')
|
||||
->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"');
|
||||
|
||||
$this->assertFalse($this->auth->authenticate($request, $this->response));
|
||||
}
|
||||
|
||||
/**
|
||||
* test the authenticate method
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testAuthenticateNoPassword() {
|
||||
$request = new CakeRequest('posts/index', false);
|
||||
$_SERVER['PHP_AUTH_USER'] = 'mariano';
|
||||
|
||||
$this->response->expects($this->once())
|
||||
->method('header')
|
||||
->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"');
|
||||
|
||||
$this->assertFalse($this->auth->authenticate($request, $this->response));
|
||||
}
|
||||
|
||||
/**
|
||||
* test the authenticate method
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testAuthenticateInjection() {
|
||||
function testAuthenticateWrongUsername() {
|
||||
$request = new CakeRequest('posts/index', false);
|
||||
$request->addParams(array('pass' => array(), 'named' => array()));
|
||||
|
||||
$_SERVER['PHP_AUTH_USER'] = '> 1';
|
||||
$_SERVER['PHP_AUTH_PW'] = "' OR 1 = 1";
|
||||
$_SERVER['PHP_AUTH_DIGEST'] = <<<DIGEST
|
||||
Digest username="incorrect_user",
|
||||
realm="localhost",
|
||||
nonce="123456",
|
||||
uri="/dir/index.html",
|
||||
qop=auth,
|
||||
nc=00000001,
|
||||
cnonce="0a4f113b",
|
||||
response="6629fae49393a05397450978507c4ef1",
|
||||
opaque="123abc"
|
||||
DIGEST;
|
||||
|
||||
$this->response->expects($this->at(0))
|
||||
->method('header')
|
||||
->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"');
|
||||
|
||||
$this->response->expects($this->at(1))
|
||||
->method('header')
|
||||
->with('Location', Router::reverse($request));
|
||||
|
||||
$this->response->expects($this->at(2))
|
||||
->method('statusCode')
|
||||
->with(401);
|
||||
|
||||
$this->response->expects($this->at(3))
|
||||
->method('send');
|
||||
|
||||
$this->assertFalse($this->auth->authenticate($request, $this->response));
|
||||
}
|
||||
|
@ -160,6 +152,7 @@ class DigestAuthenticateTest extends CakeTestCase {
|
|||
$result = $this->auth->authenticate($request, $this->response);
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test authenticate sucesss
|
||||
*
|
||||
|
@ -169,8 +162,17 @@ class DigestAuthenticateTest extends CakeTestCase {
|
|||
$request = new CakeRequest('posts/index', false);
|
||||
$request->addParams(array('pass' => array(), 'named' => array()));
|
||||
|
||||
$_SERVER['PHP_AUTH_USER'] = 'mariano';
|
||||
$_SERVER['PHP_AUTH_PW'] = 'password';
|
||||
$_SERVER['PHP_AUTH_DIGEST'] = <<<DIGEST
|
||||
Digest username="mariano",
|
||||
realm="localhost",
|
||||
nonce="123",
|
||||
uri="/dir/index.html",
|
||||
qop=auth,
|
||||
nc=1,
|
||||
cnonce="123",
|
||||
response="5e064cc2f3b20894a806b2de3edf9536",
|
||||
opaque="123abc"
|
||||
DIGEST;
|
||||
|
||||
$result = $this->auth->authenticate($request, $this->response);
|
||||
$expected = array(
|
||||
|
@ -192,8 +194,17 @@ class DigestAuthenticateTest extends CakeTestCase {
|
|||
$request = new CakeRequest('posts/index', false);
|
||||
$request->addParams(array('pass' => array(), 'named' => array()));
|
||||
|
||||
$_SERVER['PHP_AUTH_USER'] = 'mariano';
|
||||
$_SERVER['PHP_AUTH_PW'] = 'password';
|
||||
$_SERVER['PHP_AUTH_DIGEST'] = <<<DIGEST
|
||||
Digest username="mariano",
|
||||
realm="localhost",
|
||||
nonce="123",
|
||||
uri="/dir/index.html",
|
||||
qop=auth,
|
||||
nc=1,
|
||||
cnonce="123",
|
||||
response="6629fae49393a05397450978507c4ef1",
|
||||
opaque="123abc"
|
||||
DIGEST;
|
||||
|
||||
$this->response->expects($this->at(0))
|
||||
->method('header')
|
||||
|
@ -213,4 +224,72 @@ class DigestAuthenticateTest extends CakeTestCase {
|
|||
$this->assertFalse($this->auth->authenticate($request, $this->response));
|
||||
}
|
||||
|
||||
/**
|
||||
* testParseDigestAuthData method
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function testParseAuthData() {
|
||||
$digest = <<<DIGEST
|
||||
Digest username="Mufasa",
|
||||
realm="testrealm@host.com",
|
||||
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
|
||||
uri="/dir/index.html",
|
||||
qop=auth,
|
||||
nc=00000001,
|
||||
cnonce="0a4f113b",
|
||||
response="6629fae49393a05397450978507c4ef1",
|
||||
opaque="5ccc069c403ebaf9f0171e9517f40e41"
|
||||
DIGEST;
|
||||
$expected = array(
|
||||
'username' => 'Mufasa',
|
||||
'realm' => 'testrealm@host.com',
|
||||
'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
|
||||
'uri' => '/dir/index.html',
|
||||
'qop' => 'auth',
|
||||
'nc' => '00000001',
|
||||
'cnonce' => '0a4f113b',
|
||||
'response' => '6629fae49393a05397450978507c4ef1',
|
||||
'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
|
||||
);
|
||||
$result = $this->auth->parseAuthData($digest);
|
||||
$this->assertSame($expected, $result);
|
||||
|
||||
$result = $this->auth->parseAuthData('');
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test parsing digest information with email addresses
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testParseAuthEmailAddress() {
|
||||
$digest = <<<DIGEST
|
||||
Digest username="mark@example.com",
|
||||
realm="testrealm@host.com",
|
||||
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
|
||||
uri="/dir/index.html",
|
||||
qop=auth,
|
||||
nc=00000001,
|
||||
cnonce="0a4f113b",
|
||||
response="6629fae49393a05397450978507c4ef1",
|
||||
opaque="5ccc069c403ebaf9f0171e9517f40e41"
|
||||
DIGEST;
|
||||
$expected = array(
|
||||
'username' => 'mark@example.com',
|
||||
'realm' => 'testrealm@host.com',
|
||||
'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
|
||||
'uri' => '/dir/index.html',
|
||||
'qop' => 'auth',
|
||||
'nc' => '00000001',
|
||||
'cnonce' => '0a4f113b',
|
||||
'response' => '6629fae49393a05397450978507c4ef1',
|
||||
'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
|
||||
);
|
||||
$result = $this->auth->parseAuthData($digest);
|
||||
$this->assertIdentical($expected, $result);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue