Merge branch '2.0-auth' into 2.0

This commit is contained in:
mark_story 2011-02-14 22:45:39 -05:00
commit c638443b4d
20 changed files with 2608 additions and 1859 deletions

View file

@ -21,6 +21,9 @@
App::import('Core', 'Router', false);
App::import('Core', 'Security', false);
App::import('Core', 'CakeSession', false);
App::import('Component', 'auth/base_authorize');
App::import('Component', 'auth/base_authenticate');
/**
* Authentication control component class
@ -32,6 +35,8 @@ App::import('Core', 'Security', false);
*/
class AuthComponent extends Component {
const ALL = 'all';
/**
* Maintains current user login state.
*
@ -47,26 +52,86 @@ class AuthComponent extends Component {
public $components = array('Session', 'RequestHandler');
/**
* A reference to the object used for authentication
* An array of authentication objects to use for authenticating users. You can configure
* multiple adapters and they will be checked sequentially when users are identified.
*
* @var object
* {{{
* $this->Auth->authenticate = array(
* 'Form' => array(
* 'userModel' => 'Users.User'
* )
* );
* }}}
*
* Using the class name without 'Authenticate' as the key, you can pass in an array of settings for each
* authentication object. Additionally you can define settings that should be set to all authentications objects
* using the 'all' key:
*
* {{{
* $this->Auth->authenticate = array(
* 'all' => array(
* 'userModel' => 'Users.User',
* 'scope' => array('User.active' => 1)
* ),
* 'Form',
* 'Basic'
* );
* }}}
*
* You can also use AuthComponent::ALL instead of the string 'all'.
*
* @var array
* @link http://book.cakephp.org/view/1278/authenticate
*/
public $authenticate = null;
public $authenticate = array('Form');
/**
* The name of the component to use for Authorization or set this to
* 'controller' will validate against Controller::isAuthorized()
* 'actions' will validate Controller::action against an AclComponent::check()
* 'crud' will validate mapActions against an AclComponent::check()
* array('model'=> 'name'); will validate mapActions against model $name::isAuthorized(user, controller, mapAction)
* 'object' will validate Controller::action against object::isAuthorized(user, controller, action)
* Objects that will be used for authentication checks.
*
* @var array
*/
protected $_authenticateObjects = array();
/**
* An array of authorization objects to use for authorizing users. You can configure
* multiple adapters and they will be checked sequentially when authorization checks are done.
*
* {{{
* $this->Auth->authorize = array(
* 'Crud' => array(
* 'actionPath' => 'controllers/'
* )
* );
* }}}
*
* Using the class name without 'Authorize' as the key, you can pass in an array of settings for each
* authorization object. Additionally you can define settings that should be set to all authorization objects
* using the 'all' key:
*
* {{{
* $this->Auth->authorize = array(
* 'all' => array(
* 'actionPath' => 'controllers/'
* ),
* 'Crud',
* 'CustomAuth'
* );
* }}}
*
* You can also use AuthComponent::ALL instead of the string 'all'
*
* @var mixed
* @link http://book.cakephp.org/view/1275/authorize
*/
public $authorize = false;
/**
* Objects that will be used for authorization checks.
*
* @var array
*/
protected $_authorizeObjects = array();
/**
* The name of an optional view element to render when an Ajax request is made
* with an invalid or expired session
@ -77,66 +142,42 @@ class AuthComponent extends Component {
public $ajaxLogin = null;
/**
* The name of the element used for SessionComponent::setFlash
* Settings to use when Auth needs to do a flash message with SessionComponent::setFlash().
* Available keys are:
*
* @var string
*/
public $flashElement = 'default';
/**
* The name of the model that represents users which will be authenticated. Defaults to 'User'.
*
* @var string
* @link http://book.cakephp.org/view/1266/userModel
*/
public $userModel = 'User';
/**
* Additional query conditions to use when looking up and authenticating users,
* i.e. array('User.is_active' => 1).
* - `element` - The element to use, defaults to 'default'.
* - `key` - The key to use, defaults to 'auth'
* - `params` - The array of additional params to use, defaults to array()
*
* @var array
* @link http://book.cakephp.org/view/1268/userScope
*/
public $userScope = array();
/**
* Allows you to specify non-default login name and password fields used in
* $userModel, i.e. array('username' => 'login_name', 'password' => 'passwd').
*
* @var array
* @link http://book.cakephp.org/view/1267/fields
*/
public $fields = array('username' => 'username', 'password' => 'password');
public $flash = array(
'element' => 'default',
'key' => 'auth',
'params' => array()
);
/**
* 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.User".
*
* @var string
* @link http://book.cakephp.org/view/1276/sessionKey
*/
public $sessionKey = null;
/**
* If using action-based access control, this defines how the paths to action
* ACO nodes is computed. If, for example, all controller nodes are nested
* under an ACO node named 'Controllers', $actionPath should be set to
* "Controllers/".
*
* @var string
* @link http://book.cakephp.org/view/1279/actionPath
*/
public $actionPath = null;
public static $sessionKey = 'Auth.User';
/**
* A URL (defined as a string or array) to the controller action that handles
* logins.
* logins. Defaults to `/users/login`
*
* @var mixed
* @link http://book.cakephp.org/view/1269/loginAction
*/
public $loginAction = null;
public $loginAction = array(
'controller' => 'users',
'action' => 'login',
'plugin' => null
);
/**
* Normally, if a user is redirected to the $loginAction page, the location they
@ -161,22 +202,6 @@ class AuthComponent extends Component {
*/
public $logoutRedirect = null;
/**
* The name of model or model object, or any other object has an isAuthorized method.
*
* @var string
*/
public $object = null;
/**
* Error to display when user login fails. For security purposes, only one error is used for all
* login failures, so as not to expose information on why the login failed.
*
* @var string
* @link http://book.cakephp.org/view/1272/loginError
*/
public $loginError = null;
/**
* Error to display when user attempts to access an object or action to which they do not have
* acccess.
@ -186,14 +211,6 @@ class AuthComponent extends Component {
*/
public $authError = null;
/**
* Determines whether AuthComponent will automatically redirect and exit if login is successful.
*
* @var boolean
* @link http://book.cakephp.org/view/1274/autoRedirect
*/
public $autoRedirect = true;
/**
* Controller actions for which user validation is not required.
*
@ -203,20 +220,6 @@ class AuthComponent extends Component {
*/
public $allowedActions = array();
/**
* Maps actions to CRUD operations. Used for controller-based validation ($validate = 'controller').
*
* @var array
* @see AuthComponent::mapActions()
*/
public $actionMap = array(
'index' => 'read',
'add' => 'create',
'edit' => 'update',
'view' => 'read',
'remove' => 'delete'
);
/**
* Request object
*
@ -225,27 +228,11 @@ class AuthComponent extends Component {
public $request;
/**
* Form data from Controller::$data
* Response object
*
* @deprecated Use $this->request->data instead
* @var array
* @var CakeResponse
*/
public $data = array();
/**
* Parameter data from Controller::$params
*
* @deprecated Use $this->request instead
* @var array
*/
public $params = array();
/**
* AclComponent instance if using Acl + Auth
*
* @var AclComponent
*/
public $Acl;
public $response;
/**
* Method list for bound controller
@ -262,28 +249,9 @@ class AuthComponent extends Component {
*/
public function initialize($controller) {
$this->request = $controller->request;
$this->params = $this->request;
$crud = array('create', 'read', 'update', 'delete');
$this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
$this->response = $controller->response;
$this->_methods = $controller->methods;
$prefixes = Router::prefixes();
if (!empty($prefixes)) {
foreach ($prefixes as $prefix) {
$this->actionMap = array_merge($this->actionMap, array(
$prefix . '_index' => 'read',
$prefix . '_add' => 'create',
$prefix . '_edit' => 'update',
$prefix . '_view' => 'read',
$prefix . '_remove' => 'delete',
$prefix . '_create' => 'create',
$prefix . '_read' => 'read',
$prefix . '_update' => 'update',
$prefix . '_delete' => 'delete'
));
}
}
if (Configure::read('debug') > 0) {
App::import('Debugger');
Debugger::checkSecurityKeys();
@ -298,11 +266,7 @@ class AuthComponent extends Component {
* @return boolean
*/
public function startup($controller) {
$isErrorOrTests = (
strtolower($controller->name) == 'cakeerror' ||
(strtolower($controller->name) == 'tests' && Configure::read('debug') > 0)
);
if ($isErrorOrTests) {
if ($controller->name == 'CakeError') {
return true;
}
@ -322,8 +286,7 @@ class AuthComponent extends Component {
return false;
}
$request = $controller->request;
$this->request->data = $controller->request->data = $this->hashPasswords($request->data);
$url = '';
if (isset($request->query['url'])) {
@ -343,47 +306,16 @@ class AuthComponent extends Component {
}
if ($loginAction == $url) {
$model = $this->getModel();
if (empty($request->data) || !isset($request->data[$model->alias])) {
if (empty($request->data)) {
if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) {
$this->Session->write('Auth.redirect', $controller->referer(null, true));
}
return false;
}
$isValid = !empty($request->data[$model->alias][$this->fields['username']]) &&
!empty($request->data[$model->alias][$this->fields['password']]);
if ($isValid) {
$username = $request->data[$model->alias][$this->fields['username']];
$password = $request->data[$model->alias][$this->fields['password']];
$data = array(
$model->alias . '.' . $this->fields['username'] => $username,
$model->alias . '.' . $this->fields['password'] => $password
);
if ($this->login($data)) {
if ($this->autoRedirect) {
$controller->redirect($this->redirect(), null, true);
}
return true;
}
}
$this->Session->setFlash($this->loginError, $this->flashElement, array(), 'auth');
$request->data[$model->alias][$this->fields['password']] = null;
return false;
} else {
if (!$this->user()) {
if (!$this->_getUser()) {
if (!$request->is('ajax')) {
$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
if (!empty($request->query) && count($request->query) >= 2) {
$query = $request->query;
unset($query['url'], $query['ext']);
$url .= Router::queryString($query, array());
}
$this->Session->write('Auth.redirect', $url);
$this->flash($this->authError);
$this->Session->write('Auth.redirect', Router::reverse($request));
$controller->redirect($loginAction);
return false;
} elseif (!empty($this->ajaxLogin)) {
@ -397,49 +329,11 @@ class AuthComponent extends Component {
}
}
if (!$this->authorize) {
if (empty($this->authorize) || $this->isAuthorized()) {
return true;
}
extract($this->__authType());
switch ($type) {
case 'controller':
$this->object = $controller;
break;
case 'crud':
case 'actions':
if (isset($controller->Acl)) {
$this->Acl = $controller->Acl;
} else {
trigger_error(__('Could not find AclComponent. Please include Acl in Controller::$components.'), E_USER_WARNING);
}
break;
case 'model':
if (!isset($object)) {
$hasModel = (
isset($controller->{$controller->modelClass}) &&
is_object($controller->{$controller->modelClass})
);
$isUses = (
!empty($controller->uses) && isset($controller->{$controller->uses[0]}) &&
is_object($controller->{$controller->uses[0]})
);
if ($hasModel) {
$object = $controller->modelClass;
} elseif ($isUses) {
$object = $controller->uses[0];
}
}
$type = array('model' => $object);
break;
}
if ($this->isAuthorized($type)) {
return true;
}
$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
$this->flash($this->authError);
$controller->redirect($controller->referer(), null, true);
return false;
}
@ -453,20 +347,8 @@ class AuthComponent extends Component {
* @access private
*/
function __setDefaults() {
if (empty($this->userModel)) {
trigger_error(__("Could not find \$userModel. Please set AuthComponent::\$userModel in beforeFilter()."), E_USER_WARNING);
return false;
}
list($plugin, $model) = pluginSplit($this->userModel);
$defaults = array(
'loginAction' => array(
'controller' => Inflector::underscore(Inflector::pluralize($model)),
'action' => 'login',
'plugin' => Inflector::underscore($plugin),
),
'sessionKey' => 'Auth.' . $model,
'logoutRedirect' => $this->loginAction,
'loginError' => __('Login failed. Invalid username or password.'),
'authError' => __('You are not authorized to access that location.')
);
foreach ($defaults as $key => $value) {
@ -478,119 +360,77 @@ class AuthComponent extends Component {
}
/**
* Determines whether the given user is authorized to perform an action. The type of
* authorization used is based on the value of AuthComponent::$authorize or the
* passed $type param.
* Uses the configured Authorization adapters to check whether or not a user is authorized.
* Each adapter will be checked in sequence, if any of them return true, then the user will
* be authorized for the request.
*
* Types:
* 'controller' will validate against Controller::isAuthorized() if controller instance is
* passed in $object
* 'actions' will validate Controller::action against an AclComponent::check()
* 'crud' will validate mapActions against an AclComponent::check()
* array('model'=> 'name'); will validate mapActions against model
* $name::isAuthorized(user, controller, mapAction)
* 'object' will validate Controller::action against
* object::isAuthorized(user, controller, action)
*
* @param string $type Type of authorization
* @param mixed $object object, model object, or model name
* @param mixed $user The user to check the authorization of
* @param mixed $user The user to check the authorization of. If empty the user in the session will be used.
* @param CakeRequest $request The request to authenticate for. If empty, the current request will be used.
* @return boolean True if $user is authorized, otherwise false
*/
public function isAuthorized($type = null, $object = null, $user = null) {
public function isAuthorized($user = null, $request = null) {
if (empty($user) && !$this->user()) {
return false;
} elseif (empty($user)) {
$user = $this->user();
}
extract($this->__authType($type));
if (!$object) {
$object = $this->object;
if (empty($request)) {
$request = $this->request;
}
$valid = false;
switch ($type) {
case 'controller':
$valid = $object->isAuthorized();
break;
case 'actions':
$valid = $this->Acl->check($user, $this->action());
break;
case 'crud':
if (!isset($this->actionMap[$this->request['action']])) {
trigger_error(
__('Auth::startup() - Attempted access of un-mapped action "%1$s" in controller "%2$s"', $this->request['action'], $this->request['controller']),
E_USER_WARNING
);
} else {
$valid = $this->Acl->check(
$user,
$this->action(':controller'),
$this->actionMap[$this->request['action']]
);
}
break;
case 'model':
$action = $this->request['action'];
if (isset($this->actionMap[$action])) {
$action = $this->actionMap[$action];
}
if (is_string($object)) {
$object = $this->getModel($object);
}
case 'object':
if (!isset($action)) {
$action = $this->action(':action');
}
if (empty($object)) {
trigger_error(__('Could not find %s. Set AuthComponent::$object in beforeFilter() or pass a valid object', get_class($object)), E_USER_WARNING);
return;
}
if (method_exists($object, 'isAuthorized')) {
$valid = $object->isAuthorized($user, $this->action(':controller'), $action);
} elseif ($object) {
trigger_error(__('%s::isAuthorized() is not defined.', get_class($object)), E_USER_WARNING);
}
break;
case null:
case false:
if (empty($this->_authorizeObjects)) {
$this->constructAuthorize();
}
foreach ($this->_authorizeObjects as $authorizer) {
if ($authorizer->authorize($user, $request) === true) {
return true;
break;
default:
trigger_error(__('Auth::isAuthorized() - $authorize is set to an incorrect value. Allowed settings are: "actions", "crud", "model" or null.'), E_USER_WARNING);
break;
}
}
return $valid;
return false;
}
/**
* Get authorization type
* Loads the authorization objects configured.
*
* @param string $auth Type of authorization
* @return array Associative array with: type, object
* @access private
* @return mixed Either null when authorize is empty, or the loaded authorization objects.
*/
function __authType($auth = null) {
if ($auth == null) {
$auth = $this->authorize;
public function constructAuthorize() {
if (empty($this->authorize)) {
return;
}
$object = null;
if (is_array($auth)) {
$type = key($auth);
$object = $auth[$type];
} else {
$type = $auth;
return compact('type');
$this->_authorizeObjects = array();
$config = Set::normalize($this->authorize);
$global = array();
if (isset($config[AuthComponent::ALL])) {
$global = $config[AuthComponent::ALL];
unset($config[AuthComponent::ALL]);
}
return compact('type', 'object');
foreach ($config as $class => $settings) {
$className = $class . 'Authorize';
if (!class_exists($className) && !App::import('Component', 'auth/' . $class . '_authorize')) {
throw new CakeException(__('Authorization adapter "%s" was not found.', $class));
}
if (!method_exists($className, 'authorize')) {
throw new CakeException(__('Authorization objects must implement an authorize method.'));
}
$settings = array_merge($global, (array)$settings);
$this->_authorizeObjects[] = new $className($this->_Collection->getController(), $settings);
}
return $this->_authorizeObjects;
}
/**
* Takes a list of actions in the current controller for which authentication is not required, or
* no parameters to allow all actions.
*
* You can use allow with either an array, or var args.
*
* `$this->Auth->allow(array('edit', 'add'));` or
* `$this->Auth->allow('edit', 'add');`
*
* allow() also supports '*' as a wildcard to mean all actions.
*
* `$this->Auth->allow('*');`
*
* @param mixed $action Controller action name or array of actions
* @param string $action Controller action name
* @param string ... etc.
@ -610,7 +450,12 @@ class AuthComponent extends Component {
}
/**
* Removes items from the list of allowed actions.
* Removes items from the list of allowed/no authentication required actions.
*
* You can use deny with either an array, or var args.
*
* `$this->Auth->deny(array('edit', 'add'));` or
* `$this->Auth->deny('edit', 'add');`
*
* @param mixed $action Controller action name or array of actions
* @param string $action Controller action name
@ -634,47 +479,41 @@ class AuthComponent extends Component {
}
/**
* Maps action names to CRUD operations. Used for controller-based authentication.
* Maps action names to CRUD operations. Used for controller-based authentication. Make sure
* to configure the authorize property before calling this method. As it delegates $map to all the
* attached authorize objects.
*
* @param array $map Actions to map
* @return void
* @link http://book.cakephp.org/view/1260/mapActions
*/
public function mapActions($map = array()) {
$crud = array('create', 'read', 'update', 'delete');
foreach ($map as $action => $type) {
if (in_array($action, $crud) && is_array($type)) {
foreach ($type as $typedAction) {
$this->actionMap[$typedAction] = $action;
}
} else {
$this->actionMap[$action] = $type;
}
if (empty($this->_authorizeObjects)) {
$this->constructAuthorize();
}
foreach ($this->_authorizeObjects as $auth) {
$auth->mapActions($map);
}
}
/**
* Manually log-in a user with the given parameter data. The $data provided can be any data
* structure used to identify a user in AuthComponent::identify(). If $data is empty or not
* specified, POST data from Controller::$data will be used automatically.
* Log a user in. If a $user is provided that data will be stored as the logged in user. If `$user` is empty or not
* specified, the request will be used to identify a user. If the identification was successful,
* the user record is written to the session key specified in AuthComponent::$sessionKey.
*
* After (if) login is successful, the user record is written to the session key specified in
* AuthComponent::$sessionKey.
*
* @param mixed $data User object
* @param mixed $user Either an array of user data, or null to identify a user using the current request.
* @return boolean True on login success, false on failure
* @link http://book.cakephp.org/view/1261/login
*/
public function login($data = null) {
public function login($user = null) {
$this->__setDefaults();
$this->_loggedIn = false;
if (empty($data)) {
$data = $this->data;
if (empty($user)) {
$user = $this->identify($this->request, $this->response);
}
if ($user = $this->identify($data)) {
$this->Session->write($this->sessionKey, $user);
if ($user) {
$this->Session->write(self::$sessionKey, $user);
$this->_loggedIn = true;
}
return $this->_loggedIn;
@ -690,7 +529,7 @@ class AuthComponent extends Component {
*/
public function logout() {
$this->__setDefaults();
$this->Session->delete($this->sessionKey);
$this->Session->delete(self::$sessionKey);
$this->Session->delete('Auth.redirect');
$this->_loggedIn = false;
return Router::normalize($this->logoutRedirect);
@ -703,17 +542,15 @@ class AuthComponent extends Component {
* @return mixed User record. or null if no user is logged in.
* @link http://book.cakephp.org/view/1264/user
*/
public function user($key = null) {
$this->__setDefaults();
if (!$this->Session->check($this->sessionKey)) {
public static function user($key = null) {
if (!CakeSession::check(self::$sessionKey)) {
return null;
}
if ($key == null) {
$model = $this->getModel();
return array($model->alias => $this->Session->read($this->sessionKey));
return CakeSession::read(self::$sessionKey);
} else {
$user = $this->Session->read($this->sessionKey);
$user = CakeSession::read(self::$sessionKey);
if (isset($user[$key])) {
return $user[$key];
}
@ -722,7 +559,32 @@ class AuthComponent extends Component {
}
/**
* If no parameter is passed, gets the authentication redirect URL.
* Similar to AuthComponent::user() except if the session user cannot be found, connected authentication
* objects will have their getUser() methods called. This lets stateless authentication methods function correctly.
*
* @return boolean true if a user can be found, false if one cannot.
*/
protected function _getUser() {
$user = $this->user();
if ($user) {
return true;
}
if (empty($this->_authenticateObjects)) {
$this->constructAuthenticate();
}
foreach ($this->_authenticateObjects as $auth) {
$result = $auth->getUser($this->request);
if (!empty($result) && is_array($result)) {
return true;
}
}
return false;
}
/**
* If no parameter is passed, gets the authentication redirect URL. Pass a url in to
* set the destination a user should be redirected to upon logging in. Will fallback to
* AuthComponent::$loginRedirect if there is no stored redirect value.
*
* @param mixed $url Optional URL to write as the login redirect URL.
* @return string Redirect URL
@ -745,167 +607,53 @@ class AuthComponent extends Component {
}
/**
* Validates a user against an abstract object.
* Use the configured authentication adapters, and attempt to identify the user
* by credentials contained in $request.
*
* @param mixed $object The object to validate the user against.
* @param mixed $user Optional. The identity of the user to be validated.
* Uses the current user session if none specified. For
* valid forms of identifying users, see
* AuthComponent::identify().
* @param string $action Optional. The action to validate against.
* @see AuthComponent::identify()
* @return boolean True if the user validates, false otherwise.
* @param CakeRequest $request The request that contains authentication data.
* @return array User record data, or false, if the user could not be identified.
*/
public function validate($object, $user = null, $action = null) {
if (empty($user)) {
$user = $this->user();
public function identify(CakeRequest $request, CakeResponse $response) {
if (empty($this->_authenticateObjects)) {
$this->constructAuthenticate();
}
if (empty($user)) {
return false;
foreach ($this->_authenticateObjects as $auth) {
$result = $auth->authenticate($request, $response);
if (!empty($result) && is_array($result)) {
return $result;
}
}
return $this->Acl->check($user, $object, $action);
return false;
}
/**
* Returns the path to the ACO node bound to a controller/action.
* loads the configured authentication objects.
*
* @param string $action Optional. The controller/action path to validate the
* user against. The current request action is used if
* none is specified.
* @return boolean ACO node path
* @link http://book.cakephp.org/view/1256/action
* @return mixed either null on empty authenticate value, or an array of loaded objects.
*/
public function action($action = ':plugin/:controller/:action') {
$plugin = empty($this->request['plugin']) ? null : Inflector::camelize($this->request['plugin']) . '/';
return str_replace(
array(':controller', ':action', ':plugin/'),
array(Inflector::camelize($this->request['controller']), $this->request['action'], $plugin),
$this->actionPath . $action
);
}
/**
* Returns a reference to the model object specified, and attempts
* to load it if it is not found.
*
* @param string $name Model name (defaults to AuthComponent::$userModel)
* @return object A reference to a model object
*/
public function &getModel($name = null) {
$model = null;
if (!$name) {
$name = $this->userModel;
public function constructAuthenticate() {
if (empty($this->authenticate)) {
return;
}
$model = ClassRegistry::init($name);
if (empty($model)) {
trigger_error(__('Auth::getModel() - Model is not set or could not be found'), E_USER_WARNING);
return null;
$this->_authenticateObjects = array();
$config = Set::normalize($this->authenticate);
$global = array();
if (isset($config[AuthComponent::ALL])) {
$global = $config[AuthComponent::ALL];
unset($config[AuthComponent::ALL]);
}
return $model;
}
/**
* Identifies a user based on specific criteria.
*
* @param mixed $user Optional. The identity of the user to be validated.
* Uses the current user session if none specified.
* @param array $conditions Optional. Additional conditions to a find.
* @return array User record data, or null, if the user could not be identified.
*/
public function identify($user = null, $conditions = null) {
if ($conditions === false) {
$conditions = null;
} elseif (is_array($conditions)) {
$conditions = array_merge((array)$this->userScope, $conditions);
} else {
$conditions = $this->userScope;
}
$model = $this->getModel();
if (empty($user)) {
$user = $this->user();
if (empty($user)) {
return null;
foreach ($config as $class => $settings) {
$className = $class . 'Authenticate';
if (!class_exists($className) && !App::import('Component', 'auth/' . $class . '_authenticate')) {
throw new CakeException(__('Authentication adapter "%s" was not found.', $class));
}
} elseif (is_object($user) && is_a($user, 'Model')) {
if (!$user->exists()) {
return null;
if (!method_exists($className, 'authenticate')) {
throw new CakeException(__('Authentication objects must implement an authenticate method.'));
}
$user = $user->read();
$user = $user[$model->alias];
} elseif (is_array($user) && isset($user[$model->alias])) {
$user = $user[$model->alias];
$settings = array_merge($global, (array)$settings);
$this->_authenticateObjects[] = new $className($settings);
}
if (is_array($user) && (isset($user[$this->fields['username']]) || isset($user[$model->alias . '.' . $this->fields['username']]))) {
if (isset($user[$this->fields['username']]) && !empty($user[$this->fields['username']]) && !empty($user[$this->fields['password']])) {
if (trim($user[$this->fields['username']]) == '=' || trim($user[$this->fields['password']]) == '=') {
return false;
}
$find = array(
$model->alias.'.'.$this->fields['username'] => $user[$this->fields['username']],
$model->alias.'.'.$this->fields['password'] => $user[$this->fields['password']]
);
} elseif (isset($user[$model->alias . '.' . $this->fields['username']]) && !empty($user[$model->alias . '.' . $this->fields['username']])) {
if (trim($user[$model->alias . '.' . $this->fields['username']]) == '=' || trim($user[$model->alias . '.' . $this->fields['password']]) == '=') {
return false;
}
$find = array(
$model->alias.'.'.$this->fields['username'] => $user[$model->alias . '.' . $this->fields['username']],
$model->alias.'.'.$this->fields['password'] => $user[$model->alias . '.' . $this->fields['password']]
);
} else {
return false;
}
$data = $model->find('first', array(
'conditions' => array_merge($find, $conditions),
'recursive' => 0
));
if (empty($data) || empty($data[$model->alias])) {
return null;
}
} elseif (!empty($user) && is_string($user)) {
$data = $model->find('first', array(
'conditions' => array_merge(array($model->escapeField() => $user), $conditions),
));
if (empty($data) || empty($data[$model->alias])) {
return null;
}
}
if (!empty($data)) {
if (!empty($data[$model->alias][$this->fields['password']])) {
unset($data[$model->alias][$this->fields['password']]);
}
return $data[$model->alias];
}
return null;
}
/**
* Hash any passwords found in $data using $userModel and $fields['password']
*
* @param array $data Set of data to look for passwords
* @return array Data with passwords hashed
* @link http://book.cakephp.org/view/1259/hashPasswords
*/
public function hashPasswords($data) {
if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) {
return $this->authenticate->hashPasswords($data);
}
if (is_array($data)) {
$model = $this->getModel();
if(isset($data[$model->alias])) {
if (isset($data[$model->alias][$this->fields['username']]) && isset($data[$model->alias][$this->fields['password']])) {
$data[$model->alias][$this->fields['password']] = $this->password($data[$model->alias][$this->fields['password']]);
}
}
}
return $data;
return $this->_authenticateObjects;
}
/**
@ -915,7 +663,7 @@ class AuthComponent extends Component {
* @return string Hashed password
* @link http://book.cakephp.org/view/1263/password
*/
public function password($password) {
public static function password($password) {
return Security::hash($password, null, true);
}
@ -943,4 +691,14 @@ class AuthComponent extends Component {
}
return $this->_loggedIn;
}
/**
* Set a flash message. Uses the Session component, and values from AuthComponent::$flash.
*
* @param string $message The message to set.
* @return void
*/
public function flash($message) {
$this->Session->setFlash($message, $this->flash['element'], $this->flash['params'], $this->flash['key']);
}
}

View file

@ -0,0 +1,39 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/base_authorize');
/**
* An authorization adapter for AuthComponent. Provides the ability to authorize using the AclComponent,
* If AclComponent is not already loaded it will be loaded using the Controller's ComponentCollection.
*
* @package cake.libs.controller.components.auth
* @since 2.0
* @see AuthComponent::$authenticate
* @see AclComponent::check()
*/
class ActionsAuthorize extends BaseAuthorize {
/**
* Authorize a user using the AclComponent.
*
* @param array $user The user to authorize
* @param CakeRequest $request The request needing authorization.
* @return boolean
*/
public function authorize($user, CakeRequest $request) {
$Acl = $this->_controller->Components->load('Acl');
return $Acl->check($user, $this->action($request));
}
}

View file

@ -0,0 +1,101 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Core', 'Security');
/**
* Base Authentication class with common methods and properties.
*
* @package cake.libs.controller.components.auth
*/
abstract class BaseAuthenticate {
/**
* Settings for this object.
*
* - `fields` The fields to use to identify a user by.
* - `userModel` The model name of the User, defaults to User.
* - `scope` Additional conditions to use when looking up and authenticating users,
* i.e. `array('User.is_active' => 1).`
*
* @var array
*/
public $settings = array(
'fields' => array(
'username' => 'username',
'password' => 'password'
),
'userModel' => 'User',
'scope' => array()
);
/**
* Constructor
*
* @param array $settings Array of settings to use.
*/
public function __construct($settings) {
$this->settings = Set::merge($this->settings, $settings);
}
/**
* Find a user record using the standard options.
*
* @param string $username The username/identifier.
* @param string $password The unhashed password.
* @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,
$model . '.' . $fields['password'] => AuthComponent::password($password),
);
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;
}
unset($result[$model][$fields['password']]);
return $result[$model];
}
/**
* Authenticate a user based on the request information.
*
* @param CakeRequest $request Request to get authentication information from.
* @param CakeResponse $response A response object that can have headers added.
* @return mixed Either false on failure, or an array of user data on success.
*/
abstract public function authenticate(CakeRequest $request, CakeResponse $response);
/**
* Get a user based on information in the request. Primarily used by stateless authentication
* systems like basic and digest auth.
*
* @param CakeRequest $request Request object.
* @return mixed Either false or an array of user information
*/
public function getUser($request) {
return false;
}
}

View file

@ -0,0 +1,126 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Abstract base authorization adapter for AuthComponent.
*
* @package cake.libs.controller.components.auth
* @since 2.0
* @see AuthComponent::$authenticate
*/
abstract class BaseAuthorize {
/**
* Controller for the request.
*
* @var Controller
*/
protected $_controller = null;
/**
* Settings for authorize objects.
*
* - `actionPath` - The path to ACO nodes that contains the nodes for controllers. Used as a prefix
* when calling $this->action();
* - `actionMap` - Action -> crud mappings. Used by authorization objects that want to map actions to CRUD roles.
*
* @var array
*/
public $settings = array(
'actionPath' => null,
'actionMap' => array(
'index' => 'read',
'add' => 'create',
'edit' => 'update',
'view' => 'read',
'delete' => 'delete',
'remove' => 'delete'
)
);
/**
* Constructor
*
* @param Controller $controller The controller for this request.
* @param string $settings An array of settings. This class does not use any settings.
*/
public function __construct(Controller $controller, $settings = array()) {
$this->controller($controller);
$this->settings = Set::merge($this->settings, $settings);
}
/**
* Checks user authorization.
*
* @param array $user Active user data
* @param CakeRequest $request
* @return boolean
*/
abstract public function authorize($user, CakeRequest $request);
/**
* Accessor to the controller object.
*
* @param mixed $controller null to get, a controller to set.
* @return mixed.
*/
public function controller($controller = null) {
if ($controller) {
if (!$controller instanceof Controller) {
throw new CakeException(__('$controller needs to be an instance of Controller'));
}
$this->_controller = $controller;
return true;
}
return $this->_controller;
}
/**
* Get the action path for a given request. Primarily used by authorize objects
* that need to get information about the plugin, controller, and action being invoked.
*
* @param CakeRequest $request The request a path is needed for.
* @return string the action path for the given request.
*/
public function action($request, $path = '/:plugin/:controller/:action') {
$plugin = empty($request['plugin']) ? null : Inflector::camelize($request['plugin']) . '/';
return str_replace(
array(':controller', ':action', ':plugin/'),
array(Inflector::camelize($request['controller']), $request['action'], $plugin),
$this->settings['actionPath'] . $path
);
}
/**
* Maps crud actions to actual controller names. Used to modify or get the current mapped actions.
*
* @param mixed $map Either an array of mappings, or undefined to get current values.
* @return mixed Either the current mappings or null when setting.
*/
public function mapActions($map = array()) {
if (empty($map)) {
return $this->settings['actionMap'];
}
$crud = array('create', 'read', 'update', 'delete');
foreach ($map as $action => $type) {
if (in_array($action, $crud) && is_array($type)) {
foreach ($type as $typedAction) {
$this->settings['actionMap'][$typedAction] = $action;
}
} else {
$this->settings['actionMap'][$action] = $type;
}
}
}
}

View file

@ -0,0 +1,121 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/base_authenticate');
/**
* Basic Authentication adapter for AuthComponent.
*
* Provides Basic HTTP authentication support for AuthComponent. Basic Auth will authenticate users
* against the configured userModel and verify the username and passwords match. Clients using Basic Authentication
* must support cookies. Since AuthComponent identifies users based on Session contents, clients using Basic
* Auth must support cookies.
*
* ### Using Basic auth
*
* In your controller's components array, add auth + the required settings.
* {{{
* var $components = array(
* 'Auth' => array(
* 'authenticate' => array('Basic')
* )
* );
* }}}
*
* In your login function just call `$this->Auth->login()` without any checks for POST data. This
* will send the authentication headers, and trigger the login dialog in the browser/client.
*
* @package cake.libs.controller.components.auth
* @since 2.0
*/
class BasicAuthenticate extends BaseAuthenticate {
/**
* Settings for this object.
*
* - `fields` The fields to use to identify a user by.
* - `userModel` The model name of the User, defaults to User.
* - `scope` Additional conditions to use when looking up and authenticating users,
* i.e. `array('User.is_active' => 1).`
* - `realm` The realm authentication is for. Defaults the server name.
*
* @var array
*/
public $settings = array(
'fields' => array(
'username' => 'username',
'password' => 'password'
),
'userModel' => 'User',
'scope' => array(),
'realm' => '',
);
/**
* Constructor, completes configuration for basic authentication.
*
* @param array $settings An array of settings.
* @return void
*/
public function __construct($settings) {
parent::__construct($settings);
if (empty($this->settings['realm'])) {
$this->settings['realm'] = env('SERVER_NAME');
}
}
/**
* Authenticate a user using basic HTTP auth. Will use the configured User model and attempt a
* login using basic HTTP auth.
*
* @param CakeRequest $request The request to authenticate with.
* @param CakeResponse $response The response to add headers to.
* @return mixed Either false on failure, or an array of user data on success.
*/
public function authenticate(CakeRequest $request, CakeResponse $response) {
$result = $this->getUser($request);
if (empty($result)) {
$response->header($this->loginHeaders());
$response->statusCode(401);
$response->send();
return false;
}
return $result;
}
/**
* Get a user based on information in the request. Used by cookie-less auth for stateless clients.
*
* @param CakeRequest $request Request object.
* @return mixed Either false or an array of user information
*/
public function getUser($request) {
$username = env('PHP_AUTH_USER');
$pass = env('PHP_AUTH_PW');
if (empty($username) || empty($pass)) {
return false;
}
return $this->_findUser($username, $pass);
}
/**
* Generate the login headers
*
* @return string Headers for logging in.
*/
public function loginHeaders() {
return sprintf('WWW-Authenticate: Basic realm="%s"', $this->settings['realm']);
}
}

View file

@ -0,0 +1,65 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/base_authorize');
/**
* An authorization adapter for AuthComponent. Provides the ability to authorize using a controller callback.
* Your controller's isAuthorized() method should return a boolean to indicate whether or not the user is authorized.
*
* {{{
* function isAuthorized($user) {
* if (!empty($this->request->params['admin'])) {
* return $user['role'] == 'admin';
* }
* return !empty($user);
* }
* }}}
*
* the above is simple implementation that would only authorize users of the 'admin' role to access
* admin routing.
*
* @package cake.libs.controller.components.auth
* @since 2.0
* @see AuthComponent::$authenticate
*/
class ControllerAuthorize extends BaseAuthorize {
/**
* Get/set the controller this authorize object will be working with. Also checks that isAuthorized is implemented.
*
* @param mixed $controller null to get, a controller to set.
* @return mixed.
*/
public function controller($controller = null) {
if ($controller) {
if (!method_exists($controller, 'isAuthorized')) {
throw new CakeException(__('$controller does not implement an isAuthorized() method.'));
}
}
return parent::controller($controller);
}
/**
* Checks user authorization using a controller callback.
*
* @param array $user Active user data
* @param CakeRequest $request
* @return boolean
*/
public function authorize($user, CakeRequest $request) {
return (bool) $this->_controller->isAuthorized($user);
}
}

View file

@ -0,0 +1,98 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/base_authorize');
/**
* An authorization adapter for AuthComponent. Provides the ability to authorize using CRUD mappings.
* CRUD mappings allow you to translate controller actions into *C*reate *R*ead *U*pdate *D*elete actions.
* This is then checked in the AclComponent as specific permissions.
*
* For example, taking `/posts/index` as the current request. The default mapping for `index`, is a `read` permission
* check. The Acl check would then be for the `posts` controller with the `read` permission. This allows you
* to create permission systems that focus more on what is being done to resources, rather than the specific actions
* being visited.
*
* @package cake.libs.controller.components.auth
* @since 2.0
* @see AuthComponent::$authenticate
* @see AclComponent::check()
*/
class CrudAuthorize extends BaseAuthorize {
/**
* Sets up additional actionMap values that match the configured `Routing.prefixes`.
*
* @param Controller $controller The controller for this request.
* @param string $settings An array of settings. This class does not use any settings.
*/
public function __construct(Controller $controller, $settings = array()) {
parent::__construct($controller, $settings);
$this->_setPrefixMappings();
}
/**
* sets the crud mappings for prefix routes.
*
* @return void
*/
protected function _setPrefixMappings() {
$crud = array('create', 'read', 'update', 'delete');
$map = array_combine($crud, $crud);
$prefixes = Router::prefixes();
if (!empty($prefixes)) {
foreach ($prefixes as $prefix) {
$map = array_merge($map, array(
$prefix . '_index' => 'read',
$prefix . '_add' => 'create',
$prefix . '_edit' => 'update',
$prefix . '_view' => 'read',
$prefix . '_remove' => 'delete',
$prefix . '_create' => 'create',
$prefix . '_read' => 'read',
$prefix . '_update' => 'update',
$prefix . '_delete' => 'delete'
));
}
}
$this->mapActions($map);
}
/**
* Authorize a user using the mapped actions and the AclComponent.
*
* @param array $user The user to authorize
* @param CakeRequest $request The request needing authorization.
* @return boolean
*/
public function authorize($user, CakeRequest $request) {
if (!isset($this->settings['actionMap'][$request->params['action']])) {
trigger_error(__(
'CrudAuthorize::authorize() - Attempted access of un-mapped action "%1$s" in controller "%2$s"',
$request->action,
$request->controller
),
E_USER_WARNING
);
return false;
}
$Acl = $this->_controller->Components->load('Acl');
return $Acl->check(
$user,
$this->action($request, ':controller'),
$this->settings['actionMap'][$request->params['action']]
);
}
}

View file

@ -0,0 +1,261 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/base_authenticate');
/**
* Digest Authentication adapter for AuthComponent.
*
* Provides Digest HTTP authentication support for AuthComponent. Unlike most AuthComponent adapters,
* DigestAuthenticate requires a special password hash that conforms to RFC2617. You can create this
* password using `DigestAuthenticate::password()`. If you wish to use digest authentication alongside other
* authentication methods, its recommended that you store the digest authentication separately.
*
* Clients using Digest Authentication must support cookies. Since AuthComponent identifies users based
* on Session contents, clients without support for cookies will not function properly.
*
* ### Using Digest auth
*
* In your controller's components array, add auth + the required settings.
* {{{
* var $components = array(
* 'Auth' => array(
* 'authenticate' => array('Digest')
* )
* );
* }}}
*
* In your login function just call `$this->Auth->login()` without any checks for POST data. This
* will send the authentication headers, and trigger the login dialog in the browser/client.
*
* ### Generating passwords compatible with Digest authentication.
*
* Due to the Digest authentication specification, digest auth requires a special password value. You
* can generate this password using `DigestAuthenticate::password()`
*
* `$digestPass = DigestAuthenticate::password($username, env('SERVER_NAME'), $password);`
*
* Its recommended that you store this digest auth only password separate from password hashes used for other
* login methods. For example `User.digest_pass` could be used for a digest password, while `User.password` would
* store the password hash for use with other methods like Basic or Form.
*
* @package cake.libs.controller.components.auth
* @since 2.0
*/
class DigestAuthenticate extends BaseAuthenticate {
/**
* Settings for this object.
*
* - `fields` The fields to use to identify a user by.
* - `userModel` The model name of the User, defaults to User.
* - `scope` Additional conditions to use when looking up and authenticating users,
* i.e. `array('User.is_active' => 1).`
* - `realm` The realm authentication is for, Defaults to the servername.
* - `nonce` A nonce used for authentication. Defaults to `uniqid()`.
* - `qop` Defaults to auth, no other values are supported at this time.
* - `opaque` A string that must be returned unchanged by clients. Defaults to `md5($settings['realm'])`
*
* @var array
*/
public $settings = array(
'fields' => array(
'username' => 'username',
'password' => 'password'
),
'userModel' => 'User',
'scope' => array(),
'realm' => '',
'qop' => 'auth',
'nonce' => '',
'opaque' => ''
);
/**
* Constructor, completes configuration for digest authentication.
*
* @param array $settings An array of settings.
* @return void
*/
public function __construct($settings) {
parent::__construct($settings);
if (empty($this->settings['realm'])) {
$this->settings['realm'] = env('SERVER_NAME');
}
if (empty($this->settings['nonce'])) {
$this->settings['nonce'] = uniqid('');
}
if (empty($this->settings['opaque'])) {
$this->settings['opaque'] = md5($this->settings['realm']);
}
}
/**
* Authenticate a user using Digest HTTP auth. Will use the configured User model and attempt a
* login using Digest HTTP auth.
*
* @param CakeRequest $request The request to authenticate with.
* @param CakeResponse $response The response to add headers to.
* @return mixed Either false on failure, or an array of user data on success.
*/
public function authenticate(CakeRequest $request, CakeResponse $response) {
$user = $this->getUser($request);
if (empty($user)) {
$response->header($this->loginHeaders());
$response->statusCode(401);
$response->send();
return false;
}
return $user;
}
/**
* Get a user based on information in the request. Used by cookie-less auth for stateless clients.
*
* @param CakeRequest $request Request object.
* @return mixed Either false or an array of user information
*/
public function getUser($request) {
$digest = $this->_getDigest();
if (empty($digest)) {
return false;
}
$user = $this->_findUser($digest['username'], null);
if (empty($user)) {
return false;
}
$password = $user[$this->settings['fields']['password']];
unset($user[$this->settings['fields']['password']]);
if ($digest['response'] === $this->generateResponseHash($digest, $password)) {
return $user;
}
return false;
}
/**
* 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 $username The username to use in the digest hash.
* @param string $password The unhashed password to make a digest hash for.
* @param string $realm The realm the password is for.
* @return string the hashed password that can later be used with Digest authentication.
*/
public static function password($username, $password, $realm) {
return md5($username . ':' . $realm . ':' . $password);
}
/**
* Generate the login headers
*
* @return string Headers for logging in.
*/
public function loginHeaders() {
$options = array(
'realm' => $this->settings['realm'],
'qop' => $this->settings['qop'],
'nonce' => $this->settings['nonce'],
'opaque' => $this->settings['opaque']
);
$opts = array();
foreach ($options as $k => $v) {
$opts[] = sprintf('%s="%s"', $k, $v);
}
return 'WWW-Authenticate: Digest ' . implode(',', $opts);
}
}

View file

@ -0,0 +1,67 @@
<?php
/**
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/base_authenticate');
/**
* An authentication adapter for AuthComponent. Provides the ability to authenticate using POST
* data. Can be used by configuring AuthComponent to use it via the AuthComponent::$authenticate setting.
*
* {{{
* $this->Auth->authenticate = array(
* 'Form' => array(
* 'scope' => array('User.active' => 1)
* )
* )
* }}}
*
* When configuring FormAuthenticate you can pass in settings to which fields, model and additional conditions
* are used. See FormAuthenticate::$settings for more information.
*
* @package cake.libs.controller.components.auth
* @since 2.0
* @see AuthComponent::$authenticate
*/
class FormAuthenticate extends BaseAuthenticate {
/**
* Authenticates the identity contained in a request. Will use the `settings.userModel`, and `settings.fields`
* to find POST data that is used to find a matching record in the `settings.userModel`. Will return false if
* there is no post data, either username or password is missing, of if the scope conditions have not been met.
*
* @param CakeRequest $request The request that contains login information.
* @param CakeResponse $response Unused response object.
* @return mixed. False on login failure. An array of User data on success.
*/
public function authenticate(CakeRequest $request, CakeResponse $response) {
$userModel = $this->settings['userModel'];
list($plugin, $model) = pluginSplit($userModel);
$fields = $this->settings['fields'];
if (empty($request->data[$model])) {
return false;
}
if (
empty($request->data[$model][$fields['username']]) ||
empty($request->data[$model][$fields['password']])
) {
return false;
}
return $this->_findUser(
$request->data[$model][$fields['username']],
$request->data[$model][$fields['password']]
);
}
}

View file

@ -89,33 +89,6 @@ class SecurityComponent extends Component {
*/
public $requireAuth = array();
/**
* List of actions that require an HTTP-authenticated login (basic or digest)
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
public $requireLogin = array();
/**
* Login options for SecurityComponent::requireLogin()
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
public $loginOptions = array('type' => '', 'prompt' => null);
/**
* An associative array of usernames/passwords used for HTTP-authenticated logins.
*
* @var array
* @access public
* @see SecurityComponent::requireLogin()
*/
public $loginUsers = array();
/**
* Controllers from which actions of the current controller are allowed to receive
* requests.
@ -215,7 +188,6 @@ class SecurityComponent extends Component {
$this->_methodsRequired($controller);
$this->_secureRequired($controller);
$this->_authRequired($controller);
$this->_loginRequired($controller);
$isPost = ($this->request->is('post') || $this->request->is('put'));
$isRequestAction = (
@ -299,134 +271,6 @@ class SecurityComponent extends Component {
$this->_requireMethod('Auth', $args);
}
/**
* Sets the actions that require an HTTP-authenticated request, or empty for all actions
*
* @return void
* @link http://book.cakephp.org/view/1302/requireLogin
*/
public function requireLogin() {
$args = func_get_args();
$base = $this->loginOptions;
foreach ($args as $i => $arg) {
if (is_array($arg)) {
$this->loginOptions = $arg;
unset($args[$i]);
}
}
$this->loginOptions = array_merge($base, $this->loginOptions);
$this->_requireMethod('Login', $args);
if (isset($this->loginOptions['users'])) {
$this->loginUsers =& $this->loginOptions['users'];
}
}
/**
* 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.
* @return mixed If successful, returns an array with login name and password, otherwise null.
* @link http://book.cakephp.org/view/1303/loginCredentials-string-type
*/
public function loginCredentials($type = null) {
switch (strtolower($type)) {
case 'basic':
$login = array('username' => env('PHP_AUTH_USER'), 'password' => env('PHP_AUTH_PW'));
if (!empty($login['username'])) {
return $login;
}
break;
case 'digest':
default:
$digest = null;
if (version_compare(PHP_VERSION, '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 {
// Server doesn't support digest-auth headers
trigger_error(__('SecurityComponent::loginCredentials() - Server does not support digest authentication'), E_USER_WARNING);
}
if (!empty($digest)) {
return $this->parseDigestAuthData($digest);
}
break;
}
return null;
}
/**
* Generates the text of an HTTP-authentication request header from an array of options.
*
* @param array $options Set of options for header
* @return string HTTP-authentication request header
* @link http://book.cakephp.org/view/1304/loginRequest-array-options
*/
public function loginRequest($options = array()) {
$options = array_merge($this->loginOptions, $options);
$this->_setLoginDefaults($options);
$auth = 'WWW-Authenticate: ' . ucfirst($options['type']);
$out = array('realm="' . $options['realm'] . '"');
if (strtolower($options['type']) == 'digest') {
$out[] = 'qop="auth"';
$out[] = 'nonce="' . uniqid("") . '"';
$out[] = 'opaque="' . md5($options['realm']) . '"';
}
return $auth . ' ' . implode(',', $out);
}
/**
* Parses an HTTP digest authentication response, and returns an array of the data, or null on failure.
*
* @param string $digest Digest authentication response
* @return array Digest authentication parameters
* @link http://book.cakephp.org/view/1305/parseDigestAuthData-string-digest
*/
public function parseDigestAuthData($digest) {
if (substr($digest, 0, 7) == 'Digest ') {
$digest = substr($digest, 7);
}
$keys = array();
$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;
}
/**
* 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()
* @link http://book.cakephp.org/view/1306/generateDigestResponseHash-array-data
*/
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. If SecurityComponent::$blackHoleCallback
* is specified, it will use this callback by executing the method indicated in $error
@ -544,53 +388,6 @@ class SecurityComponent extends Component {
return true;
}
/**
* Check if login is required
*
* @param object $controller Instantiating controller
* @return bool true if login is required
*/
protected function _loginRequired($controller) {
if (is_array($this->requireLogin) && !empty($this->requireLogin)) {
$requireLogin = $this->requireLogin;
if (in_array($this->_action, $this->requireLogin) || $this->requireLogin == array('*')) {
$login = $this->loginCredentials($this->loginOptions['type']);
if ($login == null) {
$controller->header($this->loginRequest());
if (!empty($this->loginOptions['prompt'])) {
$this->_callback($controller, $this->loginOptions['prompt']);
} else {
$this->blackHole($controller, 'login');
}
} else {
if (isset($this->loginOptions['login'])) {
$this->_callback($controller, $this->loginOptions['login'], array($login));
} else {
if (strtolower($this->loginOptions['type']) == 'digest') {
if ($login && isset($this->loginUsers[$login['username']])) {
if ($login['response'] == $this->generateDigestResponseHash($login)) {
return true;
}
}
$this->blackHole($controller, 'login');
} else {
if (
!(in_array($login['username'], array_keys($this->loginUsers)) &&
$this->loginUsers[$login['username']] == $login['password'])
) {
$this->blackHole($controller, 'login');
}
}
}
}
}
}
return true;
}
/**
* Validate submitted form
*
@ -738,22 +535,6 @@ class SecurityComponent extends Component {
return $tokens;
}
/**
* Sets the default login options for an HTTP-authenticated request
*
* @param array $options Default login options
* @return void
*/
protected function _setLoginDefaults(&$options) {
$options = array_merge(array(
'type' => 'basic',
'realm' => env('SERVER_NAME'),
'qop' => 'auth',
'nonce' => String::uuid()
), array_filter($options));
$options = array_merge(array('opaque' => md5($options['realm'])), $options);
}
/**
* Calls a controller callback method
*

View file

@ -765,20 +765,6 @@ class Controller extends Object {
return call_user_func_array(array(&$this, $action), $args);
}
/**
* Controller callback to tie into Auth component.
* Only called when AuthComponent::$authorize is set to 'controller'.
*
* @return bool true if authorized, false otherwise
* @link http://book.cakephp.org/view/1275/authorize
*/
public function isAuthorized() {
trigger_error(sprintf(
__('%sController::isAuthorized() is not defined.'), $this->name
), E_USER_WARNING);
return false;
}
/**
* Returns number of errors in a submitted FORM.
*

View file

@ -37,6 +37,7 @@ class AllComponentsTest extends PHPUnit_Framework_TestSuite {
$suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'component.test.php');
$suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'component_collection.test.php');
$suite->addTestDirectory(CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'components');
$suite->addTestDirectory(CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'components' . DS . 'auth');
return $suite;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,143 @@
<?php
/**
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake.tests.cases.libs.controller.components.auth
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/actions_authorize');
App::import('Controller', 'ComponentCollection');
App::import('Component', 'Acl');
App::import('Core', 'CakeRequest');
App::import('Core', 'Controller');
class ActionsAuthorizeTest extends CakeTestCase {
/**
* setup
*
* @return void
*/
function setUp() {
parent::setUp();
$this->controller = $this->getMock('Controller', array(), array(), '', false);
$this->Acl = $this->getMock('AclComponent', array(), array(), '', false);
$this->controller->Components = $this->getMock('ComponentCollection');
$this->auth = new ActionsAuthorize($this->controller);
$this->auth->settings['actionPath'] = '/controllers';
}
/**
* setup the mock acl.
*
* @return void
*/
protected function _mockAcl() {
$this->controller->Components->expects($this->any())
->method('load')
->with('Acl')
->will($this->returnValue($this->Acl));
}
/**
* test failure
*
* @return void
*/
function testAuthorizeFailure() {
$user = array(
'User' => array(
'id' => 1,
'user' => 'mariano'
)
);
$request = new CakeRequest('/posts/index', false);
$request->addParams(array(
'plugin' => null,
'controller' => 'posts',
'action' => 'index'
));
$this->_mockAcl();
$this->Acl->expects($this->once())
->method('check')
->with($user, '/controllers/Posts/index')
->will($this->returnValue(false));
$this->assertFalse($this->auth->authorize($user, $request));
}
/**
* test isAuthorized working.
*
* @return void
*/
function testAuthorizeSuccess() {
$user = array(
'User' => array(
'id' => 1,
'user' => 'mariano'
)
);
$request = new CakeRequest('/posts/index', false);
$request->addParams(array(
'plugin' => null,
'controller' => 'posts',
'action' => 'index'
));
$this->_mockAcl();
$this->Acl->expects($this->once())
->method('check')
->with($user, '/controllers/Posts/index')
->will($this->returnValue(true));
$this->assertTrue($this->auth->authorize($user, $request));
}
/**
* test action()
*
* @return void
*/
function testActionMethod() {
$request = new CakeRequest('/posts/index', false);
$request->addParams(array(
'plugin' => null,
'controller' => 'posts',
'action' => 'index'
));
$result = $this->auth->action($request);
$this->assertEquals('/controllers/Posts/index', $result);
}
/**
* test action() and plugins
*
* @return void
*/
function testActionWithPlugin() {
$request = new CakeRequest('/debug_kit/posts/index', false);
$request->addParams(array(
'plugin' => 'debug_kit',
'controller' => 'posts',
'action' => 'index'
));
$result = $this->auth->action($request);
$this->assertEquals('/controllers/DebugKit/Posts/index', $result);
}
}

View file

@ -0,0 +1,210 @@
<?php
/**
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake.tests.cases.libs.controller.components.auth
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'Auth');
App::import('Component', 'auth/basic_authenticate');
App::import('Model', 'AppModel');
App::import('Core', 'CakeRequest');
App::import('Core', 'CakeResponse');
require_once CAKE_TESTS . 'cases' . DS . 'libs' . DS . 'model' . DS . 'models.php';
/**
* Test case for BasicAuthentication
*
* @package cake.test.cases.controller.components.auth
*/
class BasicAuthenticateTest extends CakeTestCase {
public $fixtures = array('core.user', 'core.auth_user');
/**
* setup
*
* @return void
*/
function setUp() {
parent::setUp();
$this->auth = new BasicAuthenticate(array(
'fields' => array('username' => 'user', 'password' => 'password'),
'userModel' => 'User',
'realm' => 'localhost',
));
$password = Security::hash('password', null, true);
ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"'));
$this->server = $_SERVER;
$this->response = $this->getMock('CakeResponse');
}
/**
* teardown
*
* @return void
*/
function tearDown() {
parent::tearDown();
$_SERVER = $this->server;
}
/**
* test applying settings in the constructor
*
* @return void
*/
function testConstructor() {
$object = new BasicAuthenticate(array(
'userModel' => 'AuthUser',
'fields' => array('username' => 'user', 'password' => 'password')
));
$this->assertEquals('AuthUser', $object->settings['userModel']);
$this->assertEquals(array('username' => 'user', 'password' => 'password'), $object->settings['fields']);
$this->assertEquals(env('SERVER_NAME'), $object->settings['realm']);
}
/**
* test the authenticate method
*
* @return void
*/
function testAuthenticateNoData() {
$request = new CakeRequest('posts/index', false);
$this->response->expects($this->once())
->method('header')
->with('WWW-Authenticate: Basic realm="localhost"');
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test the authenticate method
*
* @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: Basic realm="localhost"');
$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';
$_SERVER['PHP_AUTH_PW'] = null;
$this->response->expects($this->once())
->method('header')
->with('WWW-Authenticate: Basic realm="localhost"');
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test the authenticate method
*
* @return void
*/
function testAuthenticateInjection() {
$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";
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test that challenge headers are sent when no credentials are found.
*
* @return void
*/
function testAuthenticateChallenge() {
$request = new CakeRequest('posts/index', false);
$request->addParams(array('pass' => array(), 'named' => array()));
$this->response->expects($this->at(0))
->method('header')
->with('WWW-Authenticate: Basic realm="localhost"');
$this->response->expects($this->at(1))
->method('send');
$result = $this->auth->authenticate($request, $this->response);
$this->assertFalse($result);
}
/**
* test authenticate sucesss
*
* @return void
*/
function testAuthenticateSuccess() {
$request = new CakeRequest('posts/index', false);
$request->addParams(array('pass' => array(), 'named' => array()));
$_SERVER['PHP_AUTH_USER'] = 'mariano';
$_SERVER['PHP_AUTH_PW'] = 'password';
$result = $this->auth->authenticate($request, $this->response);
$expected = array(
'id' => 1,
'user' => 'mariano',
'created' => '2007-03-17 01:16:23',
'updated' => '2007-03-17 01:18:31'
);
$this->assertEquals($expected, $result);
}
/**
* test scope failure.
*
* @return void
*/
function testAuthenticateFailReChallenge() {
$this->auth->settings['scope'] = array('user' => 'nate');
$request = new CakeRequest('posts/index', false);
$request->addParams(array('pass' => array(), 'named' => array()));
$_SERVER['PHP_AUTH_USER'] = 'mariano';
$_SERVER['PHP_AUTH_PW'] = 'password';
$this->response->expects($this->at(0))
->method('header')
->with('WWW-Authenticate: Basic realm="localhost"');
$this->response->expects($this->at(1))
->method('statusCode')
->with(401);
$this->response->expects($this->at(2))
->method('send');
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake.tests.cases.libs.controller.components.auth
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/controller_authorize');
App::import('Core', 'CakeRequest');
App::import('Core', 'Controller');
class ControllerAuthorizeTest extends CakeTestCase {
/**
* setup
*
* @return void
*/
function setUp() {
parent::setUp();
$this->controller = $this->getMock('Controller', array('isAuthorized'), array(), '', false);
$this->auth = new ControllerAuthorize($this->controller);
}
/**
*
* @expectedException CakeException
*/
function testControllerTypeError() {
$this->auth->controller(new StdClass());
}
/**
* @expectedException CakeException
*/
function testControllerErrorOnMissingMethod() {
$this->auth->controller(new Controller());
}
/**
* test failure
*
* @return void
*/
function testAuthorizeFailure() {
$user = array();
$request = new CakeRequest('/posts/index', false);
$this->assertFalse($this->auth->authorize($user, $request));
}
/**
* test isAuthorized working.
*
* @return void
*/
function testAuthorizeSuccess() {
$user = array('User' => array('username' => 'mark'));
$request = new CakeRequest('/posts/index', false);
$this->controller->expects($this->once())
->method('isAuthorized')
->with($user)
->will($this->returnValue(true));
$this->assertTrue($this->auth->authorize($user, $request));
}
}

View file

@ -0,0 +1,181 @@
<?php
/**
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake.tests.cases.libs.controller.components.auth
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/crud_authorize');
App::import('Controller', 'ComponentCollection');
App::import('Component', 'Acl');
App::import('Core', 'CakeRequest');
App::import('Core', 'Controller');
class CrudAuthorizeTest extends CakeTestCase {
/**
* setup
*
* @return void
*/
function setUp() {
parent::setUp();
$this->controller = $this->getMock('Controller', array(), array(), '', false);
$this->Acl = $this->getMock('AclComponent', array(), array(), '', false);
$this->controller->Components = $this->getMock('ComponentCollection');
$this->auth = new CrudAuthorize($this->controller);
}
/**
* setup the mock acl.
*
* @return void
*/
protected function _mockAcl() {
$this->controller->Components->expects($this->any())
->method('load')
->with('Acl')
->will($this->returnValue($this->Acl));
}
/**
* test authorize() without a mapped action, ensure an error is generated.
*
* @expectedException Exception
* @return void
*/
function testAuthorizeNoMappedAction() {
$request = new CakeRequest('/posts/foobar', false);
$request->addParams(array(
'controller' => 'posts',
'action' => 'foobar'
));
$user = array('User' => array('user' => 'mark'));
$this->auth->authorize($user, $request);
}
/**
* test check() passing
*
* @return void
*/
function testAuthorizeCheckSuccess() {
$request = new CakeRequest('posts/index', false);
$request->addParams(array(
'controller' => 'posts',
'action' => 'index'
));
$user = array('User' => array('user' => 'mark'));
$this->_mockAcl();
$this->Acl->expects($this->once())
->method('check')
->with($user, 'Posts', 'read')
->will($this->returnValue(true));
$this->assertTrue($this->auth->authorize($user, $request));
}
/**
* test check() failing
*
* @return void
*/
function testAuthorizeCheckFailure() {
$request = new CakeRequest('posts/index', false);
$request->addParams(array(
'controller' => 'posts',
'action' => 'index'
));
$user = array('User' => array('user' => 'mark'));
$this->_mockAcl();
$this->Acl->expects($this->once())
->method('check')
->with($user, 'Posts', 'read')
->will($this->returnValue(false));
$this->assertFalse($this->auth->authorize($user, $request));
}
/**
* test getting actionMap
*
* @return void
*/
function testMapActionsGet() {
$result = $this->auth->mapActions();
$expected = array(
'create' => 'create',
'read' => 'read',
'update' => 'update',
'delete' => 'delete',
'index' => 'read',
'add' => 'create',
'edit' => 'update',
'view' => 'read',
'remove' => 'delete'
);
$this->assertEquals($expected, $result);
}
/**
* test adding into mapActions
*
* @return void
*/
function testMapActionsSet() {
$map = array(
'create' => array('generate'),
'read' => array('listing', 'show'),
'update' => array('update'),
'random' => 'custom'
);
$result = $this->auth->mapActions($map);
$this->assertNull($result);
$result = $this->auth->mapActions();
$expected = array(
'add' => 'create',
'create' => 'create',
'read' => 'read',
'index' => 'read',
'add' => 'create',
'edit' => 'update',
'view' => 'read',
'delete' => 'delete',
'remove' => 'delete',
'generate' => 'create',
'listing' => 'read',
'show' => 'read',
'update' => 'update',
'random' => 'custom',
);
$this->assertEquals($expected, $result);
}
/**
* test prefix routes getting auto mapped.
*
* @return void
*/
function testAutoPrefixMapActions() {
Configure::write('Routing.prefixes', array('admin', 'manager'));
Router::reload();
$auth = new CrudAuthorize($this->controller);
$this->assertTrue(isset($auth->settings['actionMap']['admin_index']));
}
}

View file

@ -0,0 +1,303 @@
<?php
/**
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake.tests.cases.libs.controller.components.auth
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/digest_authenticate');
App::import('Model', 'AppModel');
App::import('Core', 'CakeRequest');
App::import('Core', 'CakeResponse');
require_once CAKE_TESTS . 'cases' . DS . 'libs' . DS . 'model' . DS . 'models.php';
/**
* Test case for DigestAuthentication
*
* @package cake.test.cases.controller.components.auth
*/
class DigestAuthenticateTest extends CakeTestCase {
public $fixtures = array('core.user', 'core.auth_user');
/**
* setup
*
* @return void
*/
function setUp() {
parent::setUp();
$this->server = $_SERVER;
$this->auth = new DigestAuthenticate(array(
'fields' => array('username' => 'user', 'password' => 'password'),
'userModel' => 'User',
'realm' => 'localhost',
'nonce' => 123,
'opaque' => '123abc'
));
$password = DigestAuthenticate::password('mariano', 'cake', 'localhost');
ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"'));
$_SERVER['REQUEST_METHOD'] = 'GET';
$this->response = $this->getMock('CakeResponse');
}
/**
* teardown
*
* @return void
*/
function tearDown() {
parent::tearDown();
$_SERVER = $this->server;
}
/**
* test applying settings in the constructor
*
* @return void
*/
function testConstructor() {
$object = new DigestAuthenticate(array(
'userModel' => 'AuthUser',
'fields' => array('username' => 'user', 'password' => 'password'),
'nonce' => 123456
));
$this->assertEquals('AuthUser', $object->settings['userModel']);
$this->assertEquals(array('username' => 'user', 'password' => 'password'), $object->settings['fields']);
$this->assertEquals(123456, $object->settings['nonce']);
$this->assertEquals(env('SERVER_NAME'), $object->settings['realm']);
}
/**
* test the authenticate method
*
* @return void
*/
function testAuthenticateNoData() {
$request = new CakeRequest('posts/index', false);
$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 testAuthenticateWrongUsername() {
$request = new CakeRequest('posts/index', false);
$request->addParams(array('pass' => array(), 'named' => array()));
$_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('statusCode')
->with(401);
$this->response->expects($this->at(2))
->method('send');
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test that challenge headers are sent when no credentials are found.
*
* @return void
*/
function testAuthenticateChallenge() {
$request = new CakeRequest('posts/index', false);
$request->addParams(array('pass' => array(), 'named' => array()));
$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('statusCode')
->with(401);
$this->response->expects($this->at(2))
->method('send');
$result = $this->auth->authenticate($request, $this->response);
$this->assertFalse($result);
}
/**
* test authenticate sucesss
*
* @return void
*/
function testAuthenticateSuccess() {
$request = new CakeRequest('posts/index', false);
$request->addParams(array('pass' => array(), 'named' => array()));
$_SERVER['PHP_AUTH_DIGEST'] = <<<DIGEST
Digest username="mariano",
realm="localhost",
nonce="123",
uri="/dir/index.html",
qop=auth,
nc=1,
cnonce="123",
response="06b257a54befa2ddfb9bfa134224aa29",
opaque="123abc"
DIGEST;
$result = $this->auth->authenticate($request, $this->response);
$expected = array(
'id' => 1,
'user' => 'mariano',
'created' => '2007-03-17 01:16:23',
'updated' => '2007-03-17 01:18:31'
);
$this->assertEquals($expected, $result);
}
/**
* test scope failure.
*
* @return void
*/
function testAuthenticateFailReChallenge() {
$this->auth->settings['scope'] = array('user' => 'nate');
$request = new CakeRequest('posts/index', false);
$request->addParams(array('pass' => array(), 'named' => array()));
$_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')
->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"');
$this->response->expects($this->at(1))
->method('statusCode')
->with(401);
$this->response->expects($this->at(2))
->method('send');
$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);
}
/**
* test password hashing
*
* @return void
*/
function testPassword() {
$result = DigestAuthenticate::password('mark', 'password', 'localhost');
$expected = md5('mark:localhost:password');
$this->assertEquals($expected, $result);
}
}

View file

@ -0,0 +1,185 @@
<?php
/**
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package cake.tests.cases.libs.controller.components.auth
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::import('Component', 'auth/form_authenticate');
App::import('Model', 'AppModel');
App::import('Core', 'CakeRequest');
App::import('Core', 'CakeResponse');
require_once CAKE_TESTS . 'cases' . DS . 'libs' . DS . 'model' . DS . 'models.php';
/**
* Test case for FormAuthentication
*
* @package cake.test.cases.controller.components.auth
*/
class FormAuthenticateTest extends CakeTestCase {
public $fixtures = array('core.user', 'core.auth_user');
/**
* setup
*
* @return void
*/
function setUp() {
parent::setUp();
$this->auth = new FormAuthenticate(array(
'fields' => array('username' => 'user', 'password' => 'password'),
'userModel' => 'User'
));
$password = Security::hash('password', null, true);
ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"'));
$this->response = $this->getMock('CakeResponse');
}
/**
* test applying settings in the constructor
*
* @return void
*/
function testConstructor() {
$object = new FormAuthenticate(array(
'userModel' => 'AuthUser',
'fields' => array('username' => 'user', 'password' => 'password')
));
$this->assertEquals('AuthUser', $object->settings['userModel']);
$this->assertEquals(array('username' => 'user', 'password' => 'password'), $object->settings['fields']);
}
/**
* test the authenticate method
*
* @return void
*/
function testAuthenticateNoData() {
$request = new CakeRequest('posts/index', false);
$request->data = array();
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test the authenticate method
*
* @return void
*/
function testAuthenticateNoUsername() {
$request = new CakeRequest('posts/index', false);
$request->data = array('User' => array('password' => 'foobar'));
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test the authenticate method
*
* @return void
*/
function testAuthenticateNoPassword() {
$request = new CakeRequest('posts/index', false);
$request->data = array('User' => array('user' => 'mariano'));
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test the authenticate method
*
* @return void
*/
function testAuthenticateInjection() {
$request = new CakeRequest('posts/index', false);
$request->data = array(
'User' => array(
'user' => '> 1',
'password' => "' OR 1 = 1"
));
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test authenticate sucesss
*
* @return void
*/
function testAuthenticateSuccess() {
$request = new CakeRequest('posts/index', false);
$request->data = array('User' => array(
'user' => 'mariano',
'password' => 'password'
));
$result = $this->auth->authenticate($request, $this->response);
$expected = array(
'id' => 1,
'user' => 'mariano',
'created' => '2007-03-17 01:16:23',
'updated' => '2007-03-17 01:18:31'
);
$this->assertEquals($expected, $result);
}
/**
* test scope failure.
*
* @return void
*/
function testAuthenticateScopeFail() {
$this->auth->settings['scope'] = array('user' => 'nate');
$request = new CakeRequest('posts/index', false);
$request->data = array('User' => array(
'user' => 'mariano',
'password' => 'password'
));
$this->assertFalse($this->auth->authenticate($request, $this->response));
}
/**
* test a model in a plugin.
*
* @return void
*/
function testPluginModel() {
Cache::delete('object_map', '_cake_core_');
App::build(array(
'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
), true);
App::objects('plugin', null, false);
$PluginModel = ClassRegistry::init('TestPlugin.TestPluginAuthUser');
$user['id'] = 1;
$user['username'] = 'gwoo';
$user['password'] = Security::hash(Configure::read('Security.salt') . 'cake');
$PluginModel->save($user, false);
$this->auth->settings['userModel'] = 'TestPlugin.TestPluginAuthUser';
$this->auth->settings['fields']['username'] = 'username';
$request = new CakeRequest('posts/index', false);
$request->data = array('TestPluginAuthUser' => array(
'username' => 'gwoo',
'password' => 'cake'
));
$result = $this->auth->authenticate($request, $this->response);
$expected = array(
'id' => 1,
'username' => 'gwoo',
'created' => '2007-03-17 01:16:23',
'updated' => date('Y-m-d H:i:s')
);
$this->assertEquals($expected, $result);
}
}

View file

@ -180,10 +180,6 @@ class SecurityComponentTest extends CakeTestCase {
'requireSecure' => array('update_account'),
'requireGet' => array('index'),
'validatePost' => false,
'loginUsers' => array(
'mark' => 'password'
),
'requireLogin' => array('login'),
);
$Security = new SecurityComponent($this->Controller->Components, $settings);
$this->Controller->Security->initialize($this->Controller, $settings);
@ -191,8 +187,6 @@ class SecurityComponentTest extends CakeTestCase {
$this->assertEqual($Security->requireSecure, $settings['requireSecure']);
$this->assertEqual($Security->requireGet, $settings['requireGet']);
$this->assertEqual($Security->validatePost, $settings['validatePost']);
$this->assertEqual($Security->loginUsers, $settings['loginUsers']);
$this->assertEqual($Security->requireLogin, $settings['requireLogin']);
}
/**
@ -367,80 +361,6 @@ class SecurityComponentTest extends CakeTestCase {
$this->assertFalse($this->Controller->failed);
}
/**
* testRequireLogin method
*
* @access public
* @return void
*/
function testRequireLogin() {
$this->Controller->request['action'] = 'posted';
$this->Controller->Security->requireLogin(
'posted',
array('type' => 'basic', 'users' => array('admin' => 'password'))
);
$_SERVER['PHP_AUTH_USER'] = 'admin';
$_SERVER['PHP_AUTH_PW'] = 'password';
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
$this->Controller->request['action'] = 'posted';
$this->Controller->Security->requireLogin(
array('posted'),
array('type' => 'basic', 'users' => array('admin' => 'password'))
);
$_SERVER['PHP_AUTH_USER'] = 'admin2';
$_SERVER['PHP_AUTH_PW'] = 'password';
$this->Controller->Security->startup($this->Controller);
$this->assertTrue($this->Controller->failed);
$this->Controller->request['action'] = 'posted';
$this->Controller->Security->requireLogin(
'posted',
array('type' => 'basic', 'users' => array('admin' => 'password'))
);
$_SERVER['PHP_AUTH_USER'] = 'admin';
$_SERVER['PHP_AUTH_PW'] = 'password2';
$this->Controller->Security->startup($this->Controller);
$this->assertTrue($this->Controller->failed);
}
/**
* testDigestAuth method
*
* @access public
* @return void
*/
function testDigestAuth() {
$skip = $this->skipIf((version_compare(PHP_VERSION, '5.1') == -1) XOR (!function_exists('apache_request_headers')),
"%s Cannot run Digest Auth test for PHP versions < 5.1"
);
if ($skip) {
return;
}
$this->Controller->request['action'] = 'posted';
$_SERVER['PHP_AUTH_DIGEST'] = $digest = <<<DIGEST
Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="460d0d3c6867c2f1ab85b1ada1aece48",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
DIGEST;
$this->Controller->Security->requireLogin('posted', array(
'type' => 'digest', 'users' => array('Mufasa' => 'password'),
'realm' => 'testrealm@host.com'
));
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
/**
* testRequireGetSucceedWrongMethod method
*
@ -539,35 +459,6 @@ DIGEST;
$this->assertFalse($this->Controller->failed);
}
/**
* testRequireLoginSettings method
*
* @access public
* @return void
*/
function testRequireLoginSettings() {
$this->Controller->Security->requireLogin(
'add', 'edit',
array('type' => 'basic', 'users' => array('admin' => 'password'))
);
$this->assertEqual($this->Controller->Security->requireLogin, array('add', 'edit'));
$this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password'));
}
/**
* testRequireLoginAllActions method
*
* @access public
* @return void
*/
function testRequireLoginAllActions() {
$this->Controller->Security->requireLogin(
array('type' => 'basic', 'users' => array('admin' => 'password'))
);
$this->assertEqual($this->Controller->Security->requireLogin, array('*'));
$this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password'));
}
/**
* Simple hash validation test
*
@ -988,173 +879,6 @@ DIGEST;
$this->assertFalse($result);
}
/**
* testLoginRequest method
*
* @access public
* @return void
*/
function testLoginRequest() {
$this->Controller->Security->startup($this->Controller);
$realm = 'cakephp.org';
$options = array('realm' => $realm, 'type' => 'basic');
$result = $this->Controller->Security->loginRequest($options);
$expected = 'WWW-Authenticate: Basic realm="'.$realm.'"';
$this->assertEqual($result, $expected);
$this->Controller->Security->startup($this->Controller);
$options = array('realm' => $realm, 'type' => 'digest');
$result = $this->Controller->Security->loginRequest($options);
$this->assertPattern('/realm="'.$realm.'"/', $result);
$this->assertPattern('/qop="auth"/', $result);
}
/**
* testGenerateDigestResponseHash method
*
* @access public
* @return void
*/
function testGenerateDigestResponseHash() {
$this->Controller->Security->startup($this->Controller);
$realm = 'cakephp.org';
$loginData = array('realm' => $realm, 'users' => array('Willy Smith' => 'password'));
$this->Controller->Security->requireLogin($loginData);
$data = array(
'username' => 'Willy Smith',
'password' => 'password',
'nonce' => String::uuid(),
'nc' => 1,
'cnonce' => 1,
'realm' => $realm,
'uri' => 'path_to_identifier',
'qop' => 'testme'
);
$_SERVER['REQUEST_METHOD'] = 'POST';
$result = $this->Controller->Security->generateDigestResponseHash($data);
$expected = md5(
md5($data['username'] . ':' . $loginData['realm'] . ':' . $data['password']) . ':' .
$data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' .
md5(env('REQUEST_METHOD') . ':' . $data['uri'])
);
$this->assertIdentical($result, $expected);
}
/**
* testLoginCredentials method
*
* @access public
* @return void
*/
function testLoginCredentials() {
$this->Controller->Security->startup($this->Controller);
$_SERVER['PHP_AUTH_USER'] = $user = 'Willy Test';
$_SERVER['PHP_AUTH_PW'] = $pw = 'some password for the nice test';
$result = $this->Controller->Security->loginCredentials('basic');
$expected = array('username' => $user, 'password' => $pw);
$this->assertIdentical($result, $expected);
if (version_compare(PHP_VERSION, '5.1') != -1) {
$_SERVER['PHP_AUTH_DIGEST'] = $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->Controller->Security->loginCredentials('digest');
$this->assertIdentical($result, $expected);
}
}
/**
* testParseDigestAuthData method
*
* @access public
* @return void
*/
function testParseDigestAuthData() {
$this->Controller->Security->startup($this->Controller);
$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->Controller->Security->parseDigestAuthData($digest);
$this->assertIdentical($result, $expected);
$result = $this->Controller->Security->parseDigestAuthData('');
$this->assertNull($result);
}
/**
* test parsing digest information with email addresses
*
* @return void
*/
function testParseDigestAuthEmailAddress() {
$this->Controller->Security->startup($this->Controller);
$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->Controller->Security->parseDigestAuthData($digest);
$this->assertIdentical($result, $expected);
}
/**
* testFormDisabledFields method
*
@ -1225,26 +949,6 @@ DIGEST;
$this->assertTrue($result);
}
/**
* testInvalidAuthHeaders method
*
* @access public
* @return void
*/
function testInvalidAuthHeaders() {
$this->Controller->Security->blackHoleCallback = null;
$_SERVER['PHP_AUTH_USER'] = 'admin';
$_SERVER['PHP_AUTH_PW'] = 'password';
$realm = 'cakephp.org';
$loginData = array('type' => 'basic', 'realm' => $realm);
$this->Controller->Security->requireLogin($loginData);
$this->Controller->Security->startup($this->Controller);
$expected = 'WWW-Authenticate: Basic realm="'.$realm.'"';
$this->assertEqual(count($this->Controller->testHeaders), 1);
$this->assertEqual(current($this->Controller->testHeaders), $expected);
}
/**
* test that a requestAction's controller will have the _Token appended to
* the params.