Authentication and Authorization objects are integrated with AuthComponent. Minimal App changes are needed as legacy settings are forwarded.

This commit is contained in:
mark_story 2011-01-03 18:18:42 -05:00
parent ced832ba62
commit 3f9c83c43d
2 changed files with 158 additions and 112 deletions

View file

@ -48,20 +48,36 @@ class AuthComponent extends Component {
public $components = array('Session', 'RequestHandler'); 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 * @var object
* @link http://book.cakephp.org/view/1278/authenticate * @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 * Objects that will be used for authentication checks.
* 'controller' will validate against Controller::isAuthorized() *
* 'actions' will validate Controller::action against an AclComponent::check() * @var array
* 'crud' will validate mapActions against an AclComponent::check() */
* array('model'=> 'name'); will validate mapActions against model $name::isAuthorized(user, controller, mapAction) protected $_authenticateObjects = array();
* 'object' will validate Controller::action against object::isAuthorized(user, controller, action)
/**
* A hash mapping legacy properties => to settings passed into Authenticate objects.
*
* @var string
* @deprecated Will be removed in 2.1+
*/
protected $_authenticateLegacyMap = array(
'userModel' => 'userModel',
'userScope' => 'scope',
'fields' => 'fields'
);
/**
* 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.
* *
* @var mixed * @var mixed
* @link http://book.cakephp.org/view/1275/authorize * @link http://book.cakephp.org/view/1275/authorize
@ -363,15 +379,7 @@ class AuthComponent extends Component {
!empty($request->data[$model->alias][$this->fields['password']]); !empty($request->data[$model->alias][$this->fields['password']]);
if ($isValid) { if ($isValid) {
$username = $request->data[$model->alias][$this->fields['username']]; if ($this->login()) {
$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) { if ($this->autoRedirect) {
$controller->redirect($this->redirect(), null, true); $controller->redirect($this->redirect(), null, true);
} }
@ -456,18 +464,24 @@ class AuthComponent extends Component {
* Each adapter will be checked in sequence, if any of them return true, then the user will * Each adapter will be checked in sequence, if any of them return true, then the user will
* be authorized for the request. * be authorized for the request.
* *
* @param mixed $user The user to check the authorization of, if empty the user in the session will be used. * @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 * @return boolean True if $user is authorized, otherwise false
*/ */
public function isAuthorized($user = null) { public function isAuthorized($user = null, $request = null) {
if (empty($user) && !$this->user()) { if (empty($user) && !$this->user()) {
return false; return false;
} elseif (empty($user)) { } elseif (empty($user)) {
$user = $this->user(); $user = $this->user();
} }
if (empty($request)) {
$request = $this->request;
}
if (empty($this->_authorizeObjects)) {
$this->loadAuthorizeObjects(); $this->loadAuthorizeObjects();
}
foreach ($this->_authorizeObjects as $authorizer) { foreach ($this->_authorizeObjects as $authorizer) {
if ($authorizer->authorize($user, $this->request) === true) { if ($authorizer->authorize($user, $request) === true) {
return true; return true;
} }
} }
@ -483,6 +497,7 @@ class AuthComponent extends Component {
if (empty($this->authorize)) { if (empty($this->authorize)) {
return; return;
} }
$this->_authorizeObjects = array();
foreach (Set::normalize($this->authorize) as $class => $settings) { foreach (Set::normalize($this->authorize) as $class => $settings) {
$className = $class . 'Authorize'; $className = $class . 'Authorize';
if (!class_exists($className) && !App::import('Component', 'auth/' . $class . '_authorize')) { if (!class_exists($className) && !App::import('Component', 'auth/' . $class . '_authorize')) {
@ -596,15 +611,14 @@ class AuthComponent extends Component {
* @return boolean True on login success, false on failure * @return boolean True on login success, false on failure
* @link http://book.cakephp.org/view/1261/login * @link http://book.cakephp.org/view/1261/login
*/ */
public function login($data = null) { public function login($request = null) {
$this->__setDefaults(); $this->__setDefaults();
$this->_loggedIn = false; $this->_loggedIn = false;
if (empty($data)) { if (empty($request)) {
$data = $this->data; $request = $this->request;
} }
if ($user = $this->identify($request)) {
if ($user = $this->identify($data)) {
$this->Session->write($this->sessionKey, $user); $this->Session->write($this->sessionKey, $user);
$this->_loggedIn = true; $this->_loggedIn = true;
} }
@ -739,80 +753,51 @@ class AuthComponent extends Component {
} }
/** /**
* Identifies a user based on specific criteria. * Use the configured authentication adapters, and attempt to identify the user
* by credentials contained in $request.
* *
* @param mixed $user Optional. The identity of the user to be validated. * @param CakeRequest $request The request that contains authentication data.
* Uses the current user session if none specified. * @return array User record data, or false, if the user could not be identified.
* @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) { public function identify(CakeRequest $request) {
if ($conditions === false) { if (empty($this->_authenticateObjects)) {
$conditions = null; $this->loadAuthenticateObjects();
} elseif (is_array($conditions)) {
$conditions = array_merge((array)$this->userScope, $conditions);
} else {
$conditions = $this->userScope;
} }
$model = $this->getModel(); foreach ($this->_authenticateObjects as $auth) {
if (empty($user)) { $result = $auth->authenticate($request);
$user = $this->user(); if (!empty($result) && is_array($result)) {
if (empty($user)) { return $result;
return null;
} }
} elseif (is_object($user) && is_a($user, 'Model')) {
if (!$user->exists()) {
return null;
} }
$user = $user->read(); return false;
$user = $user[$model->alias];
} elseif (is_array($user) && isset($user[$model->alias])) {
$user = $user[$model->alias];
} }
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']])) { * loads the configured authentication objects.
if (trim($user[$this->fields['username']]) == '=' || trim($user[$this->fields['password']]) == '=') { *
return false; * @return mixed either null on empty authenticate value, or an array of loaded objects.
*/
public function loadAuthenticateObjects() {
if (empty($this->authenticate)) {
return;
} }
$find = array( $this->_authenticateObjects = array();
$model->alias.'.'.$this->fields['username'] => $user[$this->fields['username']], foreach (Set::normalize($this->authenticate) as $class => $settings) {
$model->alias.'.'.$this->fields['password'] => $user[$this->fields['password']] $className = $class . 'Authenticate';
); if (!class_exists($className) && !App::import('Component', 'auth/' . $class . '_authenticate')) {
} elseif (isset($user[$model->alias . '.' . $this->fields['username']]) && !empty($user[$model->alias . '.' . $this->fields['username']])) { throw new CakeException(__('Authentication adapter "%s" was not found.', $class));
if (trim($user[$model->alias . '.' . $this->fields['username']]) == '=' || trim($user[$model->alias . '.' . $this->fields['password']]) == '=') {
return false;
} }
$find = array( if (!method_exists($className, 'authenticate')) {
$model->alias.'.'.$this->fields['username'] => $user[$model->alias . '.' . $this->fields['username']], throw new CakeException(__('Authentication objects must implement an authenticate method.'));
$model->alias.'.'.$this->fields['password'] => $user[$model->alias . '.' . $this->fields['password']]
);
} else {
return false;
} }
$data = $model->find('first', array( foreach ($this->_authenticateLegacyMap as $old => $new) {
'conditions' => array_merge($find, $conditions), if (empty($settings[$new]) && !empty($this->{$old})) {
'recursive' => 0 $settings[$new] = $this->{$old};
));
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;
} }
} }
$this->_authenticateObjects[] = new $className($settings);
if (!empty($data)) {
if (!empty($data[$model->alias][$this->fields['password']])) {
unset($data[$model->alias][$this->fields['password']]);
} }
return $data[$model->alias]; return $this->_authenticateObjects;
}
return null;
} }
/** /**

View file

@ -582,13 +582,17 @@ class AuthTest extends CakeTestCase {
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
$user = $this->Controller->Auth->user(); $user = $this->Controller->Auth->user();
$expected = array('AuthUser' => array( $expected = array('AuthUser' => array(
'id' => 1, 'username' => 'mariano', 'created' => '2007-03-17 01:16:23', 'updated' => date('Y-m-d H:i:s') 'id' => 1,
'username' => 'mariano',
'created' => '2007-03-17 01:16:23',
'updated' => date('Y-m-d H:i:s')
)); ));
$this->assertEqual($user, $expected); $this->assertEqual($user, $expected);
$this->Controller->Session->delete('Auth'); $this->Controller->Session->delete('Auth');
$this->Controller->request->data['AuthUser'] = array( $this->Controller->request->data['AuthUser'] = array(
'username' => 'blah', 'password' => '' 'username' => 'blah',
'password' => ''
); );
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
@ -598,7 +602,8 @@ class AuthTest extends CakeTestCase {
$this->Controller->Session->delete('Auth'); $this->Controller->Session->delete('Auth');
$this->Controller->request->data['AuthUser'] = array( $this->Controller->request->data['AuthUser'] = array(
'username' => 'now() or 1=1 --', 'password' => '' 'username' => 'now() or 1=1 --',
'password' => ''
); );
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
@ -608,7 +613,8 @@ class AuthTest extends CakeTestCase {
$this->Controller->Session->delete('Auth'); $this->Controller->Session->delete('Auth');
$this->Controller->request->data['AuthUser'] = array( $this->Controller->request->data['AuthUser'] = array(
'username' => 'now() or 1=1 #something', 'password' => '' 'username' => 'now() or 1=1 #something',
'password' => ''
); );
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
@ -617,14 +623,6 @@ class AuthTest extends CakeTestCase {
$this->assertNull($user); $this->assertNull($user);
$this->Controller->Session->delete('Auth'); $this->Controller->Session->delete('Auth');
$this->Controller->Auth->userModel = 'UuidUser';
$this->Controller->Auth->login('47c36f9c-bc00-4d17-9626-4e183ca6822b');
$user = $this->Controller->Auth->user();
$expected = array('UuidUser' => array(
'id' => '47c36f9c-bc00-4d17-9626-4e183ca6822b', 'title' => 'Unique record 1', 'count' => 2, 'created' => '2008-03-13 01:16:23', 'updated' => '2008-03-13 01:18:31'
));
$this->assertEqual($user, $expected);
$this->Controller->Session->delete('Auth'); $this->Controller->Session->delete('Auth');
} }
@ -850,22 +848,80 @@ class AuthTest extends CakeTestCase {
'AuthMockThree' 'AuthMockThree'
); );
$mocks = $this->Controller->Auth->loadAuthorizeObjects(); $mocks = $this->Controller->Auth->loadAuthorizeObjects();
$request = $this->Controller->request;
$this->assertEquals(3, count($mocks)); $this->assertEquals(3, count($mocks));
$mocks[0]->expects($this->once()) $mocks[0]->expects($this->once())
->method('authorize') ->method('authorize')
->with(array('User')) ->with(array('User'), $request)
->will($this->returnValue(false)); ->will($this->returnValue(false));
$mocks[1]->expects($this->once()) $mocks[1]->expects($this->once())
->method('authorize') ->method('authorize')
->with(array('User')) ->with(array('User'), $request)
->will($this->returnValue(true)); ->will($this->returnValue(true));
$mocks[2]->expects($this->never()) $mocks[2]->expects($this->never())
->method('authorize'); ->method('authorize');
$this->assertTrue($this->Controller->Auth->isAuthorized(array('User'))); $this->assertTrue($this->Controller->Auth->isAuthorized(array('User'), $request));
}
/**
* test that loadAuthorize resets the loaded objects each time.
*
* @return void
*/
function testLoadAuthorizeResets() {
$this->Controller->Auth->authorize = array(
'Controller'
);
$result = $this->Controller->Auth->loadAuthorizeObjects();
$this->assertEquals(1, count($result));
$result = $this->Controller->Auth->loadAuthorizeObjects();
$this->assertEquals(1, count($result));
}
/**
* @expectedException CakeException
* @return void
*/
function testLoadAuthenticateNoFile() {
$this->Controller->Auth->authenticate = 'Missing';
$this->Controller->Auth->identify($this->Controller->request);
}
/**
* test that loadAuthorize resets the loaded objects each time.
*
* @return void
*/
function testLoadAuthenticateResets() {
$this->Controller->Auth->authenticate = array(
'Form'
);
$result = $this->Controller->Auth->loadAuthenticateObjects();
$this->assertEquals(1, count($result));
$result = $this->Controller->Auth->loadAuthenticateObjects();
$this->assertEquals(1, count($result));
}
/**
* test that loadAuthenticate merges in legacy authentication settings.
*
* @return void
*/
function testLoadAuthenticateSettingsPass() {
$this->Controller->Auth->userModel = 'AuthUser';
$this->Controller->Auth->userScope = array('AuthUser.active' => 1);
$this->Controller->Auth->fields = array('username' => 'user', 'password' => 'passwd');
$this->Controller->Auth->authenticate = array('Form');
$objects = $this->Controller->Auth->loadAuthenticateObjects();
$result = $objects[0];
$this->assertEquals($result->settings['userModel'], 'AuthUser');
} }
/** /**
@ -1463,7 +1519,8 @@ class AuthTest extends CakeTestCase {
$authUser = $PluginModel->find(); $authUser = $PluginModel->find();
$this->Controller->request->data['TestPluginAuthUser'] = array( $this->Controller->request->data['TestPluginAuthUser'] = array(
'username' => $authUser['TestPluginAuthUser']['username'], 'password' => 'cake' 'username' => $authUser['TestPluginAuthUser']['username'],
'password' => 'cake'
); );
$this->Controller->request->addParams(Router::parse('auth_test/login')); $this->Controller->request->addParams(Router::parse('auth_test/login'));
@ -1476,8 +1533,12 @@ class AuthTest extends CakeTestCase {
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
$user = $this->Controller->Auth->user(); $user = $this->Controller->Auth->user();
$expected = array('TestPluginAuthUser' => array( $expected = array(
'id' => 1, 'username' => 'gwoo', 'created' => '2007-03-17 01:16:23', 'updated' => date('Y-m-d H:i:s') 'TestPluginAuthUser' => array(
'id' => 1,
'username' => 'gwoo',
'created' => '2007-03-17 01:16:23',
'updated' => date('Y-m-d H:i:s')
)); ));
$this->assertEqual($user, $expected); $this->assertEqual($user, $expected);
$sessionKey = $this->Controller->Auth->sessionKey; $sessionKey = $this->Controller->Auth->sessionKey;