mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2025-03-18 15:39:53 +00:00
Refactoring AuthComponent and implementing digest authentication in SecurityComponent
git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@5745 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
parent
736ab28b0b
commit
61c06ae94a
3 changed files with 114 additions and 65 deletions
|
@ -54,6 +54,13 @@ class AuthComponent extends Object {
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
var $components = array('Session', 'RequestHandler');
|
var $components = array('Session', 'RequestHandler');
|
||||||
|
/**
|
||||||
|
* A reference to the object used for authentication
|
||||||
|
*
|
||||||
|
* @var object
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
var $authenticate = null;
|
||||||
/**
|
/**
|
||||||
* The name of the component to use for Authorization or set this to
|
* The name of the component to use for Authorization or set this to
|
||||||
* 'controller' will validate against Controller::isAuthorized()
|
* 'controller' will validate against Controller::isAuthorized()
|
||||||
|
@ -62,7 +69,7 @@ class AuthComponent extends Object {
|
||||||
* array('model'=> 'name'); will validate mapActions against model $name::isAuthorize(user, controller, mapAction)
|
* array('model'=> 'name'); will validate mapActions against model $name::isAuthorize(user, controller, mapAction)
|
||||||
* 'object' will validate Controller::action against object::isAuthorized(user, controller, action)
|
* 'object' will validate Controller::action against object::isAuthorized(user, controller, action)
|
||||||
*
|
*
|
||||||
* @var string
|
* @var mixed
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
var $authorize = false;
|
var $authorize = false;
|
||||||
|
@ -97,13 +104,6 @@ class AuthComponent extends Object {
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
var $fields = array('username' => 'username', 'password' => 'password');
|
var $fields = array('username' => 'username', 'password' => 'password');
|
||||||
/**
|
|
||||||
* the hash function to use, options: sha1, sha256, md5
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
var $hash = 'sha1';
|
|
||||||
/**
|
/**
|
||||||
* The session key name where the record of the current user is stored. If
|
* The session key name where the record of the current user is stored. If
|
||||||
* unspecified, it will be "Auth.{$userModel name}".
|
* unspecified, it will be "Auth.{$userModel name}".
|
||||||
|
@ -262,16 +262,16 @@ class AuthComponent extends Object {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->allowedActions == array('*') || in_array($controller->action, $this->allowedActions)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->__setDefaults()) {
|
if (!$this->__setDefaults()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->data = $controller->data = $this->hashPasswords($controller->data);
|
$this->data = $controller->data = $this->hashPasswords($controller->data);
|
||||||
|
|
||||||
|
if ($this->allowedActions == array('*') || in_array($controller->action, $this->allowedActions)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($controller->params['url']['url'])) {
|
if (!isset($controller->params['url']['url'])) {
|
||||||
$url = '';
|
$url = '';
|
||||||
} else {
|
} else {
|
||||||
|
@ -774,6 +774,10 @@ class AuthComponent extends Object {
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
function hashPasswords($data) {
|
function hashPasswords($data) {
|
||||||
|
if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) {
|
||||||
|
return $this->authenticate->hashPasswords($data);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($data[$this->userModel])) {
|
if (isset($data[$this->userModel])) {
|
||||||
if (!empty($data[$this->userModel][$this->fields['username']]) && !empty($data[$this->userModel][$this->fields['password']])) {
|
if (!empty($data[$this->userModel][$this->fields['username']]) && !empty($data[$this->userModel][$this->fields['password']])) {
|
||||||
$data[$this->userModel][$this->fields['password']] = $this->password($data[$this->userModel][$this->fields['password']]);
|
$data[$this->userModel][$this->fields['password']] = $this->password($data[$this->userModel][$this->fields['password']]);
|
||||||
|
@ -790,7 +794,7 @@ class AuthComponent extends Object {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
function password($password) {
|
function password($password) {
|
||||||
return Security::hash(CAKE_SESSION_STRING . $password, $this->hash);
|
return Security::hash(CAKE_SESSION_STRING . $password);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Component shutdown. If user is logged in, wipe out redirect.
|
* Component shutdown. If user is logged in, wipe out redirect.
|
||||||
|
|
|
@ -35,13 +35,6 @@
|
||||||
* @subpackage cake.cake.libs.controller.components
|
* @subpackage cake.cake.libs.controller.components
|
||||||
*/
|
*/
|
||||||
class SecurityComponent extends Object {
|
class SecurityComponent extends Object {
|
||||||
/**
|
|
||||||
* Holds an instance of the core Security object
|
|
||||||
*
|
|
||||||
* @var object Security
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
var $Security = null;
|
|
||||||
/**
|
/**
|
||||||
* The controller method that will be called if this request is black-hole'd
|
* The controller method that will be called if this request is black-hole'd
|
||||||
*
|
*
|
||||||
|
@ -88,7 +81,7 @@ class SecurityComponent extends Object {
|
||||||
* @access public
|
* @access public
|
||||||
* @see SecurityComponent::requireLogin()
|
* @see SecurityComponent::requireLogin()
|
||||||
*/
|
*/
|
||||||
var $loginOptions = array('type' => '');
|
var $loginOptions = array('type' => '', 'prompt' => null);
|
||||||
/**
|
/**
|
||||||
* An associative array of usernames/passwords used for HTTP-authenticated logins.
|
* An associative array of usernames/passwords used for HTTP-authenticated logins.
|
||||||
* If using digest authentication, passwords should be MD5-hashed.
|
* If using digest authentication, passwords should be MD5-hashed.
|
||||||
|
@ -127,12 +120,6 @@ class SecurityComponent extends Object {
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
var $components = array('RequestHandler', 'Session');
|
var $components = array('RequestHandler', 'Session');
|
||||||
/**
|
|
||||||
* Security class initialization
|
|
||||||
*/
|
|
||||||
function initialize(&$controller) {
|
|
||||||
$this->Security =& Security::getInstance();
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Component startup. All security checking happens here.
|
* Component startup. All security checking happens here.
|
||||||
*
|
*
|
||||||
|
@ -196,6 +183,8 @@ class SecurityComponent extends Object {
|
||||||
*/
|
*/
|
||||||
function requireLogin() {
|
function requireLogin() {
|
||||||
$args = func_get_args();
|
$args = func_get_args();
|
||||||
|
$base = $this->loginOptions;
|
||||||
|
|
||||||
foreach ($args as $arg) {
|
foreach ($args as $arg) {
|
||||||
if (is_array($arg)) {
|
if (is_array($arg)) {
|
||||||
$this->loginOptions = $arg;
|
$this->loginOptions = $arg;
|
||||||
|
@ -203,6 +192,7 @@ class SecurityComponent extends Object {
|
||||||
$this->requireLogin[] = $arg;
|
$this->requireLogin[] = $arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->loginOptions = am($base, $this->loginOptions);
|
||||||
|
|
||||||
if (empty($this->requireLogin)) {
|
if (empty($this->requireLogin)) {
|
||||||
$this->requireLogin = array('*');
|
$this->requireLogin = array('*');
|
||||||
|
@ -213,41 +203,40 @@ class SecurityComponent extends Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Gets the login credentials for an HTTP-authenticated request
|
* Attempts to validate the login credentials for an HTTP-authenticated request
|
||||||
*
|
*
|
||||||
* @param string $type Either 'basic', 'digest', or null. If null/empty, will try both.
|
* @param string $type Either 'basic', 'digest', or null. If null/empty, will try both.
|
||||||
* @return mixed If successful, returns an array with login name and password, otherwise null.
|
* @return mixed If successful, returns an array with login name and password, otherwise null.
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
function loginCredentials($type = null) {
|
function loginCredentials($type = null) {
|
||||||
if (empty($type) || low($type) == 'basic') {
|
switch (low($type)) {
|
||||||
$login = array('username' => env('PHP_AUTH_USER'), 'password' => env('PHP_AUTH_PW'));
|
case 'basic':
|
||||||
|
$login = array('username' => env('PHP_AUTH_USER'), 'password' => env('PHP_AUTH_PW'));
|
||||||
if ($login['username'] != null) {
|
if (!empty($login['username'])) {
|
||||||
return $login;
|
return $login;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($type == '' || low($type) == 'digest') {
|
|
||||||
$digest = null;
|
|
||||||
|
|
||||||
if (version_compare(phpversion(), '5.1') != -1) {
|
|
||||||
$digest = env('PHP_AUTH_DIGEST');
|
|
||||||
} elseif (function_exists('apache_request_headers')) {
|
|
||||||
$headers = apache_request_headers();
|
|
||||||
if (isset($headers['Authorization']) && !empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') {
|
|
||||||
$digest = substr($headers['Authorization'], 7);
|
|
||||||
}
|
}
|
||||||
} else {
|
break;
|
||||||
// Server doesn't support digest-auth headers
|
case 'digest':
|
||||||
trigger_error(__('SecurityComponent::loginCredentials() - Server does not support digest authentication', true), E_USER_WARNING);
|
default:
|
||||||
return null;
|
$digest = null;
|
||||||
}
|
|
||||||
|
|
||||||
if ($digest == null) {
|
if (version_compare(phpversion(), '5.1') != -1) {
|
||||||
return null;
|
$digest = env('PHP_AUTH_DIGEST');
|
||||||
}
|
} elseif (function_exists('apache_request_headers')) {
|
||||||
$data = $this->parseDigestAuthData($digest);
|
$headers = apache_request_headers();
|
||||||
|
if (isset($headers['Authorization']) && !empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') {
|
||||||
|
$digest = substr($headers['Authorization'], 7);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Server doesn't support digest-auth headers
|
||||||
|
trigger_error(__('SecurityComponent::loginCredentials() - Server does not support digest authentication', true), E_USER_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($digest)) {
|
||||||
|
return $this->parseDigestAuthData($digest);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -261,8 +250,16 @@ class SecurityComponent extends Object {
|
||||||
function loginRequest($options = array()) {
|
function loginRequest($options = array()) {
|
||||||
$options = am($this->loginOptions, $options);
|
$options = am($this->loginOptions, $options);
|
||||||
$this->__setLoginDefaults($options);
|
$this->__setLoginDefaults($options);
|
||||||
$data = 'WWW-Authenticate: ' . ucfirst($options['type']) . ' realm="' . $options['realm'] . '"';
|
$auth = 'WWW-Authenticate: ' . ucfirst($options['type']);
|
||||||
return $data;
|
$out = array('realm="' . $options['realm'] . '"');
|
||||||
|
|
||||||
|
if (low($options['type']) == 'digest') {
|
||||||
|
$out[] = 'qop="auth"';
|
||||||
|
$out[] = 'nonce="' . uniqid() . '"'; //str_replace('-', '', String::uuid())
|
||||||
|
$out[] = 'opaque="' . md5($options['realm']).'"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $auth . ' ' . join(',', $out);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Parses an HTTP digest authentication response, and returns an array of the data, or null on failure.
|
* Parses an HTTP digest authentication response, and returns an array of the data, or null on failure.
|
||||||
|
@ -291,6 +288,21 @@ class SecurityComponent extends Object {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Generates a hash to be compared with an HTTP digest-authenticated response
|
||||||
|
*
|
||||||
|
* @param array $data HTTP digest response data, as parsed by SecurityComponent::parseDigestAuthData()
|
||||||
|
* @return string Digest authentication hash
|
||||||
|
* @access public
|
||||||
|
* @see SecurityComponent::parseDigestAuthData()
|
||||||
|
*/
|
||||||
|
function generateDigestResponseHash($data) {
|
||||||
|
return md5(
|
||||||
|
md5($data['username'] . ':' . $this->loginOptions['realm'] . ':' . $this->loginUsers[$data['username']]) .
|
||||||
|
':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' .
|
||||||
|
md5(env('REQUEST_METHOD') . ':' . $data['uri'])
|
||||||
|
);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Black-hole an invalid request with a 404 error or custom callback
|
* Black-hole an invalid request with a 404 error or custom callback
|
||||||
*
|
*
|
||||||
|
@ -398,7 +410,7 @@ class SecurityComponent extends Object {
|
||||||
// User hasn't been authenticated yet
|
// User hasn't been authenticated yet
|
||||||
header($this->loginRequest());
|
header($this->loginRequest());
|
||||||
|
|
||||||
if (isset($this->loginOptions['prompt'])) {
|
if (!empty($this->loginOptions['prompt'])) {
|
||||||
$this->__callback($controller, $this->loginOptions['prompt']);
|
$this->__callback($controller, $this->loginOptions['prompt']);
|
||||||
} else {
|
} else {
|
||||||
$this->blackHole($controller, 'login');
|
$this->blackHole($controller, 'login');
|
||||||
|
@ -409,6 +421,12 @@ class SecurityComponent extends Object {
|
||||||
} else {
|
} else {
|
||||||
if (low($this->loginOptions['type']) == 'digest') {
|
if (low($this->loginOptions['type']) == 'digest') {
|
||||||
// Do digest authentication
|
// Do digest authentication
|
||||||
|
if ($login && isset($this->loginUsers[$login['username']])) {
|
||||||
|
if ($login['response'] == $this->generateDigestResponseHash($login)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->blackHole($controller, 'login');
|
||||||
} else {
|
} else {
|
||||||
if (!(in_array($login['username'], array_keys($this->loginUsers)) && $this->loginUsers[$login['username']] == $login['password'])) {
|
if (!(in_array($login['username'], array_keys($this->loginUsers)) && $this->loginUsers[$login['username']] == $login['password'])) {
|
||||||
$this->blackHole($controller, 'login');
|
$this->blackHole($controller, 'login');
|
||||||
|
@ -579,11 +597,12 @@ class SecurityComponent extends Object {
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
function __setLoginDefaults(&$options) {
|
function __setLoginDefaults(&$options) {
|
||||||
$options = am(array('type' => 'basic',
|
$options = am(array(
|
||||||
'realm' => env('SERVER_NAME'),
|
'type' => 'basic',
|
||||||
'qop' => 'auth',
|
'realm' => env('SERVER_NAME'),
|
||||||
'nonce' => String::uuid()),
|
'qop' => 'auth',
|
||||||
array_filter($options));
|
'nonce' => String::uuid()
|
||||||
|
), array_filter($options));
|
||||||
$options = am(array('opaque' => md5($options['realm'])), $options);
|
$options = am(array('opaque' => md5($options['realm'])), $options);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,7 +34,15 @@
|
||||||
* @package cake
|
* @package cake
|
||||||
* @subpackage cake.cake.libs
|
* @subpackage cake.cake.libs
|
||||||
*/
|
*/
|
||||||
class Security extends Object{
|
class Security extends Object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default hash method
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
var $hashType = null;
|
||||||
/**
|
/**
|
||||||
* Singleton implementation to get object instance.
|
* Singleton implementation to get object instance.
|
||||||
*
|
*
|
||||||
|
@ -106,10 +114,14 @@ class Security extends Object{
|
||||||
* @access public
|
* @access public
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function hash($string, $type = 'sha1') {
|
function hash($string, $type = null) {
|
||||||
$_this =& Security::getInstance();
|
$_this =& Security::getInstance();
|
||||||
|
if (empty($type)) {
|
||||||
|
$type = $_this->hashType;
|
||||||
|
}
|
||||||
$type = strtolower($type);
|
$type = strtolower($type);
|
||||||
if ($type == 'sha1') {
|
|
||||||
|
if ($type == 'sha1' || $type == null) {
|
||||||
if (function_exists('sha1')) {
|
if (function_exists('sha1')) {
|
||||||
$return = sha1($string);
|
$return = sha1($string);
|
||||||
return $return;
|
return $return;
|
||||||
|
@ -132,6 +144,20 @@ class Security extends Object{
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Sets the default hash method for the Security object. This affects all objects using
|
||||||
|
* Security::hash().
|
||||||
|
*
|
||||||
|
* @param string $hash Method to use (sha1/sha256/md5)
|
||||||
|
* @return void
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @see Security::hash()
|
||||||
|
*/
|
||||||
|
function setHash($hash) {
|
||||||
|
$_this =& Security::getInstance();
|
||||||
|
$_this->hashType = $hash;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Encripts/Decrypts a text using the given key.
|
* Encripts/Decrypts a text using the given key.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Add table
Reference in a new issue