From 68da3fab8f8aba726db74156e35f20bb966c752f Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 2 Jan 2011 00:57:23 -0500 Subject: [PATCH 01/65] Starting to extract authentication objects out of AuthComponent. Started off with extracting FormAuthenticate as its what currently exists in AuthComponent. Test case added. --- .../components/auth/form_authenticate.php | 70 +++++++++ .../auth/form_authenticate.test.php | 133 ++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 cake/libs/controller/components/auth/form_authenticate.php create mode 100644 cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php new file mode 100644 index 000000000..324bff557 --- /dev/null +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -0,0 +1,70 @@ + 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); + } + +/** + * 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. + * @return mixed. False on login failure. An array of User data on success. + */ + public function authenticate(CakeRequest $request) { + $userModel = $this->settings['userModel']; + $fields = $this->settings['fields']; + if (empty($request->data[$userModel])) { + return false; + } + if ( + empty($request->data[$userModel][$fields['username']]) || + empty($request->data[$userModel][$fields['password']]) + ) { + return false; + } + $conditions = array( + $userModel . '.' . $fields['username'] => $request->data[$userModel][$fields['username']], + $userModel . '.' . $fields['password'] => $request->data[$userModel][$fields['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[$userModel])) { + return false; + } + unset($result[$userModel][$fields['password']]); + return $result[$userModel]; + } +} \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php new file mode 100644 index 000000000..940c973e0 --- /dev/null +++ b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php @@ -0,0 +1,133 @@ +auth = new FormAuthenticate(array( + 'fields' => array('username' => 'user', 'password' => 'password'), + 'userModel' => 'User' + )); + $this->password = Security::hash('password', null, true); + ClassRegistry::init('User')->updateAll(array('password' => '"' . $this->password . '"')); + } + +/** + * 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)); + } + +/** + * 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)); + } + +/** + * 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)); + } + +/** + * 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)); + } + +/** + * test authenticate sucesss + * + * @return void + */ + function testAuthenticateSuccess() { + $request = new CakeRequest('posts/index', false); + $request->data = array('User' => array( + 'user' => 'mariano', + 'password' => $this->password + )); + $result = $this->auth->authenticate($request); + $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' => $this->password + )); + + $this->assertFalse($this->auth->authenticate($request)); + } + +} \ No newline at end of file From 1c827573ceb9f376c3b2acdeaf2e5605328c2ea1 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 2 Jan 2011 12:31:48 -0500 Subject: [PATCH 02/65] Adding doc blocks for FormAuthenticate. --- .../components/auth/form_authenticate.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php index 324bff557..e337b9715 100644 --- a/cake/libs/controller/components/auth/form_authenticate.php +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -1,5 +1,36 @@ 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 + */ class FormAuthenticate { /** From 693360bc9e27ff08eb5341c82b9f13aae0b88911 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 2 Jan 2011 13:21:21 -0500 Subject: [PATCH 03/65] Removing isAuthorized() as there is no need for it once AuthComponent is updated. --- .../components/auth/form_authenticate.php | 1 + cake/libs/controller/controller.php | 14 -------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php index e337b9715..e4ffb2dc6 100644 --- a/cake/libs/controller/components/auth/form_authenticate.php +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -30,6 +30,7 @@ * * @package cake.libs.controller.components.auth * @since 2.0 + * @see AuthComponent::$authenticate */ class FormAuthenticate { diff --git a/cake/libs/controller/controller.php b/cake/libs/controller/controller.php index 3929d7ed9..521b47503 100644 --- a/cake/libs/controller/controller.php +++ b/cake/libs/controller/controller.php @@ -756,20 +756,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. * From 16b3beec5ef4e0fcf47b5a87857d2bea01a9e301 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 2 Jan 2011 13:35:43 -0500 Subject: [PATCH 04/65] Starting ControllerAuthorize adding it and the test cases. --- .../components/auth/controller_authorize.php | 52 ++++++++++++++++ .../auth/controller_authorize.test.php | 62 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 cake/libs/controller/components/auth/controller_authorize.php create mode 100644 cake/tests/cases/libs/controller/components/auth/controller_authorize.test.php diff --git a/cake/libs/controller/components/auth/controller_authorize.php b/cake/libs/controller/components/auth/controller_authorize.php new file mode 100644 index 000000000..ed78ac5bd --- /dev/null +++ b/cake/libs/controller/components/auth/controller_authorize.php @@ -0,0 +1,52 @@ +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); + } + +/** + * 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')); + } + if (!method_exists($controller, 'isAuthorized')) { + throw new CakeException(__('$controller does not implement an isAuthorized() method.')); + } + $this->_controller = $controller; + return true; + } + return $this->_controller; + } +} \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth/controller_authorize.test.php b/cake/tests/cases/libs/controller/components/auth/controller_authorize.test.php new file mode 100644 index 000000000..9863d23de --- /dev/null +++ b/cake/tests/cases/libs/controller/components/auth/controller_authorize.test.php @@ -0,0 +1,62 @@ +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)); + } +} From 67c9acbc94c5399f27cdf801d2492a7fe71af6b0 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 2 Jan 2011 13:39:48 -0500 Subject: [PATCH 05/65] Adding a doc block. --- .../components/auth/controller_authorize.php | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth/controller_authorize.php b/cake/libs/controller/components/auth/controller_authorize.php index ed78ac5bd..eea082111 100644 --- a/cake/libs/controller/components/auth/controller_authorize.php +++ b/cake/libs/controller/components/auth/controller_authorize.php @@ -1,6 +1,38 @@ 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 { /** * Controller for the request. From 4058e7f48c54c8d4040cae097c483b76e5f6ee29 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 2 Jan 2011 14:23:43 -0500 Subject: [PATCH 06/65] Extracting a base class, as it will be needed. Moving AuthComponent::action() into the authorize object base as that's where its used. --- .../components/auth/base_authorize.php | 90 +++++++++++++++++++ .../components/auth/controller_authorize.php | 43 +++------ 2 files changed, 102 insertions(+), 31 deletions(-) create mode 100644 cake/libs/controller/components/auth/base_authorize.php diff --git a/cake/libs/controller/components/auth/base_authorize.php b/cake/libs/controller/components/auth/base_authorize.php new file mode 100644 index 000000000..a7828eae5 --- /dev/null +++ b/cake/libs/controller/components/auth/base_authorize.php @@ -0,0 +1,90 @@ +action(); + * + * @var string + */ + public $actionPath = null; + +/** + * 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); + } + +/** + * 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->actionPath . $path + ); + } +} \ No newline at end of file diff --git a/cake/libs/controller/components/auth/controller_authorize.php b/cake/libs/controller/components/auth/controller_authorize.php index eea082111..720242e0b 100644 --- a/cake/libs/controller/components/auth/controller_authorize.php +++ b/cake/libs/controller/components/auth/controller_authorize.php @@ -12,6 +12,7 @@ * @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. @@ -33,22 +34,21 @@ * @since 2.0 * @see AuthComponent::$authenticate */ -class ControllerAuthorize { -/** - * Controller for the request. - * - * @var Controller - */ - protected $_controller = null; +class ControllerAuthorize extends BaseAuthorize { /** - * Constructor + * Get/set the controller this authorize object will be working with. Also checks that isAuthorized is implemented. * - * @param Controller $controller The controller for this request. - * @param string $settings An array of settings. This class does not use any settings. + * @param mixed $controller null to get, a controller to set. + * @return mixed. */ - public function __construct(Controller $controller, $settings = array()) { - $this->controller($controller); + 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); } /** @@ -62,23 +62,4 @@ class ControllerAuthorize { return (bool) $this->_controller->isAuthorized($user); } -/** - * 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')); - } - if (!method_exists($controller, 'isAuthorized')) { - throw new CakeException(__('$controller does not implement an isAuthorized() method.')); - } - $this->_controller = $controller; - return true; - } - return $this->_controller; - } } \ No newline at end of file From 7207dccc7cebd641913796ff0270ff451818cfa5 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 2 Jan 2011 14:24:25 -0500 Subject: [PATCH 07/65] Adding ActionsAuthorize. It implements using Acl as the authorization object. --- .../components/auth/actions_authorize.php | 39 ++++++ .../auth/actions_authorize.test.php | 130 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 cake/libs/controller/components/auth/actions_authorize.php create mode 100644 cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php diff --git a/cake/libs/controller/components/auth/actions_authorize.php b/cake/libs/controller/components/auth/actions_authorize.php new file mode 100644 index 000000000..7f4f722d7 --- /dev/null +++ b/cake/libs/controller/components/auth/actions_authorize.php @@ -0,0 +1,39 @@ +_controller->Components->load('Acl'); + return $Acl->check($user, $this->action($request)); + } +} \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php b/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php new file mode 100644 index 000000000..5aedbda04 --- /dev/null +++ b/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php @@ -0,0 +1,130 @@ +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->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); + } +} From 2e9d9479a6b4361745919b1f1c7692c95bc90f1d Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 3 Jan 2011 01:49:06 -0500 Subject: [PATCH 08/65] Moving another part of AuthComponent's authorize strategies into BaseAuthorize. Implementing CrudAuthorize and adding tests for it. --- .../components/auth/base_authorize.php | 37 +++++ .../components/auth/crud_authorize.php | 59 +++++++ .../components/auth/crud_authorize.test.php | 149 ++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 cake/libs/controller/components/auth/crud_authorize.php create mode 100644 cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php diff --git a/cake/libs/controller/components/auth/base_authorize.php b/cake/libs/controller/components/auth/base_authorize.php index a7828eae5..cc19acb26 100644 --- a/cake/libs/controller/components/auth/base_authorize.php +++ b/cake/libs/controller/components/auth/base_authorize.php @@ -36,6 +36,21 @@ abstract class BaseAuthorize { */ public $actionPath = null; +/** + * Action -> crud mappings. Used by authorization objects that want to map actions to CRUD roles. + * + * @var array + * @see CrudAuthorize + */ + protected $_actionMap = array( + 'index' => 'read', + 'add' => 'create', + 'edit' => 'update', + 'view' => 'read', + 'delete' => 'delete', + 'remove' => 'delete' + ); + /** * Constructor * @@ -87,4 +102,26 @@ abstract class BaseAuthorize { $this->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->_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->_actionMap[$typedAction] = $action; + } + } else { + $this->_actionMap[$action] = $type; + } + } + } } \ No newline at end of file diff --git a/cake/libs/controller/components/auth/crud_authorize.php b/cake/libs/controller/components/auth/crud_authorize.php new file mode 100644 index 000000000..5e6c9c85c --- /dev/null +++ b/cake/libs/controller/components/auth/crud_authorize.php @@ -0,0 +1,59 @@ +_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->_actionMap[$request->params['action']] + ); + } +} \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php b/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php new file mode 100644 index 000000000..684fc4b97 --- /dev/null +++ b/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php @@ -0,0 +1,149 @@ +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( + 'index' => 'read', + 'add' => 'create', + 'edit' => 'update', + 'view' => 'read', + 'delete' => 'delete', + '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( + '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); + } + +} From 5ae194ec905d28d52a21a6b96a9ed00254caa7bb Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 3 Jan 2011 12:58:49 -0500 Subject: [PATCH 09/65] Updating group test case, and fixing test case classname. --- cake/tests/cases/libs/all_components.test.php | 1 + .../libs/controller/components/auth/crud_authorize.test.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cake/tests/cases/libs/all_components.test.php b/cake/tests/cases/libs/all_components.test.php index 28e59aadf..480c93219 100644 --- a/cake/tests/cases/libs/all_components.test.php +++ b/cake/tests/cases/libs/all_components.test.php @@ -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; } } diff --git a/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php b/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php index 684fc4b97..6382bed4f 100644 --- a/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php +++ b/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php @@ -6,7 +6,7 @@ App::import('Component', 'Acl'); App::import('Core', 'CakeRequest'); App::import('Core', 'Controller'); -class ActionsAuthorizeTest extends CakeTestCase { +class CrudAuthorizeTest extends CakeTestCase { /** * setup From ca9aabdfec3da8c0cd2b10ffa31f3e7f1d5a0636 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 3 Jan 2011 13:00:06 -0500 Subject: [PATCH 10/65] Adding file headers. --- .../components/auth/actions_authorize.test.php | 15 ++++++++++++++- .../components/auth/controller_authorize.test.php | 15 ++++++++++++++- .../components/auth/crud_authorize.test.php | 15 ++++++++++++++- .../components/auth/form_authenticate.test.php | 14 ++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php b/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php index 5aedbda04..7af508c9c 100644 --- a/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php +++ b/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php @@ -1,5 +1,18 @@ Date: Mon, 3 Jan 2011 16:48:16 -0500 Subject: [PATCH 11/65] Starting to integrate Authorization objects into AuthComponent. Tests updated and duplicate tests skipped, they will eventually be removed when duplication is confirmed. --- cake/libs/controller/components/auth.php | 153 +++++------------- .../libs/controller/components/auth.test.php | 58 ++++++- 2 files changed, 99 insertions(+), 112 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 063960f91..c6429a419 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -21,6 +21,7 @@ App::import('Core', 'Router', false); App::import('Core', 'Security', false); +App::import('Component', 'auth/base_authorize'); /** * Authentication control component class @@ -67,6 +68,13 @@ class AuthComponent extends Component { */ 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 @@ -400,42 +408,8 @@ class AuthComponent extends Component { if (!$this->authorize) { 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)) { + + if ($this->isAuthorized()) { return true; } @@ -478,91 +452,48 @@ 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. * @return boolean True if $user is authorized, otherwise false */ - public function isAuthorized($type = null, $object = null, $user = null) { + public function isAuthorized($user = null) { if (empty($user) && !$this->user()) { return false; } elseif (empty($user)) { $user = $this->user(); } - - extract($this->__authType($type)); - - if (!$object) { - $object = $this->object; - } - - $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: + $this->loadAuthorizeObjects(); + foreach ($this->_authorizeObjects as $authorizer) { + if ($authorizer->authorize($user, $this->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; + } + +/** + * Loads the authorization objects configured. + * + * @return mixed Either null when authorize is empty, or the loaded authorization objects. + */ + public function loadAuthorizeObjects() { + if (empty($this->authorize)) { + return; + } + foreach (Set::normalize($this->authorize) 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.')); + } + $this->_authorizeObjects[] = new $className($this->_Collection->getController(), $settings); + } + return $this->_authorizeObjects; } /** diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 371edd2ed..54689c841 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -54,6 +54,7 @@ class TestAuthComponent extends AuthComponent { function _stop($status = 0) { $this->testStop = true; } + } /** @@ -684,6 +685,8 @@ class AuthTest extends CakeTestCase { * @return void */ function testAuthorizeController() { + $this->markTestSkipped('This is already tested in ControllerAuthorizeTest'); + $this->AuthUser = new AuthUser(); $user = $this->AuthUser->find(); $this->Controller->Session->write('Auth', $user); @@ -708,6 +711,8 @@ class AuthTest extends CakeTestCase { * @return void */ function testAuthorizeModel() { + $this->markTestSkipped('This is not implemented'); + $this->AuthUser = new AuthUser(); $user = $this->AuthUser->find(); $this->Controller->Session->write('Auth', $user); @@ -734,6 +739,8 @@ class AuthTest extends CakeTestCase { * @return void */ function testAuthorizeCrud() { + $this->markTestSkipped('This is already tested in CrudAuthorizeTest'); + $this->AuthUser = new AuthUser(); $user = $this->AuthUser->find(); $this->Controller->Session->write('Auth', $user); @@ -795,6 +802,8 @@ class AuthTest extends CakeTestCase { * @return void */ function testAuthorizeActions() { + $this->markTestSkipped('This is already tested in ActionsAuthorizeTest'); + $this->AuthUser = new AuthUser(); $user = $this->AuthUser->find(); $this->Controller->Session->write('Auth', $user); @@ -816,6 +825,49 @@ class AuthTest extends CakeTestCase { $this->assertTrue($this->Controller->Auth->isAuthorized()); } +/** + * @expectedException CakeException + * @return void + */ + function testIsAuthorizedMissingFile() { + $this->Controller->Auth->authorize = 'Missing'; + $this->Controller->Auth->isAuthorized(array('User' => array('id' => 1))); + } + +/** + * test that isAuthroized calls methods correctly + * + * @return void + */ + function testIsAuthorizedDelegation() { + $this->getMock('BaseAuthorize', array('authorize'), array(), 'AuthMockOneAuthorize', false); + $this->getMock('BaseAuthorize', array('authorize'), array(), 'AuthMockTwoAuthorize', false); + $this->getMock('BaseAuthorize', array('authorize'), array(), 'AuthMockThreeAuthorize', false); + + $this->Controller->Auth->authorize = array( + 'AuthMockOne', + 'AuthMockTwo', + 'AuthMockThree' + ); + $mocks = $this->Controller->Auth->loadAuthorizeObjects(); + + $this->assertEquals(3, count($mocks)); + $mocks[0]->expects($this->once()) + ->method('authorize') + ->with(array('User')) + ->will($this->returnValue(false)); + + $mocks[1]->expects($this->once()) + ->method('authorize') + ->with(array('User')) + ->will($this->returnValue(true)); + + $mocks[2]->expects($this->never()) + ->method('authorize'); + + $this->assertTrue($this->Controller->Auth->isAuthorized(array('User'))); + } + /** * Tests that deny always takes precedence over allow * @@ -1136,6 +1188,8 @@ class AuthTest extends CakeTestCase { * @return void */ function testEmptyUsernameOrPassword() { + $this->markTestSkipped('This is already tested in FormAuthenticateTest'); + $this->AuthUser = new AuthUser(); $user['id'] = 1; $user['username'] = 'mariano'; @@ -1168,6 +1222,8 @@ class AuthTest extends CakeTestCase { * @return void */ function testInjection() { + $this->markTestSkipped('This is already tested in FormAuthenticateTest'); + $this->AuthUser = new AuthUser(); $this->AuthUser->id = 2; $this->AuthUser->saveField('password', Security::hash(Configure::read('Security.salt') . 'cake')); @@ -1326,6 +1382,7 @@ class AuthTest extends CakeTestCase { * @return void */ function testCustomField() { + $this->markTestSkipped('This is already tested in FormAuthenticateTest'); Router::reload(); $this->AuthUserCustomField = new AuthUserCustomField(); @@ -1544,7 +1601,6 @@ class AuthTest extends CakeTestCase { * @return void */ function testComponentSettings() { - $request = new CakeRequest(null, false); $this->Controller = new AuthTestController($request); From ced832ba62012e1fe40bde834bbf4a8872234d1f Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 3 Jan 2011 18:17:27 -0500 Subject: [PATCH 12/65] Fixing issues with FormAuthenticate and plugin models. --- .../components/auth/form_authenticate.php | 18 +++++---- .../auth/form_authenticate.test.php | 39 ++++++++++++++++++- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php index e4ffb2dc6..d536499b5 100644 --- a/cake/libs/controller/components/auth/form_authenticate.php +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -72,19 +72,21 @@ class FormAuthenticate { */ public function authenticate(CakeRequest $request) { $userModel = $this->settings['userModel']; + list($plugin, $model) = pluginSplit($userModel); + $fields = $this->settings['fields']; - if (empty($request->data[$userModel])) { + if (empty($request->data[$model])) { return false; } if ( - empty($request->data[$userModel][$fields['username']]) || - empty($request->data[$userModel][$fields['password']]) + empty($request->data[$model][$fields['username']]) || + empty($request->data[$model][$fields['password']]) ) { return false; } $conditions = array( - $userModel . '.' . $fields['username'] => $request->data[$userModel][$fields['username']], - $userModel . '.' . $fields['password'] => $request->data[$userModel][$fields['password']], + $model . '.' . $fields['username'] => $request->data[$model][$fields['username']], + $model . '.' . $fields['password'] => $request->data[$model][$fields['password']], ); if (!empty($this->settings['scope'])) { $conditions = array_merge($conditions, $this->settings['scope']); @@ -93,10 +95,10 @@ class FormAuthenticate { 'conditions' => $conditions, 'recursive' => 0 )); - if (empty($result) || empty($result[$userModel])) { + if (empty($result) || empty($result[$model])) { return false; } - unset($result[$userModel][$fields['password']]); - return $result[$userModel]; + unset($result[$model][$fields['password']]); + return $result[$model]; } } \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php index fe6547251..0c5e36a18 100644 --- a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php @@ -28,7 +28,7 @@ require_once CAKE_TESTS . 'cases' . DS . 'libs' . DS . 'model' . DS . 'models.p */ class FormAuthenticateTest extends CakeTestCase { - public $fixtures = array('core.user'); + public $fixtures = array('core.user', 'core.auth_user'); /** * setup @@ -144,4 +144,41 @@ class FormAuthenticateTest extends CakeTestCase { $this->assertFalse($this->auth->authenticate($request)); } +/** + * 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' => Security::hash('cake', null, true) + )); + + $result = $this->auth->authenticate($request); + $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); + } + } \ No newline at end of file From 3f9c83c43d2d2b8b9086cec83d7f3ce19d9e4b36 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 3 Jan 2011 18:18:42 -0500 Subject: [PATCH 13/65] Authentication and Authorization objects are integrated with AuthComponent. Minimal App changes are needed as legacy settings are forwarded. --- cake/libs/controller/components/auth.php | 167 ++++++++---------- .../libs/controller/components/auth.test.php | 103 ++++++++--- 2 files changed, 158 insertions(+), 112 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index c6429a419..a7993c249 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -48,20 +48,36 @@ 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 * @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(); + +/** + * 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 * @link http://book.cakephp.org/view/1275/authorize @@ -363,15 +379,7 @@ class AuthComponent extends Component { !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->login()) { if ($this->autoRedirect) { $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 * 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 */ - public function isAuthorized($user = null) { + public function isAuthorized($user = null, $request = null) { if (empty($user) && !$this->user()) { return false; } elseif (empty($user)) { $user = $this->user(); } - $this->loadAuthorizeObjects(); + if (empty($request)) { + $request = $this->request; + } + if (empty($this->_authorizeObjects)) { + $this->loadAuthorizeObjects(); + } foreach ($this->_authorizeObjects as $authorizer) { - if ($authorizer->authorize($user, $this->request) === true) { + if ($authorizer->authorize($user, $request) === true) { return true; } } @@ -483,6 +497,7 @@ class AuthComponent extends Component { if (empty($this->authorize)) { return; } + $this->_authorizeObjects = array(); foreach (Set::normalize($this->authorize) as $class => $settings) { $className = $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 * @link http://book.cakephp.org/view/1261/login */ - public function login($data = null) { + public function login($request = null) { $this->__setDefaults(); $this->_loggedIn = false; - if (empty($data)) { - $data = $this->data; + if (empty($request)) { + $request = $this->request; } - - if ($user = $this->identify($data)) { + if ($user = $this->identify($request)) { $this->Session->write($this->sessionKey, $user); $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. - * 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. + * @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 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; + public function identify(CakeRequest $request) { + if (empty($this->_authenticateObjects)) { + $this->loadAuthenticateObjects(); } - $model = $this->getModel(); - if (empty($user)) { - $user = $this->user(); - if (empty($user)) { - return null; + foreach ($this->_authenticateObjects as $auth) { + $result = $auth->authenticate($request); + if (!empty($result) && is_array($result)) { + return $result; } - } elseif (is_object($user) && is_a($user, 'Model')) { - if (!$user->exists()) { - return null; - } - $user = $user->read(); - $user = $user[$model->alias]; - } elseif (is_array($user) && isset($user[$model->alias])) { - $user = $user[$model->alias]; } + return false; + } - 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; - } +/** + * loads the configured authentication objects. + * + * @return mixed either null on empty authenticate value, or an array of loaded objects. + */ + public function loadAuthenticateObjects() { + if (empty($this->authenticate)) { + return; } - - if (!empty($data)) { - if (!empty($data[$model->alias][$this->fields['password']])) { - unset($data[$model->alias][$this->fields['password']]); + $this->_authenticateObjects = array(); + foreach (Set::normalize($this->authenticate) 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)); } - return $data[$model->alias]; + if (!method_exists($className, 'authenticate')) { + throw new CakeException(__('Authentication objects must implement an authenticate method.')); + } + foreach ($this->_authenticateLegacyMap as $old => $new) { + if (empty($settings[$new]) && !empty($this->{$old})) { + $settings[$new] = $this->{$old}; + } + } + $this->_authenticateObjects[] = new $className($settings); } - return null; + return $this->_authenticateObjects; } /** diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 54689c841..27f9b6c10 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -582,13 +582,17 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->startup($this->Controller); $user = $this->Controller->Auth->user(); $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->Controller->Session->delete('Auth'); $this->Controller->request->data['AuthUser'] = array( - 'username' => 'blah', 'password' => '' + 'username' => 'blah', + 'password' => '' ); $this->Controller->Auth->startup($this->Controller); @@ -598,7 +602,8 @@ class AuthTest extends CakeTestCase { $this->Controller->Session->delete('Auth'); $this->Controller->request->data['AuthUser'] = array( - 'username' => 'now() or 1=1 --', 'password' => '' + 'username' => 'now() or 1=1 --', + 'password' => '' ); $this->Controller->Auth->startup($this->Controller); @@ -608,7 +613,8 @@ class AuthTest extends CakeTestCase { $this->Controller->Session->delete('Auth'); $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); @@ -617,14 +623,6 @@ class AuthTest extends CakeTestCase { $this->assertNull($user); $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'); } @@ -850,22 +848,80 @@ class AuthTest extends CakeTestCase { 'AuthMockThree' ); $mocks = $this->Controller->Auth->loadAuthorizeObjects(); + $request = $this->Controller->request; $this->assertEquals(3, count($mocks)); $mocks[0]->expects($this->once()) ->method('authorize') - ->with(array('User')) + ->with(array('User'), $request) ->will($this->returnValue(false)); - + $mocks[1]->expects($this->once()) ->method('authorize') - ->with(array('User')) + ->with(array('User'), $request) ->will($this->returnValue(true)); - + $mocks[2]->expects($this->never()) ->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(); $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')); @@ -1476,8 +1533,12 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->startup($this->Controller); $user = $this->Controller->Auth->user(); - $expected = array('TestPluginAuthUser' => array( - 'id' => 1, 'username' => 'gwoo', 'created' => '2007-03-17 01:16:23', 'updated' => date('Y-m-d H:i:s') + $expected = array( + '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); $sessionKey = $this->Controller->Auth->sessionKey; From e96b20b93ea8004f0ec15d448d2d0156d29bcc43 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 4 Jan 2011 22:44:38 -0500 Subject: [PATCH 14/65] Removing skipped tests that cover features in other classes and tests. --- .../libs/controller/components/auth.test.php | 257 ------------------ 1 file changed, 257 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 27f9b6c10..13a92c91f 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -676,31 +676,6 @@ class AuthTest extends CakeTestCase { $this->assertFalse($result); } -/** - * testAuthorizeController method - * - * @access public - * @return void - */ - function testAuthorizeController() { - $this->markTestSkipped('This is already tested in ControllerAuthorizeTest'); - - $this->AuthUser = new AuthUser(); - $user = $this->AuthUser->find(); - $this->Controller->Session->write('Auth', $user); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->authorize = 'controller'; - $this->Controller->request->addParams(Router::parse('auth_test/add')); - $result = $this->Controller->Auth->startup($this->Controller); - $this->assertTrue($result); - - $this->Controller->request['testControllerAuth'] = 1; - $result = $this->Controller->Auth->startup($this->Controller); - $this->assertTrue($this->Controller->Session->check('Message.auth')); - $this->assertFalse($result); - - $this->Controller->Session->delete('Auth'); - } /** * testAuthorizeModel method @@ -730,99 +705,6 @@ class AuthTest extends CakeTestCase { $this->assertFalse($result); } -/** - * testAuthorizeCrud method - * - * @access public - * @return void - */ - function testAuthorizeCrud() { - $this->markTestSkipped('This is already tested in CrudAuthorizeTest'); - - $this->AuthUser = new AuthUser(); - $user = $this->AuthUser->find(); - $this->Controller->Session->write('Auth', $user); - - $this->Controller->request['controller'] = 'auth_test'; - $this->Controller->request['action'] = 'add'; - - $this->Controller->Acl->name = 'DbAclTest'; - - $this->Controller->Acl->Aro->id = null; - $this->Controller->Acl->Aro->create(array('alias' => 'Roles')); - $result = $this->Controller->Acl->Aro->save(); - $this->assertFalse(empty($result)); - - $parent = $this->Controller->Acl->Aro->id; - - $this->Controller->Acl->Aro->create(array('parent_id' => $parent, 'alias' => 'Admin')); - $result = $this->Controller->Acl->Aro->save(); - $this->assertFalse(empty($result)); - - $parent = $this->Controller->Acl->Aro->id; - - $this->Controller->Acl->Aro->create(array( - 'model' => 'AuthUser', 'parent_id' => $parent, 'foreign_key' => 1, 'alias'=> 'mariano' - )); - $result = $this->Controller->Acl->Aro->save(); - $this->assertFalse(empty($result)); - - $this->Controller->Acl->Aco->create(array('alias' => 'Root')); - $result = $this->Controller->Acl->Aco->save(); - $this->assertFalse(empty($result)); - - $parent = $this->Controller->Acl->Aco->id; - - $this->Controller->Acl->Aco->create(array('parent_id' => $parent, 'alias' => 'AuthTest')); - $result = $this->Controller->Acl->Aco->save(); - $this->assertFalse(empty($result)); - - $this->Controller->Acl->allow('Roles/Admin', 'Root'); - $this->Controller->Acl->allow('Roles/Admin', 'Root/AuthTest'); - - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->authorize = 'crud'; - $this->Controller->Auth->actionPath = 'Root/'; - - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue($this->Controller->Auth->isAuthorized()); - - $this->Controller->Session->delete('Auth'); - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue($this->Controller->Session->check('Message.auth')); - } - -/** - * test authorize = 'actions' setting. - * - * @return void - */ - function testAuthorizeActions() { - $this->markTestSkipped('This is already tested in ActionsAuthorizeTest'); - - $this->AuthUser = new AuthUser(); - $user = $this->AuthUser->find(); - $this->Controller->Session->write('Auth', $user); - $this->Controller->request['controller'] = 'auth_test'; - $this->Controller->request['action'] = 'add'; - - $this->Controller->Acl = $this->getMock('AclComponent', array(), array(), '', false); - $this->Controller->Acl->expects($this->atLeastOnce())->method('check')->will($this->returnValue(true)); - - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->authorize = 'actions'; - $this->Controller->Auth->actionPath = 'Root/'; - - $this->Controller->Acl->expects($this->at(0))->method('check')->with($user, 'Root/AuthTest/add'); - - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue($this->Controller->Auth->isAuthorized()); - } - /** * @expectedException CakeException * @return void @@ -1237,110 +1119,6 @@ class AuthTest extends CakeTestCase { $this->assertTrue($result, 'Auth redirected a missing action %s'); } -/** - * testEmptyUsernameOrPassword method - * - * @access public - * @return void - */ - function testEmptyUsernameOrPassword() { - $this->markTestSkipped('This is already tested in FormAuthenticateTest'); - - $this->AuthUser = new AuthUser(); - $user['id'] = 1; - $user['username'] = 'mariano'; - $user['password'] = Security::hash(Configure::read('Security.salt') . 'cake'); - $this->AuthUser->save($user, false); - - $authUser = $this->AuthUser->find(); - - $this->Controller->request->data['AuthUser'] = array( - 'username' => '', 'password' => '' - ); - - $this->Controller->request->addParams(Router::parse('auth_test/login')); - $this->Controller->request->query['url'] = 'auth_test/login'; - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = 'auth_test/login'; - $this->Controller->Auth->userModel = 'AuthUser'; - - $this->Controller->Auth->startup($this->Controller); - $user = $this->Controller->Auth->user(); - $this->assertTrue($this->Controller->Session->check('Message.auth')); - $this->assertEqual($user, false); - $this->Controller->Session->delete('Auth'); - } - -/** - * testInjection method - * - * @access public - * @return void - */ - function testInjection() { - $this->markTestSkipped('This is already tested in FormAuthenticateTest'); - - $this->AuthUser = new AuthUser(); - $this->AuthUser->id = 2; - $this->AuthUser->saveField('password', Security::hash(Configure::read('Security.salt') . 'cake')); - - $this->Controller->request->data['AuthUser'] = array( - 'username' => 'nate', 'password' => 'cake' - ); - - $this->Controller->request->addParams(Router::parse('auth_test/login')); - $this->Controller->request->query['url'] = 'auth_test/login'; - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->loginAction = 'auth_test/login'; - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue(is_array($this->Controller->Auth->user())); - - $this->Controller->Session->delete($this->Controller->Auth->sessionKey); - - $this->Controller->request->data = array( - 'AuthUser' => array( - 'username' => 'nate', - 'password' => 'cake1' - ) - ); - $this->Controller->request->query['url'] = 'auth_test/login'; - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->loginAction = 'auth_test/login'; - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue(is_null($this->Controller->Auth->user())); - - $this->Controller->Session->delete($this->Controller->Auth->sessionKey); - - $this->Controller->request->data = array( - 'AuthUser' => array( - 'username' => '> n', - 'password' => 'cake' - ) - ); - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue(is_null($this->Controller->Auth->user())); - - unset($this->Controller->request->data['AuthUser']['password']); - $this->Controller->request->data['AuthUser']['username'] = "1'1"; - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue(is_null($this->Controller->Auth->user())); - - unset($this->Controller->request->data['AuthUser']['username']); - $this->Controller->request->data['AuthUser']['password'] = "1'1"; - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue(is_null($this->Controller->Auth->user())); - } - /** * test Hashing of passwords * @@ -1431,41 +1209,6 @@ class AuthTest extends CakeTestCase { $this->assertTrue(!!$user); } -/** - * testCustomField method - * - * @access public - * @return void - */ - function testCustomField() { - $this->markTestSkipped('This is already tested in FormAuthenticateTest'); - Router::reload(); - - $this->AuthUserCustomField = new AuthUserCustomField(); - $user = array( - 'id' => 1, 'email' => 'harking@example.com', - 'password' => Security::hash(Configure::read('Security.salt') . 'cake' - )); - $user = $this->AuthUserCustomField->save($user, false); - - Router::connect('/', array('controller' => 'people', 'action' => 'login')); - $url = '/'; - $this->Controller->request->addParams(Router::parse($url)); - Router::setRequestInfo($this->Controller->request); - $this->Controller->request->data['AuthUserCustomField'] = array( - 'email' => 'harking@example.com', 'password' => 'cake' - ); - $this->Controller->request->query['url'] = substr($url, 1); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->fields = array('username' => 'email', 'password' => 'password'); - $this->Controller->Auth->loginAction = array('controller' => 'people', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUserCustomField'; - - $this->Controller->Auth->startup($this->Controller); - $user = $this->Controller->Auth->user(); - $this->assertTrue(!!$user); - } - /** * testAdminRoute method * From f21970c533ee64d7448be7f9cc05c1b636c80a6a Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 4 Jan 2011 23:12:37 -0500 Subject: [PATCH 15/65] Making AuthComponent::mapActions() delegate to the authorize objects. Adding tests. --- cake/libs/controller/components/auth.php | 40 ++++--------------- .../libs/controller/components/auth.test.php | 16 ++++++++ 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index a7993c249..244035d48 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -511,28 +511,6 @@ class AuthComponent extends Component { return $this->_authorizeObjects; } -/** - * Get authorization type - * - * @param string $auth Type of authorization - * @return array Associative array with: type, object - * @access private - */ - function __authType($auth = null) { - if ($auth == null) { - $auth = $this->authorize; - } - $object = null; - if (is_array($auth)) { - $type = key($auth); - $object = $auth[$type]; - } else { - $type = $auth; - return compact('type'); - } - return compact('type', 'object'); - } - /** * Takes a list of actions in the current controller for which authentication is not required, or * no parameters to allow all actions. @@ -580,22 +558,20 @@ 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->loadAuthorizeObjects(); + } + foreach ($this->_authorizeObjects as $auth) { + $auth->mapActions($map); } } diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 13a92c91f..4381c3a2d 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -1470,4 +1470,20 @@ class AuthTest extends CakeTestCase { $this->assertNull($this->Controller->Session->read('Auth.AuthUser')); $this->assertNull($this->Controller->Session->read('Auth.redirect')); } + +/** + * test mapActions loading and delegating to authorize objects. + * + * @return void + */ + function testMapActionsDelegation() { + $this->getMock('BaseAuthorize', array('authorize'), array(), 'MapActionMockAuthorize', false); + $this->Controller->Auth->authorize = array('MapActionMock'); + $mock = $this->Controller->Auth->loadAuthorizeObjects(); + $mock[0]->expects($this->once()) + ->method('mapActions') + ->with(array('create' => array('my_action'))); + + $this->Controller->Auth->mapActions(array('create' => array('my_action'))); + } } From 7ea914938fc0715a0cef6c5f878336d4998bd57f Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 4 Jan 2011 23:27:16 -0500 Subject: [PATCH 16/65] Changing AuthComponent::login() so you can provide an array of user data to manually login a user. Leaving $user blank will attempt to identify the user using the request. --- cake/libs/controller/components/auth.php | 26 ++++++------------- .../libs/controller/components/auth.test.php | 19 ++++++++++++++ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 244035d48..2ce617878 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -264,13 +264,6 @@ class AuthComponent extends Component { */ public $params = array(); -/** - * AclComponent instance if using Acl + Auth - * - * @var AclComponent - */ - public $Acl; - /** * Method list for bound controller * @@ -576,25 +569,22 @@ class AuthComponent extends Component { } /** - * 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, POST data from the current request will be used to identify a user. If the login 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($request = null) { + public function login($user = null) { $this->__setDefaults(); $this->_loggedIn = false; - if (empty($request)) { - $request = $this->request; + if (empty($user)) { + $user = $this->identify($this->request); } - if ($user = $this->identify($request)) { + if ($user) { $this->Session->write($this->sessionKey, $user); $this->_loggedIn = true; } diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 4381c3a2d..97115e5b6 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -1486,4 +1486,23 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->mapActions(array('create' => array('my_action'))); } + +/** + * test login() with user data + * + * @return void + */ + function testLoginWithUserData() { + $this->assertFalse($this->Controller->Auth->loggedIn()); + + $user = array( + 'username' => 'mariano', + 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', + 'created' => '2007-03-17 01:16:23', + 'updated' => '2007-03-17 01:18:31' + ); + $this->assertTrue($this->Controller->Auth->login($user)); + $this->assertTrue($this->Controller->Auth->loggedIn()); + $this->assertEquals($user['username'], $this->Controller->Auth->user('username')); + } } From e34fdde91858ed9ed0fc0c02c71fa83f060bb875 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 4 Jan 2011 23:33:09 -0500 Subject: [PATCH 17/65] Adding a test for logging users in using request data. --- .../libs/controller/components/auth.test.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 97115e5b6..d22b1ea92 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -18,6 +18,7 @@ */ App::import('Core', 'Controller'); App::import('Component', array('Auth', 'Acl')); +App::import('Component', 'auth/form_authenticate'); App::import('Model', 'DbAcl'); App::import('Core', 'Xml'); @@ -1487,6 +1488,28 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->mapActions(array('create' => array('my_action'))); } +/** + * test logging in with a request. + * + * @return void + */ + function testLoginWithRequestData() { + $this->getMock('FormAuthenticate', array(), array(), 'RequestLoginMockAuthenticate', false); + $request = new CakeRequest('users/login', false); + $user = array('username' => 'mark', 'role' => 'admin'); + + $this->Controller->Auth->request = $request; + $this->Controller->Auth->authenticate = array('RequestLoginMock'); + $mock = $this->Controller->Auth->loadAuthenticateObjects(); + $mock[0]->expects($this->once()) + ->method('authenticate') + ->with($request) + ->will($this->returnValue($user)); + + $this->assertTrue($this->Controller->Auth->login()); + $this->assertEquals($user['username'], $this->Controller->Auth->user('username')); + } + /** * test login() with user data * From 0c7f9149ca1674cee50c95bc6ff533ad46fbc446 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 5 Jan 2011 00:01:40 -0500 Subject: [PATCH 18/65] Refactoring authorization objects to also use settings, it makes them consistent with authenticate objects. Making actionPath automatically pass into authentication objects. Adding tests. --- cake/libs/controller/components/auth.php | 15 +++++++ .../components/auth/base_authorize.php | 39 +++++++++---------- .../libs/controller/components/auth.test.php | 14 +++++++ 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 2ce617878..40695b2aa 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -91,6 +91,16 @@ class AuthComponent extends Component { */ protected $_authorizeObjects = array(); +/** + * A hash mapping legacy properties => to settings passed into Authorize objects. + * + * @var string + * @deprecated Will be removed in 2.1+ + */ + protected $_authorizeLegacyMap = array( + 'actionPath' => 'actionPath', + ); + /** * The name of an optional view element to render when an Ajax request is made * with an invalid or expired session @@ -499,6 +509,11 @@ class AuthComponent extends Component { if (!method_exists($className, 'authorize')) { throw new CakeException(__('Authorization objects must implement an authorize method.')); } + foreach ($this->_authorizeLegacyMap as $old => $new) { + if (empty($settings[$new]) && !empty($this->{$old})) { + $settings[$new] = $this->{$old}; + } + } $this->_authorizeObjects[] = new $className($this->_Collection->getController(), $settings); } return $this->_authorizeObjects; diff --git a/cake/libs/controller/components/auth/base_authorize.php b/cake/libs/controller/components/auth/base_authorize.php index cc19acb26..cf9a2892f 100644 --- a/cake/libs/controller/components/auth/base_authorize.php +++ b/cake/libs/controller/components/auth/base_authorize.php @@ -29,26 +29,24 @@ abstract class BaseAuthorize { protected $_controller = null; /** - * The path to ACO nodes that contains the nodes for controllers. Used as a prefix - * when calling $this->action(); + * Settings for authorize objects. * - * @var string - */ - public $actionPath = null; - -/** - * Action -> crud mappings. Used by authorization objects that want to map actions to CRUD roles. + * - `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 - * @see CrudAuthorize */ - protected $_actionMap = array( - 'index' => 'read', - 'add' => 'create', - 'edit' => 'update', - 'view' => 'read', - 'delete' => 'delete', - 'remove' => 'delete' + public $settings = array( + 'actionPath' => null, + 'actionMap' => array( + 'index' => 'read', + 'add' => 'create', + 'edit' => 'update', + 'view' => 'read', + 'delete' => 'delete', + 'remove' => 'delete' + ) ); /** @@ -59,6 +57,7 @@ abstract class BaseAuthorize { */ public function __construct(Controller $controller, $settings = array()) { $this->controller($controller); + $this->settings = Set::merge($this->settings, $settings); } /** @@ -99,7 +98,7 @@ abstract class BaseAuthorize { return str_replace( array(':controller', ':action', ':plugin/'), array(Inflector::camelize($request['controller']), $request['action'], $plugin), - $this->actionPath . $path + $this->settings['actionPath'] . $path ); } @@ -111,16 +110,16 @@ abstract class BaseAuthorize { */ public function mapActions($map = array()) { if (empty($map)) { - return $this->_actionMap; + 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->_actionMap[$typedAction] = $action; + $this->settings['actionMap'][$typedAction] = $action; } } else { - $this->_actionMap[$action] = $type; + $this->settings['actionMap'][$action] = $type; } } } diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index d22b1ea92..c4dc81e0c 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -775,6 +775,20 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->identify($this->Controller->request); } +/** + * test that loadAuthorize merges in legacy authorize settings. + * + * @return void + */ + function testLoadAuthorizeSettingsPass() { + $this->Controller->Auth->actionPath = 'controllers/'; + + $this->Controller->Auth->authorize = array('Actions'); + $objects = $this->Controller->Auth->loadAuthorizeObjects(); + $result = $objects[0]; + $this->assertEquals($result->settings['actionPath'], 'controllers/'); + } + /** * test that loadAuthorize resets the loaded objects each time. * From e11917ae9401aa1b7c2f05e6caf112ca3aaaf63a Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 5 Jan 2011 00:03:07 -0500 Subject: [PATCH 19/65] Removing public properties that aren't used anymore. --- cake/libs/controller/components/auth.php | 16 ---------------- .../components/auth/base_authorize.php | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 40695b2aa..c18bea649 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -258,22 +258,6 @@ class AuthComponent extends Component { */ public $request; -/** - * Form data from Controller::$data - * - * @deprecated Use $this->request->data instead - * @var array - */ - public $data = array(); - -/** - * Parameter data from Controller::$params - * - * @deprecated Use $this->request instead - * @var array - */ - public $params = array(); - /** * Method list for bound controller * diff --git a/cake/libs/controller/components/auth/base_authorize.php b/cake/libs/controller/components/auth/base_authorize.php index cf9a2892f..e04f4e2de 100644 --- a/cake/libs/controller/components/auth/base_authorize.php +++ b/cake/libs/controller/components/auth/base_authorize.php @@ -32,7 +32,7 @@ abstract class BaseAuthorize { * Settings for authorize objects. * * - `actionPath` - The path to ACO nodes that contains the nodes for controllers. Used as a prefix - * when calling $this->action(); + * when calling $this->action(); * - `actionMap` - Action -> crud mappings. Used by authorization objects that want to map actions to CRUD roles. * * @var array From 1696df720135338910c0343a18da9d5dc8aa8638 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 5 Jan 2011 23:18:07 -0500 Subject: [PATCH 20/65] Removing the last of actionMap from AuthComponent. Its been moved into the authorization objects. Updating and adding tests for crud_authorize. --- cake/libs/controller/components/auth.php | 32 -------------- .../components/auth/crud_authorize.php | 43 ++++++++++++++++++- .../libs/controller/components/auth.test.php | 20 --------- .../components/auth/crud_authorize.test.php | 23 +++++++++- 4 files changed, 62 insertions(+), 56 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index c18bea649..6feac21ec 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -237,20 +237,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 * @@ -275,26 +261,8 @@ class AuthComponent extends Component { $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->_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(); diff --git a/cake/libs/controller/components/auth/crud_authorize.php b/cake/libs/controller/components/auth/crud_authorize.php index 5e6c9c85c..40844f4b1 100644 --- a/cake/libs/controller/components/auth/crud_authorize.php +++ b/cake/libs/controller/components/auth/crud_authorize.php @@ -31,6 +31,45 @@ App::import('Component', 'auth/base_authorize'); */ 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. * @@ -39,7 +78,7 @@ class CrudAuthorize extends BaseAuthorize { * @return boolean */ public function authorize($user, CakeRequest $request) { - if (!isset($this->_actionMap[$request->params['action']])) { + 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, @@ -53,7 +92,7 @@ class CrudAuthorize extends BaseAuthorize { return $Acl->check( $user, $this->action($request, ':controller'), - $this->_actionMap[$request->params['action']] + $this->settings['actionMap'][$request->params['action']] ); } } \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index c4dc81e0c..9db1ac1ca 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -1393,26 +1393,6 @@ class AuthTest extends CakeTestCase { $this->assertNull($this->Controller->Session->read('Auth.redirect')); } -/** - * test the initialize callback and its interactions with Router::prefixes() - * - * @return void - */ - function testInitializeAndRoutingPrefixes() { - $restore = Configure::read('Routing'); - Configure::write('Routing.prefixes', array('admin', 'super_user')); - Router::reload(); - $this->Controller->Auth->initialize($this->Controller); - - $this->assertTrue(isset($this->Controller->Auth->actionMap['delete'])); - $this->assertTrue(isset($this->Controller->Auth->actionMap['view'])); - $this->assertTrue(isset($this->Controller->Auth->actionMap['add'])); - $this->assertTrue(isset($this->Controller->Auth->actionMap['admin_view'])); - $this->assertTrue(isset($this->Controller->Auth->actionMap['super_user_delete'])); - - Configure::write('Routing', $restore); - } - /** * test $settings in Controller::$components * diff --git a/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php b/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php index fbad73a77..beee53fb9 100644 --- a/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php +++ b/cake/tests/cases/libs/controller/components/auth/crud_authorize.test.php @@ -117,11 +117,14 @@ class CrudAuthorizeTest extends CakeTestCase { function testMapActionsGet() { $result = $this->auth->mapActions(); $expected = array( + 'create' => 'create', + 'read' => 'read', + 'update' => 'update', + 'delete' => 'delete', 'index' => 'read', 'add' => 'create', 'edit' => 'update', 'view' => 'read', - 'delete' => 'delete', 'remove' => 'delete' ); $this->assertEquals($expected, $result); @@ -144,6 +147,9 @@ class CrudAuthorizeTest extends CakeTestCase { $result = $this->auth->mapActions(); $expected = array( + 'add' => 'create', + 'create' => 'create', + 'read' => 'read', 'index' => 'read', 'add' => 'create', 'edit' => 'update', @@ -154,9 +160,22 @@ class CrudAuthorizeTest extends CakeTestCase { 'listing' => 'read', 'show' => 'read', 'update' => 'update', - 'random' => 'custom' + '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'])); + } + } From ff889c2c8ec1a22ee40efa6f22810fdc04c7dc05 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 9 Jan 2011 00:33:06 -0500 Subject: [PATCH 21/65] Renaming method names, no need to include Objects, its implied. --- cake/libs/controller/components/auth.php | 10 +++++----- .../libs/controller/components/auth.test.php | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 6feac21ec..4aed7f015 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -433,7 +433,7 @@ class AuthComponent extends Component { $request = $this->request; } if (empty($this->_authorizeObjects)) { - $this->loadAuthorizeObjects(); + $this->constructAuthorize(); } foreach ($this->_authorizeObjects as $authorizer) { if ($authorizer->authorize($user, $request) === true) { @@ -448,7 +448,7 @@ class AuthComponent extends Component { * * @return mixed Either null when authorize is empty, or the loaded authorization objects. */ - public function loadAuthorizeObjects() { + public function constructAuthorize() { if (empty($this->authorize)) { return; } @@ -528,7 +528,7 @@ class AuthComponent extends Component { */ public function mapActions($map = array()) { if (empty($this->_authorizeObjects)) { - $this->loadAuthorizeObjects(); + $this->constructAuthorize(); } foreach ($this->_authorizeObjects as $auth) { $auth->mapActions($map); @@ -694,7 +694,7 @@ class AuthComponent extends Component { */ public function identify(CakeRequest $request) { if (empty($this->_authenticateObjects)) { - $this->loadAuthenticateObjects(); + $this->constructAuthenticate(); } foreach ($this->_authenticateObjects as $auth) { $result = $auth->authenticate($request); @@ -710,7 +710,7 @@ class AuthComponent extends Component { * * @return mixed either null on empty authenticate value, or an array of loaded objects. */ - public function loadAuthenticateObjects() { + public function constructAuthenticate() { if (empty($this->authenticate)) { return; } diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 9db1ac1ca..600b08e20 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -730,7 +730,7 @@ class AuthTest extends CakeTestCase { 'AuthMockTwo', 'AuthMockThree' ); - $mocks = $this->Controller->Auth->loadAuthorizeObjects(); + $mocks = $this->Controller->Auth->constructAuthorize(); $request = $this->Controller->request; $this->assertEquals(3, count($mocks)); @@ -759,10 +759,10 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->authorize = array( 'Controller' ); - $result = $this->Controller->Auth->loadAuthorizeObjects(); + $result = $this->Controller->Auth->constructAuthorize(); $this->assertEquals(1, count($result)); - $result = $this->Controller->Auth->loadAuthorizeObjects(); + $result = $this->Controller->Auth->constructAuthorize(); $this->assertEquals(1, count($result)); } @@ -784,7 +784,7 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->actionPath = 'controllers/'; $this->Controller->Auth->authorize = array('Actions'); - $objects = $this->Controller->Auth->loadAuthorizeObjects(); + $objects = $this->Controller->Auth->constructAuthorize(); $result = $objects[0]; $this->assertEquals($result->settings['actionPath'], 'controllers/'); } @@ -798,10 +798,10 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->authenticate = array( 'Form' ); - $result = $this->Controller->Auth->loadAuthenticateObjects(); + $result = $this->Controller->Auth->constructAuthenticate(); $this->assertEquals(1, count($result)); - $result = $this->Controller->Auth->loadAuthenticateObjects(); + $result = $this->Controller->Auth->constructAuthenticate(); $this->assertEquals(1, count($result)); } @@ -816,7 +816,7 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->fields = array('username' => 'user', 'password' => 'passwd'); $this->Controller->Auth->authenticate = array('Form'); - $objects = $this->Controller->Auth->loadAuthenticateObjects(); + $objects = $this->Controller->Auth->constructAuthenticate(); $result = $objects[0]; $this->assertEquals($result->settings['userModel'], 'AuthUser'); } @@ -1474,7 +1474,7 @@ class AuthTest extends CakeTestCase { function testMapActionsDelegation() { $this->getMock('BaseAuthorize', array('authorize'), array(), 'MapActionMockAuthorize', false); $this->Controller->Auth->authorize = array('MapActionMock'); - $mock = $this->Controller->Auth->loadAuthorizeObjects(); + $mock = $this->Controller->Auth->constructAuthorize(); $mock[0]->expects($this->once()) ->method('mapActions') ->with(array('create' => array('my_action'))); @@ -1494,7 +1494,7 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->request = $request; $this->Controller->Auth->authenticate = array('RequestLoginMock'); - $mock = $this->Controller->Auth->loadAuthenticateObjects(); + $mock = $this->Controller->Auth->constructAuthenticate(); $mock[0]->expects($this->once()) ->method('authenticate') ->with($request) From b59d0e8bb15e6f9cd6b6b48a320cef73e2857005 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 19 Jan 2011 16:17:17 -0500 Subject: [PATCH 22/65] Replacing Authcomponent::$flashElement with Authcomponent::$flash, which gives access to all the flash parameters. Also adding a wrapper method for more terse code and ability to extend functionality. --- cake/libs/controller/components/auth.php | 31 +++++++++++++++---- .../libs/controller/components/auth.test.php | 19 ++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 4aed7f015..afe98c888 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -111,11 +111,20 @@ 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 + * - `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 */ - public $flashElement = 'default'; + public $flash = array( + 'element' => 'default', + 'key' => 'auth', + 'params' => array() + ); /** * The name of the model that represents users which will be authenticated. Defaults to 'User'. @@ -342,13 +351,13 @@ class AuthComponent extends Component { } } - $this->Session->setFlash($this->loginError, $this->flashElement, array(), 'auth'); + $this->flash($this->loginError); $request->data[$model->alias][$this->fields['password']] = null; return false; } else { if (!$this->user()) { if (!$request->is('ajax')) { - $this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth'); + $this->flash($this->authError); if (!empty($request->query) && count($request->query) >= 2) { $query = $request->query; unset($query['url'], $query['ext']); @@ -376,7 +385,7 @@ class AuthComponent extends Component { return true; } - $this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth'); + $this->flash($this->authError); $controller->redirect($controller->referer(), null, true); return false; } @@ -792,4 +801,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']); + } } diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 600b08e20..baac8287d 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -1522,4 +1522,23 @@ class AuthTest extends CakeTestCase { $this->assertTrue($this->Controller->Auth->loggedIn()); $this->assertEquals($user['username'], $this->Controller->Auth->user('username')); } + +/** + * test flash settings. + * + * @return void + */ + function testFlashSettings() { + $this->Controller->Auth->Session = $this->getMock('SessionComponent', array(), array(), '', zfalse); + $this->Controller->Auth->Session->expects($this->once()) + ->method('setFlash') + ->with('Auth failure', 'custom', array(1), 'auth-key'); + + $this->Controller->Auth->flash = array( + 'element' => 'custom', + 'params' => array(1), + 'key' => 'auth-key' + ); + $this->Controller->Auth->flash('Auth failure'); + } } From d8f2cf9395ddfd46009d2942dc635e706915874f Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 19 Jan 2011 16:53:58 -0500 Subject: [PATCH 23/65] Starting to remove magic around userModel, and deprecating/removing userModel from AuthComponent. --- cake/libs/controller/components/auth.php | 28 +++++----------- .../libs/controller/components/auth.test.php | 33 +++++++------------ 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index afe98c888..fafce8af8 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -154,12 +154,12 @@ class AuthComponent extends Component { /** * 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; + public $sessionKey = 'Auth.User'; /** * If using action-based access control, this defines how the paths to action @@ -174,12 +174,16 @@ class AuthComponent extends Component { /** * 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 @@ -268,8 +272,6 @@ class AuthComponent extends Component { */ public function initialize($controller) { $this->request = $controller->request; - $this->params = $this->request; - $this->_methods = $controller->methods; if (Configure::read('debug') > 0) { @@ -399,18 +401,7 @@ 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.') @@ -597,8 +588,7 @@ class AuthComponent extends Component { } if ($key == null) { - $model = $this->getModel(); - return array($model->alias => $this->Session->read($this->sessionKey)); + return $this->Session->read($this->sessionKey); } else { $user = $this->Session->read($this->sessionKey); if (isset($user[$key])) { diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index baac8287d..df63a5019 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -582,12 +582,12 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->startup($this->Controller); $user = $this->Controller->Auth->user(); - $expected = array('AuthUser' => array( + $expected = array( 'id' => 1, 'username' => 'mariano', 'created' => '2007-03-17 01:16:23', 'updated' => date('Y-m-d H:i:s') - )); + ); $this->assertEqual($user, $expected); $this->Controller->Session->delete('Auth'); @@ -660,7 +660,7 @@ class AuthTest extends CakeTestCase { function testAuthorizeFalse() { $this->AuthUser = new AuthUser(); $user = $this->AuthUser->find(); - $this->Controller->Session->write('Auth', $user); + $this->Controller->Session->write('Auth.User', $user['AuthUser']); $this->Controller->Auth->userModel = 'AuthUser'; $this->Controller->Auth->authorize = false; $this->Controller->request->addParams(Router::parse('auth_test/add')); @@ -1008,7 +1008,7 @@ class AuthTest extends CakeTestCase { ); $this->Controller->Auth->userModel = 'AuthUser'; $this->Controller->Auth->startup($this->Controller); - $expected = Router::normalize('/'); + $expected = Router::normalize('/AuthTest/login'); $this->assertEqual($expected, $this->Controller->testUrl); $this->Controller->Session->delete('Auth'); @@ -1292,25 +1292,14 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->startup($this->Controller); $user = $this->Controller->Auth->user(); $expected = array( - 'TestPluginAuthUser' => array( - 'id' => 1, - 'username' => 'gwoo', - 'created' => '2007-03-17 01:16:23', - 'updated' => date('Y-m-d H:i:s') - )); + 'id' => 1, + 'username' => 'gwoo', + 'created' => '2007-03-17 01:16:23', + 'updated' => date('Y-m-d H:i:s') + ); $this->assertEqual($user, $expected); $sessionKey = $this->Controller->Auth->sessionKey; - $this->assertEqual('Auth.TestPluginAuthUser', $sessionKey); - - $this->Controller->Auth->loginAction = null; - $this->Controller->Auth->__setDefaults(); - $loginAction = $this->Controller->Auth->loginAction; - $expected = array( - 'controller' => 'test_plugin_auth_users', - 'action' => 'login', - 'plugin' => 'test_plugin' - ); - $this->assertEqual($loginAction, $expected); + $this->assertEqual('Auth.User', $sessionKey); // Reverting changes Cache::delete('object_map', '_cake_core_'); @@ -1529,7 +1518,7 @@ class AuthTest extends CakeTestCase { * @return void */ function testFlashSettings() { - $this->Controller->Auth->Session = $this->getMock('SessionComponent', array(), array(), '', zfalse); + $this->Controller->Auth->Session = $this->getMock('SessionComponent', array(), array(), '', false); $this->Controller->Auth->Session->expects($this->once()) ->method('setFlash') ->with('Auth failure', 'custom', array(1), 'auth-key'); From dc8c99308efc6754feda5beea7fdebd6d2647c78 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 20 Jan 2011 17:50:18 -0500 Subject: [PATCH 24/65] Starting to remove magical login process. Updating tests to actually test the methods they are named after. --- cake/libs/controller/components/auth.php | 20 +---- .../libs/controller/components/auth.test.php | 84 ++++++------------- 2 files changed, 27 insertions(+), 77 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index fafce8af8..1361a8797 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -333,29 +333,11 @@ 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) { - if ($this->login()) { - if ($this->autoRedirect) { - $controller->redirect($this->redirect(), null, true); - } - return true; - } - } - - $this->flash($this->loginError); - $request->data[$model->alias][$this->fields['password']] = null; - return false; } else { if (!$this->user()) { if (!$request->is('ajax')) { diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index df63a5019..923aa7e70 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -505,6 +505,8 @@ class AuthTest extends CakeTestCase { $this->initialized = true; Router::reload(); + + ClassRegistry::init('AuthUser')->updateAll(array('password' => '"' . Security::hash('cake', null, true) . '"')); } /** @@ -560,71 +562,37 @@ class AuthTest extends CakeTestCase { * @return void */ function testLogin() { - $this->AuthUser = new AuthUser(); - $user['id'] = 1; - $user['username'] = 'mariano'; - $user['password'] = Security::hash(Configure::read('Security.salt') . 'cake'); - $this->AuthUser->save($user, false); + $this->getMock('FormAuthenticate', array(), array(), 'AuthLoginFormAuthenticate', false); + $this->Controller->Auth->authenticate = array( + 'AuthLoginForm' => array( + 'userModel' => 'AuthUser' + ) + ); + $mocks = $this->Controller->Auth->constructAuthenticate(); + $this->mockObjects[] = $mocks[0]; - $authUser = $this->AuthUser->find(); - - $this->Controller->request->data['AuthUser'] = array( - 'username' => $authUser['AuthUser']['username'], 'password' => 'cake' + $this->Controller->Auth->request->data = array( + 'AuthUser' => array( + 'username' => 'mark', + 'password' => Security::hash('cake', null, true) + ) ); - $this->Controller->request->addParams(Router::parse('auth_test/login')); - $this->Controller->request->query['url'] = 'auth_test/login'; - - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->loginAction = 'auth_test/login'; - $this->Controller->Auth->userModel = 'AuthUser'; - - $this->Controller->Auth->startup($this->Controller); - $user = $this->Controller->Auth->user(); - $expected = array( - 'id' => 1, - 'username' => 'mariano', - 'created' => '2007-03-17 01:16:23', - 'updated' => date('Y-m-d H:i:s') - ); - $this->assertEqual($user, $expected); - $this->Controller->Session->delete('Auth'); - - $this->Controller->request->data['AuthUser'] = array( - 'username' => 'blah', - 'password' => '' + $user = array( + 'id' => 1, + 'username' => 'mark' ); - $this->Controller->Auth->startup($this->Controller); + $mocks[0]->expects($this->once()) + ->method('authenticate') + ->with($this->Controller->Auth->request) + ->will($this->returnValue($user)); - $user = $this->Controller->Auth->user(); - $this->assertNull($user); - $this->Controller->Session->delete('Auth'); + $result = $this->Controller->Auth->login(); + $this->assertTrue($result); - $this->Controller->request->data['AuthUser'] = array( - 'username' => 'now() or 1=1 --', - 'password' => '' - ); - - $this->Controller->Auth->startup($this->Controller); - - $user = $this->Controller->Auth->user(); - $this->assertNull($user); - $this->Controller->Session->delete('Auth'); - - $this->Controller->request->data['AuthUser'] = array( - 'username' => 'now() or 1=1 #something', - 'password' => '' - ); - - $this->Controller->Auth->startup($this->Controller); - - $user = $this->Controller->Auth->user(); - $this->assertNull($user); - $this->Controller->Session->delete('Auth'); - - $this->Controller->Session->delete('Auth'); + $this->assertTrue($this->Controller->Auth->loggedIn()); + $this->assertEquals($user, $this->Controller->Auth->user()); } /** From 6eb31dfc94f83cda055f6dba3a679611cc7b7d86 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 20 Jan 2011 20:01:18 -0500 Subject: [PATCH 25/65] Removing test cases that were doing integration testing with router, and plugin models. Plugin model tests are in FormAuthenticate. And there is no more automatic login. --- .../libs/controller/components/auth.test.php | 109 +----------------- 1 file changed, 1 insertion(+), 108 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 923aa7e70..12ba3e878 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -1137,60 +1137,6 @@ class AuthTest extends CakeTestCase { $this->assertEqual($return, $expected); } -/** - * testCustomRoute method - * - * @access public - * @return void - */ - function testCustomRoute() { - Router::reload(); - Router::connect( - '/:lang/:controller/:action/*', - array('lang' => null), - array('lang' => '[a-z]{2,3}') - ); - - $url = '/en/users/login'; - $this->Controller->request->addParams(Router::parse($url)); - Router::setRequestInfo($this->Controller->request); - - $this->AuthUser = new AuthUser(); - $user = array( - 'id' => 1, 'username' => 'felix', - 'password' => Security::hash(Configure::read('Security.salt') . 'cake' - )); - $user = $this->AuthUser->save($user, false); - - $this->Controller->request->data['AuthUser'] = array('username' => 'felix', 'password' => 'cake'); - $this->Controller->request->query['url'] = substr($url, 1); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array('lang' => 'en', 'controller' => 'users', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - - $this->Controller->Auth->startup($this->Controller); - $user = $this->Controller->Auth->user(); - $this->assertTrue(!!$user); - - $this->Controller->Session->delete('Auth'); - Router::reload(); - Router::connect('/', array('controller' => 'people', 'action' => 'login')); - $url = '/'; - $this->Controller->request->addParams(Router::parse($url)); - Router::setRequestInfo(array($this->Controller->passedArgs, array( - 'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(), - 'argSeparator' => ':', 'namedArgs' => array() - ))); - $this->Controller->request->data['AuthUser'] = array('username' => 'felix', 'password' => 'cake'); - $this->Controller->request->query['url'] = substr($url, 1); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array('controller' => 'people', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - - $this->Controller->Auth->startup($this->Controller); - $user = $this->Controller->Auth->user(); - $this->assertTrue(!!$user); - } /** * testAdminRoute method @@ -1221,60 +1167,6 @@ class AuthTest extends CakeTestCase { Configure::write('Routing.prefixes', $prefixes); } -/** - * testPluginModel method - * - * @access public - * @return void - */ - function testPluginModel() { - // Adding plugins - Cache::delete('object_map', '_cake_core_'); - App::build(array( - 'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS), - 'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . 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); - - $authUser = $PluginModel->find(); - - $this->Controller->request->data['TestPluginAuthUser'] = array( - 'username' => $authUser['TestPluginAuthUser']['username'], - 'password' => 'cake' - ); - - $this->Controller->request->addParams(Router::parse('auth_test/login')); - $this->Controller->request->query['url'] = 'auth_test/login'; - - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->loginAction = 'auth_test/login'; - $this->Controller->Auth->userModel = 'TestPlugin.TestPluginAuthUser'; - - $this->Controller->Auth->startup($this->Controller); - $user = $this->Controller->Auth->user(); - $expected = array( - 'id' => 1, - 'username' => 'gwoo', - 'created' => '2007-03-17 01:16:23', - 'updated' => date('Y-m-d H:i:s') - ); - $this->assertEqual($user, $expected); - $sessionKey = $this->Controller->Auth->sessionKey; - $this->assertEqual('Auth.User', $sessionKey); - - // Reverting changes - Cache::delete('object_map', '_cake_core_'); - App::build(); - App::objects('plugin', null, false); - } - /** * testAjaxLogin method * @@ -1389,6 +1281,7 @@ class AuthTest extends CakeTestCase { ); $this->Controller->request->query['url'] = substr($url, 1); $this->Controller->Auth->startup($this->Controller); + $this->Controller->Auth->login(); $user = $this->Controller->Auth->user(); $this->assertTrue(!!$user); From f2bba3d36e58347e7c444272c51007615550e8d4 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 20 Jan 2011 20:10:46 -0500 Subject: [PATCH 26/65] Adding tests for AuthComponent::redirect() previously there weren't any. --- .../libs/controller/components/auth.test.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 12ba3e878..685c48740 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -1391,4 +1391,46 @@ class AuthTest extends CakeTestCase { ); $this->Controller->Auth->flash('Auth failure'); } + +/** + * test the various states of Auth::redirect() + * + * @return void + */ + function testRedirectSet() { + $value = array('controller' => 'users', 'action' => 'home'); + $result = $this->Controller->Auth->redirect($value); + $this->assertEquals('/users/home', $result); + $this->assertEquals($value, $this->Controller->Session->read('Auth.redirect')); + } + +/** + * test redirect using Auth.redirect from the session. + * + * @return void + */ + function testRedirectSessionRead() { + $this->Controller->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); + $this->Controller->Session->write('Auth.redirect', '/users/home'); + + $result = $this->Controller->Auth->redirect(); + $this->assertEquals('/users/home', $result); + $this->assertFalse($this->Controller->Session->check('Auth.redirect')); + } + +/** + * test that redirect does not return loginAction if that is what's stored in Auth.redirect. + * instead loginRedirect should be used. + * + * @return void + */ + function testRedirectSessionReadEqualToLoginAction() { + $this->Controller->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); + $this->Controller->Auth->loginRedirect = array('controller' => 'users', 'action' => 'home'); + $this->Controller->Session->write('Auth.redirect', array('controller' => 'users', 'action' => 'login')); + + $result = $this->Controller->Auth->redirect(); + $this->assertEquals('/users/home', $result); + $this->assertFalse($this->Controller->Session->check('Auth.redirect')); + } } From ed122c70f46a6e591727e6c8743a009f4ac7bf5e Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 20 Jan 2011 21:22:54 -0500 Subject: [PATCH 27/65] Removing custom request reversal code, and using Router::reverse() Removing dead tests that are covered by Router tests. Removing check for TestsController, it hasn't existed in forever. --- cake/libs/controller/components/auth.php | 21 ++++--------------- .../libs/controller/components/auth.test.php | 15 +------------ 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 1361a8797..d398392de 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -288,11 +288,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; } @@ -342,12 +338,7 @@ class AuthComponent extends Component { if (!$this->user()) { if (!$request->is('ajax')) { $this->flash($this->authError); - 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->Session->write('Auth.redirect', Router::reverse($request)); $controller->redirect($loginAction); return false; } elseif (!empty($this->ajaxLogin)) { @@ -361,11 +352,7 @@ class AuthComponent extends Component { } } - if (!$this->authorize) { - return true; - } - - if ($this->isAuthorized()) { + if (empty($this->authorize) || $this->isAuthorized()) { return true; } @@ -519,7 +506,7 @@ class AuthComponent extends Component { /** * 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, POST data from the current request will be used to identify a user. If the login was successful, + * 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. * * @param mixed $user Either an array of user data, or null to identify a user using the current request. diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 685c48740..7783c5d29 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -943,17 +943,6 @@ class AuthTest extends CakeTestCase { $this->Controller->Session->delete('Auth'); - $this->Controller->request->query['url'] = 'admin/'; - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->loginRedirect = null; - $this->Controller->Auth->startup($this->Controller); - $expected = Router::normalize('admin/'); - $this->assertTrue($this->Controller->Session->check('Message.auth')); - $this->assertEqual($expected, $this->Controller->Auth->redirect()); - - $this->Controller->Session->delete('Auth'); - //empty referer no session $_SERVER['HTTP_REFERER'] = false; $_ENV['HTTP_REFERER'] = false; @@ -969,7 +958,6 @@ class AuthTest extends CakeTestCase { $this->Controller->Auth->initialize($this->Controller); $this->Controller->Auth->authorize = 'controller'; - $this->Controller->request['testControllerAuth'] = true; $this->Controller->Auth->loginAction = array( 'controller' => 'AuthTest', 'action' => 'login' @@ -1041,8 +1029,7 @@ class AuthTest extends CakeTestCase { $_GET = array( 'url' => '/posts/index/29', 'print' => 'true', - 'refer' => 'menu', - 'ext' => 'html' + 'refer' => 'menu' ); $this->Controller->Session->delete('Auth'); $url = '/posts/index/29'; From e155e6acbaa35a16339ce93d6a4ca52c9d769743 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 20 Jan 2011 21:34:39 -0500 Subject: [PATCH 28/65] Adding doc block usage information. --- cake/libs/controller/components/auth.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index d398392de..ad2ab464b 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -444,6 +444,15 @@ class AuthComponent extends Component { * 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'));` + * `$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. @@ -463,7 +472,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'));` + * `$this->Auth->deny('edit', 'add');` * * @param mixed $action Controller action name or array of actions * @param string $action Controller action name From 23db2f086efcacaeb982eac134ae7163807ea59a Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 20 Jan 2011 21:39:59 -0500 Subject: [PATCH 29/65] Adding more documentation. --- cake/libs/controller/components/auth.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index ad2ab464b..5a477cae4 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -582,7 +582,9 @@ class AuthComponent extends Component { } /** - * If no parameter is passed, gets the authentication redirect URL. + * 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 From fd8fb1225d3da750c465f972758c98a9741607b5 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 16:22:29 -0500 Subject: [PATCH 30/65] Removing automatic password hashing from AuthComponent. Its a frustrating feature that often befuddles new users, and can be plain annoying sometimes. Moving hashing into FormAuthenticate. Updating tests. --- cake/libs/controller/components/auth.php | 27 +------------- .../components/auth/form_authenticate.php | 12 ++++++- .../libs/controller/components/auth.test.php | 36 ------------------- .../auth/form_authenticate.test.php | 10 +++--- 4 files changed, 17 insertions(+), 68 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 5a477cae4..fb878472f 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -308,8 +308,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'])) { @@ -717,30 +716,6 @@ class AuthComponent extends Component { return $this->_authenticateObjects; } -/** - * 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; - } - /** * Hash a password with the application's salt value (as defined with Configure::write('Security.salt'); * diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php index d536499b5..f0d9f862c 100644 --- a/cake/libs/controller/components/auth/form_authenticate.php +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -86,7 +86,7 @@ class FormAuthenticate { } $conditions = array( $model . '.' . $fields['username'] => $request->data[$model][$fields['username']], - $model . '.' . $fields['password'] => $request->data[$model][$fields['password']], + $model . '.' . $fields['password'] => $this->hash($request->data[$model][$fields['password']]), ); if (!empty($this->settings['scope'])) { $conditions = array_merge($conditions, $this->settings['scope']); @@ -101,4 +101,14 @@ class FormAuthenticate { unset($result[$model][$fields['password']]); return $result[$model]; } + +/** + * Hash the supplied password using the configured hashing method. + * + * @param string $password The password to hash. + * @return string Hashed string + */ + public function hash($password) { + return Security::hash($password, null, true); + } } \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 7783c5d29..4180256f9 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -1089,42 +1089,6 @@ class AuthTest extends CakeTestCase { $this->assertTrue($result, 'Auth redirected a missing action %s'); } -/** - * test Hashing of passwords - * - * @return void - */ - function testHashPasswords() { - $this->Controller->Auth->userModel = 'AuthUser'; - - $data['AuthUser']['password'] = 'superSecret'; - $data['AuthUser']['username'] = 'superman@dailyplanet.com'; - $return = $this->Controller->Auth->hashPasswords($data); - $expected = $data; - $expected['AuthUser']['password'] = Security::hash($expected['AuthUser']['password'], null, true); - $this->assertEqual($return, $expected); - - $data['Wrong']['password'] = 'superSecret'; - $data['Wrong']['username'] = 'superman@dailyplanet.com'; - $data['AuthUser']['password'] = 'IcantTellYou'; - $return = $this->Controller->Auth->hashPasswords($data); - $expected = $data; - $expected['AuthUser']['password'] = Security::hash($expected['AuthUser']['password'], null, true); - $this->assertEqual($return, $expected); - - $xml = array( - 'User' => array( - 'username' => 'batman@batcave.com', - 'password' => 'bruceWayne', - ) - ); - $data = new Xml($xml); - $return = $this->Controller->Auth->hashPasswords($data); - $expected = $data; - $this->assertEqual($return, $expected); - } - - /** * testAdminRoute method * diff --git a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php index 0c5e36a18..de4600e6d 100644 --- a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php @@ -41,8 +41,8 @@ class FormAuthenticateTest extends CakeTestCase { 'fields' => array('username' => 'user', 'password' => 'password'), 'userModel' => 'User' )); - $this->password = Security::hash('password', null, true); - ClassRegistry::init('User')->updateAll(array('password' => '"' . $this->password . '"')); + $password = Security::hash('password', null, true); + ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"')); } /** @@ -116,7 +116,7 @@ class FormAuthenticateTest extends CakeTestCase { $request = new CakeRequest('posts/index', false); $request->data = array('User' => array( 'user' => 'mariano', - 'password' => $this->password + 'password' => 'password' )); $result = $this->auth->authenticate($request); $expected = array( @@ -138,7 +138,7 @@ class FormAuthenticateTest extends CakeTestCase { $request = new CakeRequest('posts/index', false); $request->data = array('User' => array( 'user' => 'mariano', - 'password' => $this->password + 'password' => 'password' )); $this->assertFalse($this->auth->authenticate($request)); @@ -168,7 +168,7 @@ class FormAuthenticateTest extends CakeTestCase { $request = new CakeRequest('posts/index', false); $request->data = array('TestPluginAuthUser' => array( 'username' => 'gwoo', - 'password' => Security::hash('cake', null, true) + 'password' => 'cake' )); $result = $this->auth->authenticate($request); From dc03e4f26cef743ef216189134b9e8b2531cf317 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 16:24:43 -0500 Subject: [PATCH 31/65] Moving an import. --- cake/libs/controller/components/auth/form_authenticate.php | 1 + .../libs/controller/components/auth/form_authenticate.test.php | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php index f0d9f862c..ce4a6e7c9 100644 --- a/cake/libs/controller/components/auth/form_authenticate.php +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -12,6 +12,7 @@ * @link http://cakephp.org CakePHP(tm) Project * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +App::import('Core', 'Security'); /** * An authentication adapter for AuthComponent. Provides the ability to authenticate using POST diff --git a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php index de4600e6d..b2218a75b 100644 --- a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php @@ -17,7 +17,6 @@ App::import('Component', 'auth/form_authenticate'); App::import('Model', 'AppModel'); App::import('Core', 'CakeRequest'); -App::import('Core', 'Security'); require_once CAKE_TESTS . 'cases' . DS . 'libs' . DS . 'model' . DS . 'models.php'; From 28ad51c92b205ebef2447acd5b9ab3d8094a7883 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 16:25:04 -0500 Subject: [PATCH 32/65] Removing action() from AuthComponent, its in the authorization objects now. Updating tests. --- cake/libs/controller/components/auth.php | 18 ------------ .../libs/controller/components/auth.test.php | 29 ------------------- 2 files changed, 47 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index fb878472f..ea7b6e69c 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -627,24 +627,6 @@ class AuthComponent extends Component { return $this->Acl->check($user, $object, $action); } -/** - * Returns the path to the ACO node bound to a controller/action. - * - * @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 - */ - 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. diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 4180256f9..94f3b8d7b 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -817,35 +817,6 @@ class AuthTest extends CakeTestCase { $this->assertFalse($this->Controller->Auth->startup($this->Controller)); } -/** - * test the action() method - * - * @return void - */ - function testActionMethod() { - $this->Controller->request['controller'] = 'auth_test'; - $this->Controller->request['action'] = 'add'; - - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->actionPath = 'ROOT/'; - - $result = $this->Controller->Auth->action(); - $this->assertEqual($result, 'ROOT/AuthTest/add'); - - $result = $this->Controller->Auth->action(':controller'); - $this->assertEqual($result, 'ROOT/AuthTest'); - - $result = $this->Controller->Auth->action(':controller'); - $this->assertEqual($result, 'ROOT/AuthTest'); - - $this->Controller->request['plugin'] = 'test_plugin'; - $this->Controller->request['controller'] = 'auth_test'; - $this->Controller->request['action'] = 'add'; - $this->Controller->Auth->initialize($this->Controller); - $result = $this->Controller->Auth->action(); - $this->assertEqual($result, 'ROOT/TestPlugin/AuthTest/add'); - } - /** * test that deny() converts camel case inputs to lowercase. * From 66f5ae07ed4a9a376b6340a604b6573670b0c100 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 16:27:52 -0500 Subject: [PATCH 33/65] Removing AuthComponent::validate() there are no tests and no documentation on this method. It also uses components that may or may not exist even in the old implementation. --- cake/libs/controller/components/auth.php | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index ea7b6e69c..c6b1e412a 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -605,28 +605,6 @@ class AuthComponent extends Component { return Router::normalize($redir); } -/** - * Validates a user against an abstract object. - * - * @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. - */ - public function validate($object, $user = null, $action = null) { - if (empty($user)) { - $user = $this->user(); - } - if (empty($user)) { - return false; - } - return $this->Acl->check($user, $object, $action); - } - /** * Returns a reference to the model object specified, and attempts * to load it if it is not found. From 35864c2a02c5bb54731a7eba41878e4524b85830 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 16:28:59 -0500 Subject: [PATCH 34/65] Removing AuthComponent::getModel(). Auth no longer directly interacts with models. --- cake/libs/controller/components/auth.php | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index c6b1e412a..6e02e13d3 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -605,29 +605,6 @@ class AuthComponent extends Component { return Router::normalize($redir); } -/** - * 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; - } - - $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; - } - - return $model; - } - /** * Use the configured authentication adapters, and attempt to identify the user * by credentials contained in $request. From b207ee8cbe2716f241137ba000e2c91132b740f2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 16:55:02 -0500 Subject: [PATCH 35/65] Making AuthComponent::user static, so user data can be fetched from anywhere. --- cake/libs/controller/components/auth.php | 20 +++++++++---------- .../libs/controller/components/auth.test.php | 7 ++----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 6e02e13d3..7dd94840e 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -21,6 +21,7 @@ App::import('Core', 'Router', false); App::import('Core', 'Security', false); +App::import('Core', 'CakeSession', false); App::import('Component', 'auth/base_authorize'); /** @@ -159,7 +160,7 @@ class AuthComponent extends Component { * @var string * @link http://book.cakephp.org/view/1276/sessionKey */ - public $sessionKey = 'Auth.User'; + public static $sessionKey = 'Auth.User'; /** * If using action-based access control, this defines how the paths to action @@ -445,7 +446,7 @@ class AuthComponent extends Component { * * You can use allow with either an array, or var args. * - * `$this->Auth->allow(array('edit', 'add'));` + * `$this->Auth->allow(array('edit', 'add'));` or * `$this->Auth->allow('edit', 'add');` * * allow() also supports '*' as a wildcard to mean all actions. @@ -475,7 +476,7 @@ class AuthComponent extends Component { * * You can use deny with either an array, or var args. * - * `$this->Auth->deny(array('edit', 'add'));` + * `$this->Auth->deny(array('edit', 'add'));` or * `$this->Auth->deny('edit', 'add');` * * @param mixed $action Controller action name or array of actions @@ -534,7 +535,7 @@ class AuthComponent extends Component { $user = $this->identify($this->request); } if ($user) { - $this->Session->write($this->sessionKey, $user); + $this->Session->write(self::$sessionKey, $user); $this->_loggedIn = true; } return $this->_loggedIn; @@ -550,7 +551,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); @@ -563,16 +564,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) { - return $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]; } diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 94f3b8d7b..1f3a2a49b 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -1178,8 +1178,7 @@ class AuthTest extends CakeTestCase { 'Auth' => array( 'fields' => array('username' => 'email', 'password' => 'password'), 'loginAction' => array('controller' => 'people', 'action' => 'login'), - 'userModel' => 'AuthUserCustomField', - 'sessionKey' => 'AltAuth.AuthUserCustomField' + 'userModel' => 'AuthUserCustomField' ), 'Session' ); @@ -1212,14 +1211,12 @@ class AuthTest extends CakeTestCase { 'fields' => array('username' => 'email', 'password' => 'password'), 'loginAction' => array('controller' => 'people', 'action' => 'login'), 'logoutRedirect' => array('controller' => 'people', 'action' => 'login'), - 'userModel' => 'AuthUserCustomField', - 'sessionKey' => 'AltAuth.AuthUserCustomField' + 'userModel' => 'AuthUserCustomField' ); $this->assertEqual($expected['fields'], $this->Controller->Auth->fields); $this->assertEqual($expected['loginAction'], $this->Controller->Auth->loginAction); $this->assertEqual($expected['logoutRedirect'], $this->Controller->Auth->logoutRedirect); $this->assertEqual($expected['userModel'], $this->Controller->Auth->userModel); - $this->assertEqual($expected['sessionKey'], $this->Controller->Auth->sessionKey); } /** From 41819975e869beeb9b42ffe10c68612f7baf5d62 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 17:04:10 -0500 Subject: [PATCH 36/65] Removing a dead property. --- cake/libs/controller/components/auth.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 7dd94840e..7b41e499f 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -209,12 +209,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 From ee804c6f8cef2c4f0b99f678847cc7a126086222 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 17:55:04 -0500 Subject: [PATCH 37/65] Removing properties from AuthComponent that have been moved to authentication and authorization objects. Adding the '*' key to allow code to stay DRY, also adding a constant for ALL, so there aren't as many magic strings around. Updating tests. --- cake/libs/controller/components/auth.php | 145 ++++++++---------- .../libs/controller/components/auth.test.php | 24 +-- 2 files changed, 76 insertions(+), 93 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 7b41e499f..5f4631ab8 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -34,6 +34,8 @@ App::import('Component', 'auth/base_authorize'); */ class AuthComponent extends Component { + const ALL = '*'; + /** * Maintains current user login state. * @@ -52,7 +54,30 @@ class AuthComponent extends Component { * 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 '*' key: + * + * {{{ + * $this->Auth->authenticate = array( + * '*' => array( + * 'userModel' => 'Users.User', + * 'scope' => array('User.active' => 1) + * ), + * 'Form', + * 'Basic' + * ); + * }}} + * + * @var array * @link http://book.cakephp.org/view/1278/authenticate */ public $authenticate = array('Form'); @@ -64,22 +89,32 @@ class AuthComponent extends Component { */ protected $_authenticateObjects = array(); -/** - * 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. * + * {{{ + * $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 '*' key: + * + * {{{ + * $this->Auth->authorize = array( + * '*' => array( + * 'actionPath' => 'controllers/' + * ), + * 'Crud', + * 'CustomAuth' + * ); + * }}} + * * @var mixed * @link http://book.cakephp.org/view/1275/authorize */ @@ -92,16 +127,6 @@ class AuthComponent extends Component { */ protected $_authorizeObjects = array(); -/** - * A hash mapping legacy properties => to settings passed into Authorize objects. - * - * @var string - * @deprecated Will be removed in 2.1+ - */ - protected $_authorizeLegacyMap = array( - 'actionPath' => 'actionPath', - ); - /** * The name of an optional view element to render when an Ajax request is made * with an invalid or expired session @@ -127,32 +152,6 @@ class AuthComponent extends Component { 'params' => array() ); -/** - * 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). - * - * @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'); - /** * The session key name where the record of the current user is stored. If * unspecified, it will be "Auth.User". @@ -162,17 +161,6 @@ class AuthComponent extends Component { */ public static $sessionKey = 'Auth.User'; -/** - * 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; - /** * A URL (defined as a string or array) to the controller action that handles * logins. Defaults to `/users/login` @@ -209,7 +197,6 @@ class AuthComponent extends Component { */ public $logoutRedirect = 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. @@ -228,14 +215,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. * @@ -416,7 +395,13 @@ class AuthComponent extends Component { return; } $this->_authorizeObjects = array(); - foreach (Set::normalize($this->authorize) as $class => $settings) { + $config = Set::normalize($this->authorize); + $global = array(); + if (isset($config[AuthComponent::ALL])) { + $global = $config[AuthComponent::ALL]; + unset($config[AuthComponent::ALL]); + } + 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)); @@ -424,11 +409,7 @@ class AuthComponent extends Component { if (!method_exists($className, 'authorize')) { throw new CakeException(__('Authorization objects must implement an authorize method.')); } - foreach ($this->_authorizeLegacyMap as $old => $new) { - if (empty($settings[$new]) && !empty($this->{$old})) { - $settings[$new] = $this->{$old}; - } - } + $settings = array_merge($global, (array)$settings); $this->_authorizeObjects[] = new $className($this->_Collection->getController(), $settings); } return $this->_authorizeObjects; @@ -629,7 +610,13 @@ class AuthComponent extends Component { return; } $this->_authenticateObjects = array(); - foreach (Set::normalize($this->authenticate) as $class => $settings) { + $config = Set::normalize($this->authenticate); + $global = array(); + if (isset($config[AuthComponent::ALL])) { + $global = $config[AuthComponent::ALL]; + unset($config[AuthComponent::ALL]); + } + 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)); @@ -637,11 +624,7 @@ class AuthComponent extends Component { if (!method_exists($className, 'authenticate')) { throw new CakeException(__('Authentication objects must implement an authenticate method.')); } - foreach ($this->_authenticateLegacyMap as $old => $new) { - if (empty($settings[$new]) && !empty($this->{$old})) { - $settings[$new] = $this->{$old}; - } - } + $settings = array_merge((array)$settings, $global); $this->_authenticateObjects[] = new $className($settings); } return $this->_authenticateObjects; diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 1f3a2a49b..bb5d5e29b 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -744,14 +744,15 @@ class AuthTest extends CakeTestCase { } /** - * test that loadAuthorize merges in legacy authorize settings. + * test the * key with authenticate * * @return void */ - function testLoadAuthorizeSettingsPass() { - $this->Controller->Auth->actionPath = 'controllers/'; - - $this->Controller->Auth->authorize = array('Actions'); + function testAllConfigWithAuthorize() { + $this->Controller->Auth->authorize = array( + AuthComponent::ALL => array('actionPath' => 'controllers/'), + 'Actions' + ); $objects = $this->Controller->Auth->constructAuthorize(); $result = $objects[0]; $this->assertEquals($result->settings['actionPath'], 'controllers/'); @@ -774,16 +775,15 @@ class AuthTest extends CakeTestCase { } /** - * test that loadAuthenticate merges in legacy authentication settings. + * test the * key with authenticate * * @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'); + function testAllConfigWithAuthenticate() { + $this->Controller->Auth->authenticate = array( + AuthComponent::ALL => array('userModel' => 'AuthUser'), + 'Form' + ); $objects = $this->Controller->Auth->constructAuthenticate(); $result = $objects[0]; $this->assertEquals($result->settings['userModel'], 'AuthUser'); From 041e0a65aca39e77f960e127228953b3947d003a Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 18:09:41 -0500 Subject: [PATCH 38/65] Extracting a base class. --- .../components/auth/base_authenticate.php | 69 +++++++++++++++++++ .../components/auth/form_authenticate.php | 41 +---------- 2 files changed, 71 insertions(+), 39 deletions(-) create mode 100644 cake/libs/controller/components/auth/base_authenticate.php diff --git a/cake/libs/controller/components/auth/base_authenticate.php b/cake/libs/controller/components/auth/base_authenticate.php new file mode 100644 index 000000000..2bc8ad725 --- /dev/null +++ b/cake/libs/controller/components/auth/base_authenticate.php @@ -0,0 +1,69 @@ + 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); + } + +/** + * Hash the supplied password using the configured hashing method. + * + * @param string $password The password to hash. + * @return string Hashed string + */ + public function hash($password) { + return Security::hash($password, null, true); + } + +/** + * Authenticate a user based on the request information. + * + * @param CakeRequest $request Request to get authentication information from. + * @return mixed Either false on failure, or an array of user data on success. + */ + abstract public function authenticate(CakeRequest $request); +} \ No newline at end of file diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php index ce4a6e7c9..3f284eaf2 100644 --- a/cake/libs/controller/components/auth/form_authenticate.php +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -12,7 +12,7 @@ * @link http://cakephp.org CakePHP(tm) Project * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -App::import('Core', 'Security'); +App::import('Component', 'auth/base_authenticate'); /** * An authentication adapter for AuthComponent. Provides the ability to authenticate using POST @@ -33,35 +33,7 @@ App::import('Core', 'Security'); * @since 2.0 * @see AuthComponent::$authenticate */ -class FormAuthenticate { - -/** - * 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); - } +class FormAuthenticate extends BaseAuthenticate { /** * Authenticates the identity contained in a request. Will use the `settings.userModel`, and `settings.fields` @@ -103,13 +75,4 @@ class FormAuthenticate { return $result[$model]; } -/** - * Hash the supplied password using the configured hashing method. - * - * @param string $password The password to hash. - * @return string Hashed string - */ - public function hash($password) { - return Security::hash($password, null, true); - } } \ No newline at end of file From 332b6cfc223fb10c7860486a0c03f6f06edc6345 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 18:10:45 -0500 Subject: [PATCH 39/65] Adding basic authentication skeleton. --- .../components/auth/basic_authenticate.php | 27 ++++ .../auth/basic_authenticate.test.php | 146 ++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 cake/libs/controller/components/auth/basic_authenticate.php create mode 100644 cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php diff --git a/cake/libs/controller/components/auth/basic_authenticate.php b/cake/libs/controller/components/auth/basic_authenticate.php new file mode 100644 index 000000000..af1361bd1 --- /dev/null +++ b/cake/libs/controller/components/auth/basic_authenticate.php @@ -0,0 +1,27 @@ +auth = new BasicAuthenticate(array( + 'fields' => array('username' => 'user', 'password' => 'password'), + 'userModel' => 'User' + )); + $password = Security::hash('password', null, true); + ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"')); + } + +/** + * 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']); + } + +/** + * test the authenticate method + * + * @return void + */ + function testAuthenticateNoData() { + $request = new CakeRequest('posts/index', false); + $request->data = array(); + $this->assertFalse($this->auth->authenticate($request)); + } + +/** + * 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)); + } + +/** + * 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)); + } + +/** + * 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)); + } + +/** + * 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); + $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)); + } + +} \ No newline at end of file From ba02483ae8b641c64cec5122dffa4d3df4060a5c Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 19:56:23 -0500 Subject: [PATCH 40/65] Adding a response parameter to authenticate() both basic and digest auth need to set response headers. --- cake/libs/controller/components/auth.php | 16 ++++++++++++---- .../components/auth/base_authenticate.php | 3 ++- .../components/auth/form_authenticate.php | 3 ++- .../components/auth/form_authenticate.test.php | 16 +++++++++------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 5f4631ab8..7f12d9842 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -34,7 +34,7 @@ App::import('Component', 'auth/base_authorize'); */ class AuthComponent extends Component { - const ALL = '*'; + const ALL = 'all'; /** * Maintains current user login state. @@ -231,6 +231,13 @@ class AuthComponent extends Component { */ public $request; +/** + * Response object + * + * @var CakeResponse + */ + public $response; + /** * Method list for bound controller * @@ -246,6 +253,7 @@ class AuthComponent extends Component { */ public function initialize($controller) { $this->request = $controller->request; + $this->response = $controller->response; $this->_methods = $controller->methods; if (Configure::read('debug') > 0) { @@ -507,7 +515,7 @@ class AuthComponent extends Component { $this->_loggedIn = false; if (empty($user)) { - $user = $this->identify($this->request); + $user = $this->identify($this->request, $this->response); } if ($user) { $this->Session->write(self::$sessionKey, $user); @@ -587,12 +595,12 @@ class AuthComponent extends Component { * @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 identify(CakeRequest $request) { + public function identify(CakeRequest $request, CakeResponse $response) { if (empty($this->_authenticateObjects)) { $this->constructAuthenticate(); } foreach ($this->_authenticateObjects as $auth) { - $result = $auth->authenticate($request); + $result = $auth->authenticate($request, $response); if (!empty($result) && is_array($result)) { return $result; } diff --git a/cake/libs/controller/components/auth/base_authenticate.php b/cake/libs/controller/components/auth/base_authenticate.php index 2bc8ad725..8c9fb145d 100644 --- a/cake/libs/controller/components/auth/base_authenticate.php +++ b/cake/libs/controller/components/auth/base_authenticate.php @@ -63,7 +63,8 @@ abstract class BaseAuthenticate { * 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); + abstract public function authenticate(CakeRequest $request, CakeResponse $response); } \ No newline at end of file diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php index 3f284eaf2..24b472376 100644 --- a/cake/libs/controller/components/auth/form_authenticate.php +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -41,9 +41,10 @@ class FormAuthenticate extends BaseAuthenticate { * 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) { + public function authenticate(CakeRequest $request, CakeResponse $response) { $userModel = $this->settings['userModel']; list($plugin, $model) = pluginSplit($userModel); diff --git a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php index b2218a75b..e6b5a4aa7 100644 --- a/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/form_authenticate.test.php @@ -17,6 +17,7 @@ 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'; @@ -42,6 +43,7 @@ class FormAuthenticateTest extends CakeTestCase { )); $password = Security::hash('password', null, true); ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"')); + $this->response = $this->getMock('CakeResponse'); } /** @@ -66,7 +68,7 @@ class FormAuthenticateTest extends CakeTestCase { function testAuthenticateNoData() { $request = new CakeRequest('posts/index', false); $request->data = array(); - $this->assertFalse($this->auth->authenticate($request)); + $this->assertFalse($this->auth->authenticate($request, $this->response)); } /** @@ -77,7 +79,7 @@ class FormAuthenticateTest extends CakeTestCase { function testAuthenticateNoUsername() { $request = new CakeRequest('posts/index', false); $request->data = array('User' => array('password' => 'foobar')); - $this->assertFalse($this->auth->authenticate($request)); + $this->assertFalse($this->auth->authenticate($request, $this->response)); } /** @@ -88,7 +90,7 @@ class FormAuthenticateTest extends CakeTestCase { function testAuthenticateNoPassword() { $request = new CakeRequest('posts/index', false); $request->data = array('User' => array('user' => 'mariano')); - $this->assertFalse($this->auth->authenticate($request)); + $this->assertFalse($this->auth->authenticate($request, $this->response)); } /** @@ -103,7 +105,7 @@ class FormAuthenticateTest extends CakeTestCase { 'user' => '> 1', 'password' => "' OR 1 = 1" )); - $this->assertFalse($this->auth->authenticate($request)); + $this->assertFalse($this->auth->authenticate($request, $this->response)); } /** @@ -117,7 +119,7 @@ class FormAuthenticateTest extends CakeTestCase { 'user' => 'mariano', 'password' => 'password' )); - $result = $this->auth->authenticate($request); + $result = $this->auth->authenticate($request, $this->response); $expected = array( 'id' => 1, 'user' => 'mariano', @@ -140,7 +142,7 @@ class FormAuthenticateTest extends CakeTestCase { 'password' => 'password' )); - $this->assertFalse($this->auth->authenticate($request)); + $this->assertFalse($this->auth->authenticate($request, $this->response)); } /** @@ -170,7 +172,7 @@ class FormAuthenticateTest extends CakeTestCase { 'password' => 'cake' )); - $result = $this->auth->authenticate($request); + $result = $this->auth->authenticate($request, $this->response); $expected = array( 'id' => 1, 'username' => 'gwoo', From 67c4c9a693b4f2a0ad3f56577d57a1c6f278ed71 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 20:28:03 -0500 Subject: [PATCH 41/65] Fix failing test and removing tests for settings that no longer exist. --- .../libs/controller/components/auth.test.php | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index bb5d5e29b..502c5dfe5 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -740,7 +740,7 @@ class AuthTest extends CakeTestCase { */ function testLoadAuthenticateNoFile() { $this->Controller->Auth->authenticate = 'Missing'; - $this->Controller->Auth->identify($this->Controller->request); + $this->Controller->Auth->identify($this->Controller->request, $this->Controller->response); } /** @@ -1176,9 +1176,7 @@ class AuthTest extends CakeTestCase { $this->Controller->components = array( 'Auth' => array( - 'fields' => array('username' => 'email', 'password' => 'password'), 'loginAction' => array('controller' => 'people', 'action' => 'login'), - 'userModel' => 'AuthUserCustomField' ), 'Session' ); @@ -1186,37 +1184,12 @@ class AuthTest extends CakeTestCase { $this->Controller->Components->trigger('initialize', array(&$this->Controller)); Router::reload(); - $this->AuthUserCustomField = new AuthUserCustomField(); - $user = array( - 'id' => 1, 'email' => 'harking@example.com', - 'password' => Security::hash(Configure::read('Security.salt') . 'cake' - )); - $user = $this->AuthUserCustomField->save($user, false); - - Router::connect('/', array('controller' => 'people', 'action' => 'login')); - $url = '/'; - $this->Controller->request->addParams(Router::parse($url)); - Router::setRequestInfo($this->Controller->request); - $this->Controller->request->data['AuthUserCustomField'] = array( - 'email' => 'harking@example.com', 'password' => 'cake' - ); - $this->Controller->request->query['url'] = substr($url, 1); - $this->Controller->Auth->startup($this->Controller); - $this->Controller->Auth->login(); - - $user = $this->Controller->Auth->user(); - $this->assertTrue(!!$user); - $expected = array( - 'fields' => array('username' => 'email', 'password' => 'password'), 'loginAction' => array('controller' => 'people', 'action' => 'login'), 'logoutRedirect' => array('controller' => 'people', 'action' => 'login'), - 'userModel' => 'AuthUserCustomField' ); - $this->assertEqual($expected['fields'], $this->Controller->Auth->fields); $this->assertEqual($expected['loginAction'], $this->Controller->Auth->loginAction); $this->assertEqual($expected['logoutRedirect'], $this->Controller->Auth->logoutRedirect); - $this->assertEqual($expected['userModel'], $this->Controller->Auth->userModel); } /** From e8bf6ed176136b6923486d88eefa104755e7f6c4 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 20:28:24 -0500 Subject: [PATCH 42/65] Basic tests for BasicAuthenticate are passing. --- .../components/auth/basic_authenticate.php | 77 +++++++++++- .../auth/basic_authenticate.test.php | 111 ++++++++++++++---- 2 files changed, 162 insertions(+), 26 deletions(-) diff --git a/cake/libs/controller/components/auth/basic_authenticate.php b/cake/libs/controller/components/auth/basic_authenticate.php index af1361bd1..085b0770c 100644 --- a/cake/libs/controller/components/auth/basic_authenticate.php +++ b/cake/libs/controller/components/auth/basic_authenticate.php @@ -13,15 +13,88 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::import('Component', 'auth/base_authenticate'); +App::import('Core', 'String'); + 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).` + * + * @var array + */ + public $settings = array( + 'fields' => array( + 'username' => 'username', + 'password' => 'password' + ), + 'userModel' => 'User', + 'scope' => array(), + 'realm' => '', + ); + +/** + * Constructor, completes configuration for basic authentication. + * + * @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) { - + public function authenticate(CakeRequest $request, CakeResponse $response) { + $username = env('PHP_AUTH_USER'); + $pass = env('PHP_AUTH_PW'); + if (empty($username) || empty($pass)) { + $response->header($this->loginHeaders()); + return false; + } + $userModel = $this->settings['userModel']; + list($plugin, $model) = pluginSplit($userModel); + $fields = $this->settings['fields']; + + $conditions = array( + $model . '.' . $fields['username'] => $username, + $model . '.' . $fields['password'] => $this->hash($pass), + ); + 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])) { + $response->header($this->loginHeaders()); + $response->header('Location', Router::reverse($request)); + $response->statusCode(401); + $response->send(); + return false; + } + unset($result[$model][$fields['password']]); + return $result[$model]; + } + +/** + * Generate the login headers + * + * @return string Headers for logging in. + */ + public function loginHeaders() { + return sprintf('WWW-Authenticate: Basic realm="%s"', $this->settings['realm']); } } \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php index 7da7e692b..867b50c11 100644 --- a/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php @@ -17,6 +17,8 @@ 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'; @@ -38,10 +40,24 @@ class BasicAuthenticateTest extends CakeTestCase { parent::setUp(); $this->auth = new BasicAuthenticate(array( 'fields' => array('username' => 'user', 'password' => 'password'), - 'userModel' => 'User' + '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; } /** @@ -56,6 +72,7 @@ class BasicAuthenticateTest extends CakeTestCase { )); $this->assertEquals('AuthUser', $object->settings['userModel']); $this->assertEquals(array('username' => 'user', 'password' => 'password'), $object->settings['fields']); + $this->assertEquals(env('SERVER_NAME'), $object->settings['realm']); } /** @@ -65,8 +82,12 @@ class BasicAuthenticateTest extends CakeTestCase { */ function testAuthenticateNoData() { $request = new CakeRequest('posts/index', false); - $request->data = array(); - $this->assertFalse($this->auth->authenticate($request)); + + $this->response->expects($this->once()) + ->method('header') + ->with('WWW-Authenticate: Basic realm="localhost"'); + + $this->assertFalse($this->auth->authenticate($request, $this->response)); } /** @@ -76,8 +97,13 @@ class BasicAuthenticateTest extends CakeTestCase { */ function testAuthenticateNoUsername() { $request = new CakeRequest('posts/index', false); - $request->data = array('User' => array('password' => 'foobar')); - $this->assertFalse($this->auth->authenticate($request)); + $_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)); } /** @@ -87,8 +113,13 @@ class BasicAuthenticateTest extends CakeTestCase { */ function testAuthenticateNoPassword() { $request = new CakeRequest('posts/index', false); - $request->data = array('User' => array('user' => 'mariano')); - $this->assertFalse($this->auth->authenticate($request)); + $_SERVER['PHP_AUTH_USER'] = 'mariano'; + + $this->response->expects($this->once()) + ->method('header') + ->with('WWW-Authenticate: Basic realm="localhost"'); + + $this->assertFalse($this->auth->authenticate($request, $this->response)); } /** @@ -98,14 +129,30 @@ class BasicAuthenticateTest extends CakeTestCase { */ 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)); + $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->once()) + ->method('header') + ->with('WWW-Authenticate: Basic realm="localhost"'); + + $result = $this->auth->authenticate($request, $this->response); + $this->assertFalse($result); + } /** * test authenticate sucesss * @@ -113,11 +160,12 @@ class BasicAuthenticateTest extends CakeTestCase { */ function testAuthenticateSuccess() { $request = new CakeRequest('posts/index', false); - $request->data = array('User' => array( - 'user' => 'mariano', - 'password' => 'password' - )); - $result = $this->auth->authenticate($request); + $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', @@ -132,15 +180,30 @@ class BasicAuthenticateTest extends CakeTestCase { * * @return void */ - function testAuthenticateScopeFail() { + function testAuthenticateFailReChallenge() { $this->auth->settings['scope'] = array('user' => 'nate'); $request = new CakeRequest('posts/index', false); - $request->data = array('User' => array( - 'user' => 'mariano', - 'password' => 'password' - )); + $request->addParams(array('pass' => array(), 'named' => array())); - $this->assertFalse($this->auth->authenticate($request)); + $_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('header') + ->with('Location', Router::reverse($request)); + + $this->response->expects($this->at(2)) + ->method('statusCode') + ->with(401); + + $this->response->expects($this->at(3)) + ->method('send'); + + $this->assertFalse($this->auth->authenticate($request, $this->response)); } } \ No newline at end of file From 4610a0bf3cda634cf66c07bd411e1553d0caf14e Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 20:41:02 -0500 Subject: [PATCH 43/65] Adding some more tests for basic auth challenge headers. --- cake/libs/controller/components/auth/basic_authenticate.php | 3 +++ .../controller/components/auth/basic_authenticate.test.php | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth/basic_authenticate.php b/cake/libs/controller/components/auth/basic_authenticate.php index 085b0770c..0ce840ca5 100644 --- a/cake/libs/controller/components/auth/basic_authenticate.php +++ b/cake/libs/controller/components/auth/basic_authenticate.php @@ -59,10 +59,13 @@ class BasicAuthenticate extends BaseAuthenticate { public function authenticate(CakeRequest $request, CakeResponse $response) { $username = env('PHP_AUTH_USER'); $pass = env('PHP_AUTH_PW'); + if (empty($username) || empty($pass)) { $response->header($this->loginHeaders()); + $response->send(); return false; } + $userModel = $this->settings['userModel']; list($plugin, $model) = pluginSplit($userModel); $fields = $this->settings['fields']; diff --git a/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php index 867b50c11..656ca0fbb 100644 --- a/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php @@ -146,10 +146,13 @@ class BasicAuthenticateTest extends CakeTestCase { $request = new CakeRequest('posts/index', false); $request->addParams(array('pass' => array(), 'named' => array())); - $this->response->expects($this->once()) + $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); } From bcd8dcd0f7f997ed1094e6391ff6ce5e879d6fd0 Mon Sep 17 00:00:00 2001 From: mark_story Date: Fri, 21 Jan 2011 20:52:38 -0500 Subject: [PATCH 44/65] Extracting common logic into the base class. --- .../components/auth/base_authenticate.php | 30 +++++++++++++++++++ .../components/auth/basic_authenticate.php | 20 ++----------- .../components/auth/form_authenticate.php | 18 ++--------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/cake/libs/controller/components/auth/base_authenticate.php b/cake/libs/controller/components/auth/base_authenticate.php index 8c9fb145d..0753640f8 100644 --- a/cake/libs/controller/components/auth/base_authenticate.php +++ b/cake/libs/controller/components/auth/base_authenticate.php @@ -59,6 +59,36 @@ abstract class BaseAuthenticate { return Security::hash($password, null, true); } +/** + * 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'] => $this->hash($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. * diff --git a/cake/libs/controller/components/auth/basic_authenticate.php b/cake/libs/controller/components/auth/basic_authenticate.php index 0ce840ca5..3d6cbc845 100644 --- a/cake/libs/controller/components/auth/basic_authenticate.php +++ b/cake/libs/controller/components/auth/basic_authenticate.php @@ -66,30 +66,16 @@ class BasicAuthenticate extends BaseAuthenticate { return false; } - $userModel = $this->settings['userModel']; - list($plugin, $model) = pluginSplit($userModel); - $fields = $this->settings['fields']; + $result = $this->_findUser($username, $pass); - $conditions = array( - $model . '.' . $fields['username'] => $username, - $model . '.' . $fields['password'] => $this->hash($pass), - ); - 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])) { + if (empty($result)) { $response->header($this->loginHeaders()); $response->header('Location', Router::reverse($request)); $response->statusCode(401); $response->send(); return false; } - unset($result[$model][$fields['password']]); - return $result[$model]; + return $result; } /** diff --git a/cake/libs/controller/components/auth/form_authenticate.php b/cake/libs/controller/components/auth/form_authenticate.php index 24b472376..d24d9acf2 100644 --- a/cake/libs/controller/components/auth/form_authenticate.php +++ b/cake/libs/controller/components/auth/form_authenticate.php @@ -58,22 +58,10 @@ class FormAuthenticate extends BaseAuthenticate { ) { return false; } - $conditions = array( - $model . '.' . $fields['username'] => $request->data[$model][$fields['username']], - $model . '.' . $fields['password'] => $this->hash($request->data[$model][$fields['password']]), + return $this->_findUser( + $request->data[$model][$fields['username']], + $request->data[$model][$fields['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]; } } \ No newline at end of file From 945e49ad09816c79892fdddbf9fe313317316fd9 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 22 Jan 2011 13:29:56 -0500 Subject: [PATCH 45/65] Starting Digest auth, using Basic auth as a starting base. --- .../components/auth/digest_authenticate.php | 108 +++++++++ .../auth/digest_authenticate.test.php | 216 ++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 cake/libs/controller/components/auth/digest_authenticate.php create mode 100644 cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php diff --git a/cake/libs/controller/components/auth/digest_authenticate.php b/cake/libs/controller/components/auth/digest_authenticate.php new file mode 100644 index 000000000..f58623315 --- /dev/null +++ b/cake/libs/controller/components/auth/digest_authenticate.php @@ -0,0 +1,108 @@ + 1).` + * + * @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. + * + * @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['realm'] = 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) { + $username = env('PHP_AUTH_USER'); + $pass = env('PHP_AUTH_PW'); + + if (empty($username) || empty($pass)) { + $response->header($this->loginHeaders()); + $response->send(); + return false; + } + + $result = $this->_findUser($username, $pass); + + if (empty($result)) { + $response->header($this->loginHeaders()); + $response->header('Location', Router::reverse($request)); + $response->statusCode(401); + $response->send(); + return false; + } + return $result; + } + +/** + * 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); + } +} \ No newline at end of file diff --git a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php new file mode 100644 index 000000000..40a5a6b71 --- /dev/null +++ b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php @@ -0,0 +1,216 @@ +auth = new DigestAuthenticate(array( + 'fields' => array('username' => 'user', 'password' => 'password'), + 'userModel' => 'User', + 'realm' => 'localhost', + 'nonce' => 123, + 'opaque' => '123abc' + )); + + $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 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 testAuthenticateNoUsername() { + $request = new CakeRequest('posts/index', false); + $_SERVER['PHP_AUTH_PW'] = 'foobar'; + + $this->response->expects($this->once()) + ->method('header') + ->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); + + $this->assertFalse($this->auth->authenticate($request, $this->response)); + } + +/** + * test the authenticate method + * + * @return void + */ + function testAuthenticateNoPassword() { + $request = new CakeRequest('posts/index', false); + $_SERVER['PHP_AUTH_USER'] = 'mariano'; + + $this->response->expects($this->once()) + ->method('header') + ->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); + + $this->assertFalse($this->auth->authenticate($request, $this->response)); + } + +/** + * test the authenticate method + * + * @return void + */ + function testAuthenticateInjection() { + $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: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); + + $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: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); + + $this->response->expects($this->at(1)) + ->method('header') + ->with('Location', Router::reverse($request)); + + $this->response->expects($this->at(2)) + ->method('statusCode') + ->with(401); + + $this->response->expects($this->at(3)) + ->method('send'); + + $this->assertFalse($this->auth->authenticate($request, $this->response)); + } + +} \ No newline at end of file From 705b3288e1dccd80eb0fc32422a546add7a879f9 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 22 Jan 2011 14:48:12 -0500 Subject: [PATCH 46/65] Basics of DigestAuth are working. --- .../components/auth/digest_authenticate.php | 108 +++++++++++- .../auth/digest_authenticate.test.php | 159 +++++++++++++----- 2 files changed, 222 insertions(+), 45 deletions(-) diff --git a/cake/libs/controller/components/auth/digest_authenticate.php b/cake/libs/controller/components/auth/digest_authenticate.php index f58623315..10212f1a0 100644 --- a/cake/libs/controller/components/auth/digest_authenticate.php +++ b/cake/libs/controller/components/auth/digest_authenticate.php @@ -66,18 +66,19 @@ class DigestAuthenticate extends BaseAuthenticate { * @return mixed Either false on failure, or an array of user data on success. */ public function authenticate(CakeRequest $request, CakeResponse $response) { - $username = env('PHP_AUTH_USER'); - $pass = env('PHP_AUTH_PW'); + $digest = $this->_getDigest(); - if (empty($username) || empty($pass)) { + if (empty($digest)) { $response->header($this->loginHeaders()); $response->send(); return false; } - $result = $this->_findUser($username, $pass); + $result = $this->_findUser($digest['username'], null); + $password = $result[$this->settings['fields']['password']]; + unset($result[$this->settings['fields']['password']]); - if (empty($result)) { + if (empty($result) || $digest['response'] !== $this->generateResponseHash($digest, $password)) { $response->header($this->loginHeaders()); $response->header('Location', Router::reverse($request)); $response->statusCode(401); @@ -87,6 +88,103 @@ class DigestAuthenticate extends BaseAuthenticate { return $result; } +/** + * Find a user record using the standard options. + * + * @param string $username The username/identifier. + * @param string $password Unused password, digest doesn't require passwords. + * @return Mixed Either false on failure, or an array of user data. + */ + protected function _findUser($username, $password) { + $userModel = $this->settings['userModel']; + list($plugin, $model) = pluginSplit($userModel); + $fields = $this->settings['fields']; + + $conditions = array( + $model . '.' . $fields['username'] => $username, + ); + if (!empty($this->settings['scope'])) { + $conditions = array_merge($conditions, $this->settings['scope']); + } + $result = ClassRegistry::init($userModel)->find('first', array( + 'conditions' => $conditions, + 'recursive' => 0 + )); + if (empty($result) || empty($result[$model])) { + return false; + } + return $result[$model]; + } + +/** + * Gets the digest headers from the request/environment. + * + * @return array Array of digest information. + */ + protected function _getDigest() { + $digest = env('PHP_AUTH_DIGEST'); + if (empty($digest) && function_exists('apache_request_headers')) { + $headers = apache_request_headers(); + if (!empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') { + $digest = substr($headers['Authorization'], 7); + } + } + if (empty($digest)) { + return false; + } + return $this->parseAuthData($digest); + } + +/** + * Parse the digest authentication headers and split them up. + * + * @param string $digest The raw digest authentication headers. + * @return array An array of digest authentication headers + */ + public function parseAuthData($digest) { + if (substr($digest, 0, 7) == 'Digest ') { + $digest = substr($digest, 7); + } + $keys = $match = array(); + $req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1); + preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER); + + foreach ($match as $i) { + $keys[$i[1]] = $i[3]; + unset($req[$i[1]]); + } + + if (empty($req)) { + return $keys; + } + return null; + } + +/** + * Generate the response hash for a given digest array. + * + * @param array $digest Digest information containing data from DigestAuthenticate::parseAuthData(). + * @param string $password The digest hash password generated with DigestAuthenticate::password() + * @return string Response hash + */ + public function generateResponseHash($digest, $password) { + return md5( + $password . + ':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' . + md5(env('REQUEST_METHOD') . ':' . $digest['uri']) + ); + } + +/** + * Creates an auth digest password hash to store + * + * @param string $password The unhashed password to make a digest hash for. + * @return string the hashed password that can later be used with Digest authentication. + */ + public static function password($username, $realm, $password) { + return md5($username . ':' . $realm . ':' . $password); + } + /** * Generate the login headers * diff --git a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php index 40a5a6b71..4709890be 100644 --- a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php @@ -46,7 +46,7 @@ class DigestAuthenticateTest extends CakeTestCase { 'opaque' => '123abc' )); - $password = Security::hash('password', null, true); + $password = DigestAuthenticate::password('mariano', 'localhost', 'cake'); ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"')); $this->server = $_SERVER; $this->response = $this->getMock('CakeResponse'); @@ -99,44 +99,36 @@ class DigestAuthenticateTest extends CakeTestCase { * * @return void */ - function testAuthenticateNoUsername() { - $request = new CakeRequest('posts/index', false); - $_SERVER['PHP_AUTH_PW'] = 'foobar'; - - $this->response->expects($this->once()) - ->method('header') - ->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); - - $this->assertFalse($this->auth->authenticate($request, $this->response)); - } - -/** - * test the authenticate method - * - * @return void - */ - function testAuthenticateNoPassword() { - $request = new CakeRequest('posts/index', false); - $_SERVER['PHP_AUTH_USER'] = 'mariano'; - - $this->response->expects($this->once()) - ->method('header') - ->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); - - $this->assertFalse($this->auth->authenticate($request, $this->response)); - } - -/** - * test the authenticate method - * - * @return void - */ - function testAuthenticateInjection() { + function testAuthenticateWrongUsername() { $request = new CakeRequest('posts/index', false); $request->addParams(array('pass' => array(), 'named' => array())); - $_SERVER['PHP_AUTH_USER'] = '> 1'; - $_SERVER['PHP_AUTH_PW'] = "' OR 1 = 1"; + $_SERVER['PHP_AUTH_DIGEST'] = <<response->expects($this->at(0)) + ->method('header') + ->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); + + $this->response->expects($this->at(1)) + ->method('header') + ->with('Location', Router::reverse($request)); + + $this->response->expects($this->at(2)) + ->method('statusCode') + ->with(401); + + $this->response->expects($this->at(3)) + ->method('send'); $this->assertFalse($this->auth->authenticate($request, $this->response)); } @@ -160,6 +152,7 @@ class DigestAuthenticateTest extends CakeTestCase { $result = $this->auth->authenticate($request, $this->response); $this->assertFalse($result); } + /** * test authenticate sucesss * @@ -169,8 +162,17 @@ class DigestAuthenticateTest extends CakeTestCase { $request = new CakeRequest('posts/index', false); $request->addParams(array('pass' => array(), 'named' => array())); - $_SERVER['PHP_AUTH_USER'] = 'mariano'; - $_SERVER['PHP_AUTH_PW'] = 'password'; + $_SERVER['PHP_AUTH_DIGEST'] = <<auth->authenticate($request, $this->response); $expected = array( @@ -192,8 +194,17 @@ class DigestAuthenticateTest extends CakeTestCase { $request = new CakeRequest('posts/index', false); $request->addParams(array('pass' => array(), 'named' => array())); - $_SERVER['PHP_AUTH_USER'] = 'mariano'; - $_SERVER['PHP_AUTH_PW'] = 'password'; + $_SERVER['PHP_AUTH_DIGEST'] = <<response->expects($this->at(0)) ->method('header') @@ -213,4 +224,72 @@ class DigestAuthenticateTest extends CakeTestCase { $this->assertFalse($this->auth->authenticate($request, $this->response)); } +/** + * testParseDigestAuthData method + * + * @access public + * @return void + */ + function testParseAuthData() { + $digest = << '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 = << '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); + } + } \ No newline at end of file From d5f5ae3271e3915abe4c5f4d2c4d67bb4e22d2d4 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 22 Jan 2011 15:46:21 -0500 Subject: [PATCH 47/65] Fixing typo where realm would be wrong if not specified. --- .../controller/components/auth/digest_authenticate.php | 2 +- .../components/auth/digest_authenticate.test.php | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth/digest_authenticate.php b/cake/libs/controller/components/auth/digest_authenticate.php index 10212f1a0..9e425f87a 100644 --- a/cake/libs/controller/components/auth/digest_authenticate.php +++ b/cake/libs/controller/components/auth/digest_authenticate.php @@ -51,7 +51,7 @@ class DigestAuthenticate extends BaseAuthenticate { $this->settings['realm'] = env('SERVER_NAME'); } if (empty($this->settings['nonce'])) { - $this->settings['realm'] = uniqid(''); + $this->settings['nonce'] = uniqid(''); } if (empty($this->settings['opaque'])) { $this->settings['opaque'] = md5($this->settings['realm']); diff --git a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php index 4709890be..08cba463b 100644 --- a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php @@ -292,4 +292,14 @@ DIGEST; $this->assertIdentical($expected, $result); } +/** + * test password hashing + * + * @return void + */ + function testPassword() { + $result = DigestAuthenticate::password('mark', 'localhost', 'password'); + $expected = md5('mark:localhost:password'); + $this->assertEquals($expected, $result); + } } \ No newline at end of file From 669672a1a01782620b5ab6227724f21968e9f42c Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 22 Jan 2011 16:27:07 -0500 Subject: [PATCH 48/65] Adding doc blocks. --- .../components/auth/basic_authenticate.php | 30 ++++++++++++- .../components/auth/digest_authenticate.php | 45 ++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/cake/libs/controller/components/auth/basic_authenticate.php b/cake/libs/controller/components/auth/basic_authenticate.php index 3d6cbc845..08a358463 100644 --- a/cake/libs/controller/components/auth/basic_authenticate.php +++ b/cake/libs/controller/components/auth/basic_authenticate.php @@ -13,9 +13,32 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::import('Component', 'auth/base_authenticate'); -App::import('Core', 'String'); - +/** + * 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. @@ -24,6 +47,7 @@ class BasicAuthenticate extends BaseAuthenticate { * - `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 */ @@ -40,6 +64,7 @@ class BasicAuthenticate extends BaseAuthenticate { /** * Constructor, completes configuration for basic authentication. * + * @param array $settings An array of settings. * @return void */ public function __construct($settings) { @@ -48,6 +73,7 @@ class BasicAuthenticate extends BaseAuthenticate { $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. diff --git a/cake/libs/controller/components/auth/digest_authenticate.php b/cake/libs/controller/components/auth/digest_authenticate.php index 9e425f87a..899701182 100644 --- a/cake/libs/controller/components/auth/digest_authenticate.php +++ b/cake/libs/controller/components/auth/digest_authenticate.php @@ -15,7 +15,45 @@ App::import('Component', 'auth/base_authenticate'); App::import('Core', 'String'); - +/** + * 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. @@ -24,6 +62,10 @@ class DigestAuthenticate extends BaseAuthenticate { * - `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 */ @@ -43,6 +85,7 @@ class DigestAuthenticate extends BaseAuthenticate { /** * Constructor, completes configuration for digest authentication. * + * @param array $settings An array of settings. * @return void */ public function __construct($settings) { From 0e7f49048987f77baf493cc5cc156987643adc80 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 22 Jan 2011 16:29:07 -0500 Subject: [PATCH 49/65] Fixing failing test. --- .../libs/controller/components/auth/actions_authorize.test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php b/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php index 7af508c9c..69d0feb9f 100644 --- a/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php +++ b/cake/tests/cases/libs/controller/components/auth/actions_authorize.test.php @@ -33,7 +33,7 @@ class ActionsAuthorizeTest extends CakeTestCase { $this->controller->Components = $this->getMock('ComponentCollection'); $this->auth = new ActionsAuthorize($this->controller); - $this->auth->actionPath = '/controllers'; + $this->auth->settings['actionPath'] = '/controllers'; } /** From 91a3fc4aeecb427b70ce0ebdbf619c40fd3b3e38 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 22 Jan 2011 17:15:55 -0500 Subject: [PATCH 50/65] Fixing tests so they run in the webrunner. --- .../controller/components/auth/digest_authenticate.test.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php index 08cba463b..40b8ef4c1 100644 --- a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php @@ -38,6 +38,7 @@ class DigestAuthenticateTest extends CakeTestCase { */ function setUp() { parent::setUp(); + $this->server = $_SERVER; $this->auth = new DigestAuthenticate(array( 'fields' => array('username' => 'user', 'password' => 'password'), 'userModel' => 'User', @@ -48,7 +49,8 @@ class DigestAuthenticateTest extends CakeTestCase { $password = DigestAuthenticate::password('mariano', 'localhost', 'cake'); ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"')); - $this->server = $_SERVER; + + $_SERVER['REQUEST_METHOD'] = 'GET'; $this->response = $this->getMock('CakeResponse'); } @@ -170,7 +172,7 @@ uri="/dir/index.html", qop=auth, nc=1, cnonce="123", -response="5e064cc2f3b20894a806b2de3edf9536", +response="06b257a54befa2ddfb9bfa134224aa29", opaque="123abc" DIGEST; From 82851895ef16e0cb4058f6d88611a89b647ce888 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 23 Jan 2011 11:09:01 -0500 Subject: [PATCH 51/65] Shifting includes around. --- cake/libs/controller/components/auth.php | 1 + cake/libs/controller/components/auth/digest_authenticate.php | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 7f12d9842..fb2e0e48e 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -23,6 +23,7 @@ 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 diff --git a/cake/libs/controller/components/auth/digest_authenticate.php b/cake/libs/controller/components/auth/digest_authenticate.php index 899701182..91d8ed270 100644 --- a/cake/libs/controller/components/auth/digest_authenticate.php +++ b/cake/libs/controller/components/auth/digest_authenticate.php @@ -13,7 +13,6 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::import('Component', 'auth/base_authenticate'); -App::import('Core', 'String'); /** * Digest Authentication adapter for AuthComponent. From 2f917674d12e59e2a1cef320c9656f3a9d326819 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 24 Jan 2011 22:13:39 -0500 Subject: [PATCH 52/65] Fixing docblock to reflect change from '*' to 'all'. Adding note about constant. --- cake/libs/controller/components/auth.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index fb2e0e48e..543012c91 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -65,11 +65,11 @@ class AuthComponent extends Component { * * 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 '*' key: + * using the 'all' key: * * {{{ * $this->Auth->authenticate = array( - * '*' => array( + * 'all' => array( * 'userModel' => 'Users.User', * 'scope' => array('User.active' => 1) * ), @@ -78,6 +78,8 @@ class AuthComponent extends Component { * ); * }}} * + * You can also use AuthComponent::ALL instead of the string 'all'. + * * @var array * @link http://book.cakephp.org/view/1278/authenticate */ @@ -104,11 +106,11 @@ class AuthComponent extends Component { * * 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 '*' key: + * using the 'all' key: * * {{{ * $this->Auth->authorize = array( - * '*' => array( + * 'all' => array( * 'actionPath' => 'controllers/' * ), * 'Crud', @@ -116,6 +118,8 @@ class AuthComponent extends Component { * ); * }}} * + * You can also use AuthComponent::ALL instead of the string 'all' + * * @var mixed * @link http://book.cakephp.org/view/1275/authorize */ From ffb6c29e7900bd1fde49050d222cf14158c60f86 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 25 Jan 2011 21:55:29 -0500 Subject: [PATCH 53/65] Removing dead fixtures, dead properties and dead models. --- .../libs/controller/components/auth.test.php | 135 +----------------- 1 file changed, 3 insertions(+), 132 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index 502c5dfe5..a34d7a070 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -20,7 +20,6 @@ App::import('Core', 'Controller'); App::import('Component', array('Auth', 'Acl')); App::import('Component', 'auth/form_authenticate'); App::import('Model', 'DbAcl'); -App::import('Core', 'Xml'); /** * TestAuthComponent class @@ -38,14 +37,6 @@ class TestAuthComponent extends AuthComponent { */ public $testStop = false; -/** - * Sets default login state - * - * @var bool true - * @access protected - */ - protected $_loggedIn = true; - /** * stop method * @@ -120,92 +111,6 @@ class AuthUser extends CakeTestModel { } } -/** - * AuthUserCustomField class - * - * @package cake.tests.cases.libs.controller.components - */ -class AuthUserCustomField extends AuthUser { - -/** - * name property - * - * @var string 'AuthUser' - * @access public - */ - public $name = 'AuthUserCustomField'; -} - -/** -* UuidUser class -* -* @package cake -* @package cake.tests.cases.libs.controller.components -*/ -class UuidUser extends CakeTestModel { - -/** - * name property - * - * @var string 'AuthUser' - * @access public - */ - public $name = 'UuidUser'; - -/** - * useDbConfig property - * - * @var string 'test' - * @access public - */ - public $useDbConfig = 'test'; - -/** - * useTable property - * - * @var string 'uuid' - * @access public - */ - public $useTable = 'uuids'; - -/** - * parentNode method - * - * @access public - * @return void - */ - function parentNode() { - return true; - } - -/** - * bindNode method - * - * @param mixed $object - * @access public - * @return void - */ - function bindNode($object) { - return 'Roles/Admin'; - } - -/** - * isAuthorized method - * - * @param mixed $user - * @param mixed $controller - * @param mixed $action - * @access public - * @return void - */ - function isAuthorized($user, $controller = null, $action = null) { - if (!empty($user)) { - return true; - } - return false; - } -} - /** * AuthTestController class * @@ -295,7 +200,7 @@ class AuthTestController extends Controller { * @return void */ function logout() { - // $this->redirect($this->Auth->logout()); + } /** @@ -461,7 +366,7 @@ class AuthTest extends CakeTestCase { * @var array * @access public */ - public $fixtures = array('core.uuid', 'core.auth_user', 'core.auth_user_custom_field', 'core.aro', 'core.aco', 'core.aros_aco', 'core.aco_action'); + public $fixtures = array('core.auth_user'); /** * initialized property @@ -485,9 +390,6 @@ class AuthTest extends CakeTestCase { Configure::write('Security.salt', 'YJfIxfs2guVoUubWDYhG93b0qyJfIxfs2guwvniR2G0FgaC9mi'); Configure::write('Security.cipherSeed', 770011223369876); - Configure::write('Acl.database', 'test'); - Configure::write('Acl.classname', 'DbAcl'); - $request = new CakeRequest(null, false); $this->Controller = new AuthTestController($request); @@ -497,9 +399,6 @@ class AuthTest extends CakeTestCase { ); $this->Controller->beforeFilter(); - $view = new View($this->Controller); - ClassRegistry::addObject('view', $view); - $this->Controller->Session->delete('Auth'); $this->Controller->Session->delete('Message.auth'); @@ -645,35 +544,6 @@ class AuthTest extends CakeTestCase { $this->assertFalse($result); } - -/** - * testAuthorizeModel method - * - * @access public - * @return void - */ - function testAuthorizeModel() { - $this->markTestSkipped('This is not implemented'); - - $this->AuthUser = new AuthUser(); - $user = $this->AuthUser->find(); - $this->Controller->Session->write('Auth', $user); - - $this->Controller->request['controller'] = 'auth_test'; - $this->Controller->request['action'] = 'add'; - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->authorize = array('model'=>'AuthUser'); - $result = $this->Controller->Auth->startup($this->Controller); - $this->assertTrue($result); - - $this->Controller->Session->delete('Auth'); - $this->Controller->Auth->startup($this->Controller); - $this->assertTrue($this->Controller->Session->check('Message.auth')); - $result = $this->Controller->Auth->isAuthorized(); - $this->assertFalse($result); - } - /** * @expectedException CakeException * @return void @@ -1177,6 +1047,7 @@ class AuthTest extends CakeTestCase { $this->Controller->components = array( 'Auth' => array( 'loginAction' => array('controller' => 'people', 'action' => 'login'), + 'logoutRedirect' => array('controller' => 'people', 'action' => 'login'), ), 'Session' ); From af934e3443dc33b61380b97f60e91089f51ce200 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 27 Jan 2011 21:10:57 -0500 Subject: [PATCH 54/65] Removing more dead code in the Auth test. --- .../libs/controller/components/auth.test.php | 55 +------------------ 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index a34d7a070..e1f9e4edd 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -73,42 +73,6 @@ class AuthUser extends CakeTestModel { */ public $useDbConfig = 'test'; -/** - * parentNode method - * - * @access public - * @return void - */ - function parentNode() { - return true; - } - -/** - * bindNode method - * - * @param mixed $object - * @access public - * @return void - */ - function bindNode($object) { - return 'Roles/Admin'; - } - -/** - * isAuthorized method - * - * @param mixed $user - * @param mixed $controller - * @param mixed $action - * @access public - * @return void - */ - function isAuthorized($user, $controller = null, $action = null) { - if (!empty($user)) { - return true; - } - return false; - } } /** @@ -244,26 +208,9 @@ class AuthTestController extends Controller { * @return void */ function isAuthorized() { - if (isset($this->request['testControllerAuth'])) { - return false; - } - return true; + } -/** - * Mock delete method - * - * @param mixed $url - * @param mixed $status - * @param mixed $exit - * @access public - * @return void - */ - function delete($id = null) { - if ($this->TestAuth->testStop !== true && $id !== null) { - echo 'Deleted Record: ' . var_export($id, true); - } - } } /** From dc7565ad4771dd3bc28fc84e0a61c3c29dd3da1d Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 27 Jan 2011 21:55:28 -0500 Subject: [PATCH 55/65] Removing crufty methods Removing additiona ->Controller all over the place --- .../libs/controller/components/auth.test.php | 247 ++++++++---------- 1 file changed, 112 insertions(+), 135 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index e1f9e4edd..f2fed0110 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -105,7 +105,7 @@ class AuthTestController extends Controller { * @var array * @access public */ - public $components = array('Session', 'Auth', 'Acl'); + public $components = array('Session', 'Auth'); /** * testUrl property @@ -129,16 +129,6 @@ class AuthTestController extends Controller { parent::__construct($request); } -/** - * beforeFilter method - * - * @access public - * @return void - */ - function beforeFilter() { - $this->Auth->userModel = 'AuthUser'; - } - /** * login method * @@ -340,14 +330,14 @@ class AuthTest extends CakeTestCase { $request = new CakeRequest(null, false); $this->Controller = new AuthTestController($request); - $this->Controller->Components->init($this->Controller); - $this->Controller->Components->trigger( - 'initialize', array(&$this->Controller), array('triggerDisabled' => true) - ); - $this->Controller->beforeFilter(); - $this->Controller->Session->delete('Auth'); - $this->Controller->Session->delete('Message.auth'); + $collection = new ComponentCollection(); + $collection->init($this->Controller); + $this->Auth = new TestAuthComponent($collection); + $this->Auth->request = $request; + $this->Auth->response = $this->getMock('CakeResponse'); + + $this->Controller->Components->init($this->Controller); $this->initialized = true; Router::reload(); @@ -367,7 +357,7 @@ class AuthTest extends CakeTestCase { $this->Controller->Session->delete('Auth'); $this->Controller->Session->delete('Message.auth'); - unset($this->Controller, $this->AuthUser); + unset($this->Controller, $this->Auth); } /** @@ -377,7 +367,7 @@ class AuthTest extends CakeTestCase { * @return void */ function testNoAuth() { - $this->assertFalse($this->Controller->Auth->isAuthorized()); + $this->assertFalse($this->Auth->isAuthorized()); } /** @@ -409,15 +399,15 @@ class AuthTest extends CakeTestCase { */ function testLogin() { $this->getMock('FormAuthenticate', array(), array(), 'AuthLoginFormAuthenticate', false); - $this->Controller->Auth->authenticate = array( + $this->Auth->authenticate = array( 'AuthLoginForm' => array( 'userModel' => 'AuthUser' ) ); - $mocks = $this->Controller->Auth->constructAuthenticate(); + $mocks = $this->Auth->constructAuthenticate(); $this->mockObjects[] = $mocks[0]; - $this->Controller->Auth->request->data = array( + $this->Auth->request->data = array( 'AuthUser' => array( 'username' => 'mark', 'password' => Security::hash('cake', null, true) @@ -431,14 +421,14 @@ class AuthTest extends CakeTestCase { $mocks[0]->expects($this->once()) ->method('authenticate') - ->with($this->Controller->Auth->request) + ->with($this->Auth->request) ->will($this->returnValue($user)); - $result = $this->Controller->Auth->login(); + $result = $this->Auth->login(); $this->assertTrue($result); - $this->assertTrue($this->Controller->Auth->loggedIn()); - $this->assertEquals($user, $this->Controller->Auth->user()); + $this->assertTrue($this->Auth->loggedIn()); + $this->assertEquals($user, $this->Auth->user()); } /** @@ -456,11 +446,11 @@ class AuthTest extends CakeTestCase { $this->Controller->request->query['url'] = 'auth_test/login'; $this->Controller->Session->delete('Auth'); - $this->Controller->Auth->loginRedirect = '/users/dashboard'; - $this->Controller->Auth->loginAction = 'auth_test/login'; - $this->Controller->Auth->userModel = 'AuthUser'; + $this->Auth->loginRedirect = '/users/dashboard'; + $this->Auth->loginAction = 'auth_test/login'; + $this->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->startup($this->Controller); $redirect = $this->Controller->Session->read('Auth.redirect'); $this->assertNull($redirect); } @@ -717,17 +707,16 @@ class AuthTest extends CakeTestCase { 'AuthUser' => array('id' => '1', 'username' => 'nate') )); - $this->Controller->request->addParams(Router::parse('users/login')); - $this->Controller->request->query['url'] = 'users/login'; - $this->Controller->Auth->initialize($this->Controller); + $this->Auth->request->addParams(Router::parse('users/login')); + $this->Auth->request->query['url'] = 'users/login'; + $this->Auth->initialize($this->Controller); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->loginRedirect = array( + $this->Auth->loginRedirect = array( 'controller' => 'pages', 'action' => 'display', 'welcome' ); - $this->Controller->Auth->startup($this->Controller); - $expected = Router::normalize($this->Controller->Auth->loginRedirect); - $this->assertEqual($expected, $this->Controller->Auth->redirect()); + $this->Auth->startup($this->Controller); + $expected = Router::normalize($this->Auth->loginRedirect); + $this->assertEqual($expected, $this->Auth->redirect()); $this->Controller->Session->delete('Auth'); @@ -741,17 +730,16 @@ class AuthTest extends CakeTestCase { 'AuthUser' => array('id' => '1', 'username' => 'nate')) ); $this->Controller->testUrl = null; - $this->Controller->request->addParams(Router::parse($url)); + $this->Auth->request->addParams(Router::parse($url)); array_push($this->Controller->methods, 'view', 'edit', 'index'); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->authorize = 'controller'; + $this->Auth->initialize($this->Controller); + $this->Auth->authorize = 'controller'; - $this->Controller->Auth->loginAction = array( + $this->Auth->loginAction = array( 'controller' => 'AuthTest', 'action' => 'login' ); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->startup($this->Controller); $expected = Router::normalize('/AuthTest/login'); $this->assertEqual($expected, $this->Controller->testUrl); @@ -760,38 +748,35 @@ class AuthTest extends CakeTestCase { $this->Controller->Session->write('Auth', array( 'AuthUser' => array('id'=>'1', 'username' => 'nate') )); - $this->Controller->request->params['action'] = 'login'; - $this->Controller->request->query['url'] = 'auth_test/login'; - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = 'auth_test/login'; - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->loginRedirect = false; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->request->params['action'] = 'login'; + $this->Auth->request->query['url'] = 'auth_test/login'; + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = 'auth_test/login'; + $this->Auth->loginRedirect = false; + $this->Auth->startup($this->Controller); $expected = Router::normalize('/admin'); - $this->assertEqual($expected, $this->Controller->Auth->redirect()); + $this->assertEqual($expected, $this->Auth->redirect()); //Ticket #4750 //named params $this->Controller->Session->delete('Auth'); $url = '/posts/index/year:2008/month:feb'; - $this->Controller->request->addParams(Router::parse($url)); - $this->Controller->request->query['url'] = Router::normalize($url); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->request->addParams(Router::parse($url)); + $this->Auth->request->query['url'] = Router::normalize($url); + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Auth->startup($this->Controller); $expected = Router::normalize('posts/index/year:2008/month:feb'); $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); //passed args $this->Controller->Session->delete('Auth'); $url = '/posts/view/1'; - $this->Controller->request->addParams(Router::parse($url)); - $this->Controller->request->query['url'] = Router::normalize($url); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->request->addParams(Router::parse($url)); + $this->Auth->request->query['url'] = Router::normalize($url); + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Auth->startup($this->Controller); $expected = Router::normalize('posts/view/1'); $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); @@ -804,13 +789,12 @@ class AuthTest extends CakeTestCase { ); $this->Controller->Session->delete('Auth'); $url = '/posts/index/29'; - $this->Controller->request = new CakeRequest($url); - $this->Controller->request->addParams(Router::parse($url)); + $this->Auth->request = $this->Controller->request = new CakeRequest($url); + $this->Auth->request->addParams(Router::parse($url)); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Auth->startup($this->Controller); $expected = Router::normalize('posts/index/29?print=true&refer=menu'); $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); @@ -821,13 +805,12 @@ class AuthTest extends CakeTestCase { ); $this->Controller->Session->delete('Auth'); $url = '/posts/index/29'; - $this->Controller->request = new CakeRequest($url); - $this->Controller->request->addParams(Router::parse($url)); + $this->Auth->request = $this->Controller->request = new CakeRequest($url); + $this->Auth->request->addParams(Router::parse($url)); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Auth->startup($this->Controller); $expected = Router::normalize('posts/index/29?print=true&refer=menu'); $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); $_GET = $_back; @@ -836,13 +819,12 @@ class AuthTest extends CakeTestCase { $_SERVER['HTTP_REFERER'] = 'http://webmail.example.com/view/message'; $this->Controller->Session->delete('Auth'); $url = '/posts/edit/1'; - $this->Controller->request = new CakeRequest($url); - $this->Controller->request->addParams(Router::parse($url)); - $this->Controller->request->query = array('url' => Router::normalize($url)); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->request = $this->Controller->request = new CakeRequest($url); + $this->Auth->request->addParams(Router::parse($url)); + $this->Auth->request->query = array('url' => Router::normalize($url)); + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Auth->startup($this->Controller); $expected = Router::normalize('/posts/edit/1'); $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); @@ -850,13 +832,12 @@ class AuthTest extends CakeTestCase { $_SERVER['HTTP_REFERER'] = 'http://webmail.example.com/view/message'; $this->Controller->Session->delete('Auth'); $url = '/AuthTest/login'; - $this->Controller->request = new CakeRequest($url); - $this->Controller->request->addParams(Router::parse($url)); - $this->Controller->request->query['url'] = Router::normalize($url); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->request = $this->Controller->request = new CakeRequest($url); + $this->Auth->request->addParams(Router::parse($url)); + $this->Auth->request->query['url'] = Router::normalize($url); + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); + $this->Auth->startup($this->Controller); $expected = Router::normalize('/'); $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); @@ -871,9 +852,9 @@ class AuthTest extends CakeTestCase { */ function testNoRedirectOn404() { $this->Controller->Session->delete('Auth'); - $this->Controller->Auth->initialize($this->Controller); - $this->Controller->request->addParams(Router::parse('auth_test/something_totally_wrong')); - $result = $this->Controller->Auth->startup($this->Controller); + $this->Auth->initialize($this->Controller); + $this->Auth->request->addParams(Router::parse('auth_test/something_totally_wrong')); + $result = $this->Auth->startup($this->Controller); $this->assertTrue($result, 'Auth redirected a missing action %s'); } @@ -889,18 +870,17 @@ class AuthTest extends CakeTestCase { Router::reload(); $url = '/admin/auth_test/add'; - $this->Controller->request->addParams(Router::parse($url)); - $this->Controller->request->query['url'] = ltrim($url, '/'); - $this->Controller->request->base = ''; - Router::setRequestInfo($this->Controller->request); - $this->Controller->Auth->initialize($this->Controller); + $this->Auth->request->addParams(Router::parse($url)); + $this->Auth->request->query['url'] = ltrim($url, '/'); + $this->Auth->request->base = ''; + Router::setRequestInfo($this->Auth->request); + $this->Auth->initialize($this->Controller); - $this->Controller->Auth->loginAction = array( + $this->Auth->loginAction = array( 'admin' => true, 'controller' => 'auth_test', 'action' => 'login' ); - $this->Controller->Auth->userModel = 'AuthUser'; - $this->Controller->Auth->startup($this->Controller); + $this->Auth->startup($this->Controller); $this->assertEqual($this->Controller->testUrl, '/admin/auth_test/login'); Configure::write('Routing.prefixes', $prefixes); @@ -941,12 +921,12 @@ class AuthTest extends CakeTestCase { Router::reload(); $url = '/admin/auth_test/login'; - $this->Controller->request->addParams(Router::parse($url)); - $this->Controller->request->query['url'] = ltrim($url, '/'); + $this->Auth->request->addParams(Router::parse($url)); + $this->Auth->request->query['url'] = ltrim($url, '/'); Router::setRequestInfo(array( array( 'pass' => array(), 'action' => 'admin_login', 'plugin' => null, 'controller' => 'auth_test', - 'admin' => true, 'url' => array('url' => $this->Controller->request->query['url']), + 'admin' => true, 'url' => array('url' => $this->Auth->request->query['url']), ), array( 'base' => null, 'here' => $url, @@ -954,12 +934,9 @@ class AuthTest extends CakeTestCase { ) )); - $this->Controller->Auth->initialize($this->Controller); - - $this->Controller->Auth->loginAction = array('admin' => true, 'controller' => 'auth_test', 'action' => 'login'); - $this->Controller->Auth->userModel = 'AuthUser'; - - $this->Controller->Auth->startup($this->Controller); + $this->Auth->initialize($this->Controller); + $this->Auth->loginAction = array('admin' => true, 'controller' => 'auth_test', 'action' => 'login'); + $this->Auth->startup($this->Controller); $this->assertNull($this->Controller->testUrl); @@ -1018,8 +995,8 @@ class AuthTest extends CakeTestCase { function testLogout() { $this->Controller->Session->write('Auth.User.id', '1'); $this->Controller->Session->write('Auth.redirect', '/users/login'); - $this->Controller->Auth->logoutRedirect = '/'; - $result = $this->Controller->Auth->logout(); + $this->Auth->logoutRedirect = '/'; + $result = $this->Auth->logout(); $this->assertEqual($result, '/'); $this->assertNull($this->Controller->Session->read('Auth.AuthUser')); @@ -1033,13 +1010,13 @@ class AuthTest extends CakeTestCase { */ function testMapActionsDelegation() { $this->getMock('BaseAuthorize', array('authorize'), array(), 'MapActionMockAuthorize', false); - $this->Controller->Auth->authorize = array('MapActionMock'); - $mock = $this->Controller->Auth->constructAuthorize(); + $this->Auth->authorize = array('MapActionMock'); + $mock = $this->Auth->constructAuthorize(); $mock[0]->expects($this->once()) ->method('mapActions') ->with(array('create' => array('my_action'))); - $this->Controller->Auth->mapActions(array('create' => array('my_action'))); + $this->Auth->mapActions(array('create' => array('my_action'))); } /** @@ -1052,16 +1029,16 @@ class AuthTest extends CakeTestCase { $request = new CakeRequest('users/login', false); $user = array('username' => 'mark', 'role' => 'admin'); - $this->Controller->Auth->request = $request; - $this->Controller->Auth->authenticate = array('RequestLoginMock'); - $mock = $this->Controller->Auth->constructAuthenticate(); + $this->Auth->request = $request; + $this->Auth->authenticate = array('RequestLoginMock'); + $mock = $this->Auth->constructAuthenticate(); $mock[0]->expects($this->once()) ->method('authenticate') ->with($request) ->will($this->returnValue($user)); - $this->assertTrue($this->Controller->Auth->login()); - $this->assertEquals($user['username'], $this->Controller->Auth->user('username')); + $this->assertTrue($this->Auth->login()); + $this->assertEquals($user['username'], $this->Auth->user('username')); } /** @@ -1070,7 +1047,7 @@ class AuthTest extends CakeTestCase { * @return void */ function testLoginWithUserData() { - $this->assertFalse($this->Controller->Auth->loggedIn()); + $this->assertFalse($this->Auth->loggedIn()); $user = array( 'username' => 'mariano', @@ -1078,9 +1055,9 @@ class AuthTest extends CakeTestCase { 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31' ); - $this->assertTrue($this->Controller->Auth->login($user)); - $this->assertTrue($this->Controller->Auth->loggedIn()); - $this->assertEquals($user['username'], $this->Controller->Auth->user('username')); + $this->assertTrue($this->Auth->login($user)); + $this->assertTrue($this->Auth->loggedIn()); + $this->assertEquals($user['username'], $this->Auth->user('username')); } /** @@ -1089,17 +1066,17 @@ class AuthTest extends CakeTestCase { * @return void */ function testFlashSettings() { - $this->Controller->Auth->Session = $this->getMock('SessionComponent', array(), array(), '', false); - $this->Controller->Auth->Session->expects($this->once()) + $this->Auth->Session = $this->getMock('SessionComponent', array(), array(), '', false); + $this->Auth->Session->expects($this->once()) ->method('setFlash') ->with('Auth failure', 'custom', array(1), 'auth-key'); - $this->Controller->Auth->flash = array( + $this->Auth->flash = array( 'element' => 'custom', 'params' => array(1), 'key' => 'auth-key' ); - $this->Controller->Auth->flash('Auth failure'); + $this->Auth->flash('Auth failure'); } /** @@ -1109,7 +1086,7 @@ class AuthTest extends CakeTestCase { */ function testRedirectSet() { $value = array('controller' => 'users', 'action' => 'home'); - $result = $this->Controller->Auth->redirect($value); + $result = $this->Auth->redirect($value); $this->assertEquals('/users/home', $result); $this->assertEquals($value, $this->Controller->Session->read('Auth.redirect')); } @@ -1120,10 +1097,10 @@ class AuthTest extends CakeTestCase { * @return void */ function testRedirectSessionRead() { - $this->Controller->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); + $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); $this->Controller->Session->write('Auth.redirect', '/users/home'); - $result = $this->Controller->Auth->redirect(); + $result = $this->Auth->redirect(); $this->assertEquals('/users/home', $result); $this->assertFalse($this->Controller->Session->check('Auth.redirect')); } @@ -1135,11 +1112,11 @@ class AuthTest extends CakeTestCase { * @return void */ function testRedirectSessionReadEqualToLoginAction() { - $this->Controller->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); - $this->Controller->Auth->loginRedirect = array('controller' => 'users', 'action' => 'home'); + $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); + $this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'home'); $this->Controller->Session->write('Auth.redirect', array('controller' => 'users', 'action' => 'login')); - $result = $this->Controller->Auth->redirect(); + $result = $this->Auth->redirect(); $this->assertEquals('/users/home', $result); $this->assertFalse($this->Controller->Session->check('Auth.redirect')); } From 060360c787875015d46735fbf797f6c5598d457e Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 29 Jan 2011 20:55:48 -0500 Subject: [PATCH 56/65] Switching Controller->Session => Auth->Session. --- .../libs/controller/components/auth.test.php | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index f2fed0110..fc5788ebc 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -355,8 +355,8 @@ class AuthTest extends CakeTestCase { $_SERVER = $this->_server; $_ENV = $this->_env; - $this->Controller->Session->delete('Auth'); - $this->Controller->Session->delete('Message.auth'); + $this->Auth->Session->delete('Auth'); + $this->Auth->Session->delete('Message.auth'); unset($this->Controller, $this->Auth); } @@ -444,14 +444,14 @@ class AuthTest extends CakeTestCase { $this->Controller->data = array(); $this->Controller->request->addParams(Router::parse('auth_test/login')); $this->Controller->request->query['url'] = 'auth_test/login'; - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $this->Auth->loginRedirect = '/users/dashboard'; $this->Auth->loginAction = 'auth_test/login'; $this->Auth->userModel = 'AuthUser'; $this->Auth->startup($this->Controller); - $redirect = $this->Controller->Session->read('Auth.redirect'); + $redirect = $this->Auth->Session->read('Auth.redirect'); $this->assertNull($redirect); } @@ -464,17 +464,17 @@ class AuthTest extends CakeTestCase { function testAuthorizeFalse() { $this->AuthUser = new AuthUser(); $user = $this->AuthUser->find(); - $this->Controller->Session->write('Auth.User', $user['AuthUser']); + $this->Auth->Session->write('Auth.User', $user['AuthUser']); $this->Controller->Auth->userModel = 'AuthUser'; $this->Controller->Auth->authorize = false; $this->Controller->request->addParams(Router::parse('auth_test/add')); $result = $this->Controller->Auth->startup($this->Controller); $this->assertTrue($result); - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $result = $this->Controller->Auth->startup($this->Controller); $this->assertFalse($result); - $this->assertTrue($this->Controller->Session->check('Message.auth')); + $this->assertTrue($this->Auth->Session->check('Message.auth')); $this->Controller->request->addParams(Router::parse('auth_test/camelCase')); $result = $this->Controller->Auth->startup($this->Controller); @@ -703,7 +703,7 @@ class AuthTest extends CakeTestCase { $_ENV['HTTP_REFERER'] = false; putenv('HTTP_REFERER='); - $this->Controller->Session->write('Auth', array( + $this->Auth->Session->write('Auth', array( 'AuthUser' => array('id' => '1', 'username' => 'nate') )); @@ -718,7 +718,7 @@ class AuthTest extends CakeTestCase { $expected = Router::normalize($this->Auth->loginRedirect); $this->assertEqual($expected, $this->Auth->redirect()); - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); //empty referer no session $_SERVER['HTTP_REFERER'] = false; @@ -726,7 +726,7 @@ class AuthTest extends CakeTestCase { putenv('HTTP_REFERER='); $url = '/posts/view/1'; - $this->Controller->Session->write('Auth', array( + $this->Auth->Session->write('Auth', array( 'AuthUser' => array('id' => '1', 'username' => 'nate')) ); $this->Controller->testUrl = null; @@ -743,9 +743,9 @@ class AuthTest extends CakeTestCase { $expected = Router::normalize('/AuthTest/login'); $this->assertEqual($expected, $this->Controller->testUrl); - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $_SERVER['HTTP_REFERER'] = $_ENV['HTTP_REFERER'] = Router::url('/admin', true); - $this->Controller->Session->write('Auth', array( + $this->Auth->Session->write('Auth', array( 'AuthUser' => array('id'=>'1', 'username' => 'nate') )); $this->Auth->request->params['action'] = 'login'; @@ -759,7 +759,7 @@ class AuthTest extends CakeTestCase { //Ticket #4750 //named params - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $url = '/posts/index/year:2008/month:feb'; $this->Auth->request->addParams(Router::parse($url)); $this->Auth->request->query['url'] = Router::normalize($url); @@ -767,10 +767,10 @@ class AuthTest extends CakeTestCase { $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); $this->Auth->startup($this->Controller); $expected = Router::normalize('posts/index/year:2008/month:feb'); - $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + $this->assertEqual($expected, $this->Auth->Session->read('Auth.redirect')); //passed args - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $url = '/posts/view/1'; $this->Auth->request->addParams(Router::parse($url)); $this->Auth->request->query['url'] = Router::normalize($url); @@ -778,7 +778,7 @@ class AuthTest extends CakeTestCase { $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); $this->Auth->startup($this->Controller); $expected = Router::normalize('posts/view/1'); - $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + $this->assertEqual($expected, $this->Auth->Session->read('Auth.redirect')); // QueryString parameters $_back = $_GET; @@ -787,7 +787,7 @@ class AuthTest extends CakeTestCase { 'print' => 'true', 'refer' => 'menu' ); - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $url = '/posts/index/29'; $this->Auth->request = $this->Controller->request = new CakeRequest($url); $this->Auth->request->addParams(Router::parse($url)); @@ -796,14 +796,14 @@ class AuthTest extends CakeTestCase { $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); $this->Auth->startup($this->Controller); $expected = Router::normalize('posts/index/29?print=true&refer=menu'); - $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + $this->assertEqual($expected, $this->Auth->Session->read('Auth.redirect')); $_GET = array( 'url' => '/posts/index/29', 'print' => 'true', 'refer' => 'menu' ); - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $url = '/posts/index/29'; $this->Auth->request = $this->Controller->request = new CakeRequest($url); $this->Auth->request->addParams(Router::parse($url)); @@ -812,12 +812,12 @@ class AuthTest extends CakeTestCase { $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); $this->Auth->startup($this->Controller); $expected = Router::normalize('posts/index/29?print=true&refer=menu'); - $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + $this->assertEqual($expected, $this->Auth->Session->read('Auth.redirect')); $_GET = $_back; //external authed action $_SERVER['HTTP_REFERER'] = 'http://webmail.example.com/view/message'; - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $url = '/posts/edit/1'; $this->Auth->request = $this->Controller->request = new CakeRequest($url); $this->Auth->request->addParams(Router::parse($url)); @@ -826,11 +826,11 @@ class AuthTest extends CakeTestCase { $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); $this->Auth->startup($this->Controller); $expected = Router::normalize('/posts/edit/1'); - $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + $this->assertEqual($expected, $this->Auth->Session->read('Auth.redirect')); //external direct login link $_SERVER['HTTP_REFERER'] = 'http://webmail.example.com/view/message'; - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $url = '/AuthTest/login'; $this->Auth->request = $this->Controller->request = new CakeRequest($url); $this->Auth->request->addParams(Router::parse($url)); @@ -839,9 +839,9 @@ class AuthTest extends CakeTestCase { $this->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); $this->Auth->startup($this->Controller); $expected = Router::normalize('/'); - $this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect')); + $this->assertEqual($expected, $this->Auth->Session->read('Auth.redirect')); - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); } /** @@ -851,7 +851,7 @@ class AuthTest extends CakeTestCase { * @return void */ function testNoRedirectOn404() { - $this->Controller->Session->delete('Auth'); + $this->Auth->Session->delete('Auth'); $this->Auth->initialize($this->Controller); $this->Auth->request->addParams(Router::parse('auth_test/something_totally_wrong')); $result = $this->Auth->startup($this->Controller); @@ -951,11 +951,11 @@ class AuthTest extends CakeTestCase { */ function testShutDown() { $this->Controller->Auth->initialize($this->Controller, array('_loggedIn' => true)); - $this->Controller->Session->write('Auth.redirect', 'foo'); + $this->Auth->Session->write('Auth.redirect', 'foo'); $this->Controller->Auth->loggedIn(true); $this->Controller->Auth->shutdown($this->Controller); - $this->assertNull($this->Controller->Session->read('Auth.redirect')); + $this->assertNull($this->Auth->Session->read('Auth.redirect')); } /** @@ -993,14 +993,14 @@ class AuthTest extends CakeTestCase { * @return void */ function testLogout() { - $this->Controller->Session->write('Auth.User.id', '1'); - $this->Controller->Session->write('Auth.redirect', '/users/login'); + $this->Auth->Session->write('Auth.User.id', '1'); + $this->Auth->Session->write('Auth.redirect', '/users/login'); $this->Auth->logoutRedirect = '/'; $result = $this->Auth->logout(); $this->assertEqual($result, '/'); - $this->assertNull($this->Controller->Session->read('Auth.AuthUser')); - $this->assertNull($this->Controller->Session->read('Auth.redirect')); + $this->assertNull($this->Auth->Session->read('Auth.AuthUser')); + $this->assertNull($this->Auth->Session->read('Auth.redirect')); } /** @@ -1088,7 +1088,7 @@ class AuthTest extends CakeTestCase { $value = array('controller' => 'users', 'action' => 'home'); $result = $this->Auth->redirect($value); $this->assertEquals('/users/home', $result); - $this->assertEquals($value, $this->Controller->Session->read('Auth.redirect')); + $this->assertEquals($value, $this->Auth->Session->read('Auth.redirect')); } /** @@ -1098,11 +1098,11 @@ class AuthTest extends CakeTestCase { */ function testRedirectSessionRead() { $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); - $this->Controller->Session->write('Auth.redirect', '/users/home'); + $this->Auth->Session->write('Auth.redirect', '/users/home'); $result = $this->Auth->redirect(); $this->assertEquals('/users/home', $result); - $this->assertFalse($this->Controller->Session->check('Auth.redirect')); + $this->assertFalse($this->Auth->Session->check('Auth.redirect')); } /** @@ -1114,10 +1114,10 @@ class AuthTest extends CakeTestCase { function testRedirectSessionReadEqualToLoginAction() { $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); $this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'home'); - $this->Controller->Session->write('Auth.redirect', array('controller' => 'users', 'action' => 'login')); + $this->Auth->Session->write('Auth.redirect', array('controller' => 'users', 'action' => 'login')); $result = $this->Auth->redirect(); $this->assertEquals('/users/home', $result); - $this->assertFalse($this->Controller->Session->check('Auth.redirect')); + $this->assertFalse($this->Auth->Session->check('Auth.redirect')); } } From 59dac2255ec2d4c310e28fddc71d04f85bc6e1d9 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 29 Jan 2011 21:02:51 -0500 Subject: [PATCH 57/65] Expanding test coverage. --- .../libs/controller/components/auth.test.php | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/cake/tests/cases/libs/controller/components/auth.test.php b/cake/tests/cases/libs/controller/components/auth.test.php index fc5788ebc..e7c689ae0 100644 --- a/cake/tests/cases/libs/controller/components/auth.test.php +++ b/cake/tests/cases/libs/controller/components/auth.test.php @@ -500,13 +500,13 @@ class AuthTest extends CakeTestCase { $this->getMock('BaseAuthorize', array('authorize'), array(), 'AuthMockTwoAuthorize', false); $this->getMock('BaseAuthorize', array('authorize'), array(), 'AuthMockThreeAuthorize', false); - $this->Controller->Auth->authorize = array( + $this->Auth->authorize = array( 'AuthMockOne', 'AuthMockTwo', 'AuthMockThree' ); - $mocks = $this->Controller->Auth->constructAuthorize(); - $request = $this->Controller->request; + $mocks = $this->Auth->constructAuthorize(); + $request = $this->Auth->request; $this->assertEquals(3, count($mocks)); $mocks[0]->expects($this->once()) @@ -522,7 +522,29 @@ class AuthTest extends CakeTestCase { $mocks[2]->expects($this->never()) ->method('authorize'); - $this->assertTrue($this->Controller->Auth->isAuthorized(array('User'), $request)); + $this->assertTrue($this->Auth->isAuthorized(array('User'), $request)); + } + +/** + * test that isAuthorized will use the session user if none is given. + * + * @return void + */ + function testIsAuthorizedUsingUserInSession() { + $this->getMock('BaseAuthorize', array('authorize'), array(), 'AuthMockFourAuthorize', false); + $this->Auth->authorize = array('AuthMockFour'); + + $user = array('user' => 'mark'); + $this->Auth->Session->write('Auth.User', $user); + $mocks = $this->Auth->constructAuthorize(); + $request = $this->Controller->request; + + $mocks[0]->expects($this->once()) + ->method('authorize') + ->with($user, $request) + ->will($this->returnValue(true)); + + $this->assertTrue($this->Auth->isAuthorized(null, $request)); } /** @@ -1120,4 +1142,16 @@ class AuthTest extends CakeTestCase { $this->assertEquals('/users/home', $result); $this->assertFalse($this->Auth->Session->check('Auth.redirect')); } + +/** + * test password hashing + * + * @return void + */ + function testPassword() { + $result = $this->Auth->password('password'); + $expected = Security::hash('password', null, true); + $this->assertEquals($expected, $result); + + } } From a5d9a64e1a741b7acb9e622e2d01e070de3fa319 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 30 Jan 2011 15:58:37 -0500 Subject: [PATCH 58/65] Removing loginError, its not used anymore. --- cake/libs/controller/components/auth.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 543012c91..123f7697c 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -202,15 +202,6 @@ class AuthComponent extends Component { */ public $logoutRedirect = 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. @@ -358,7 +349,6 @@ class AuthComponent extends Component { function __setDefaults() { $defaults = array( 'logoutRedirect' => $this->loginAction, - 'loginError' => __('Login failed. Invalid username or password.'), 'authError' => __('You are not authorized to access that location.') ); foreach ($defaults as $key => $value) { From 5834225cc3f0c06e2cbc82bd3c17c5f2de34fb64 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 30 Jan 2011 16:06:01 -0500 Subject: [PATCH 59/65] Making AuthComponent::password() static, as it might be needed in places where an instance of AuthComponent is not handy. --- cake/libs/controller/components/auth.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 123f7697c..e07f89e94 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -640,7 +640,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); } From 9854947d0fd839e7e1bfced48e043583c2379b0c Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 1 Feb 2011 21:24:46 -0500 Subject: [PATCH 60/65] Fixing reversed args. --- cake/libs/controller/components/auth.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index e07f89e94..001bdeef1 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -627,7 +627,7 @@ class AuthComponent extends Component { if (!method_exists($className, 'authenticate')) { throw new CakeException(__('Authentication objects must implement an authenticate method.')); } - $settings = array_merge((array)$settings, $global); + $settings = array_merge($global, (array)$settings); $this->_authenticateObjects[] = new $className($settings); } return $this->_authenticateObjects; From 3875d0ef548fc0c0ee5e5cac8da072f0b17ebe9b Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 3 Feb 2011 23:00:41 -0500 Subject: [PATCH 61/65] Removing a method that is duplicated in AuthComponent. --- .../controller/components/auth/base_authenticate.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/cake/libs/controller/components/auth/base_authenticate.php b/cake/libs/controller/components/auth/base_authenticate.php index 0753640f8..d34789729 100644 --- a/cake/libs/controller/components/auth/base_authenticate.php +++ b/cake/libs/controller/components/auth/base_authenticate.php @@ -49,16 +49,6 @@ abstract class BaseAuthenticate { $this->settings = Set::merge($this->settings, $settings); } -/** - * Hash the supplied password using the configured hashing method. - * - * @param string $password The password to hash. - * @return string Hashed string - */ - public function hash($password) { - return Security::hash($password, null, true); - } - /** * Find a user record using the standard options. * @@ -73,7 +63,7 @@ abstract class BaseAuthenticate { $conditions = array( $model . '.' . $fields['username'] => $username, - $model . '.' . $fields['password'] => $this->hash($password), + $model . '.' . $fields['password'] => AuthComponent::password($password), ); if (!empty($this->settings['scope'])) { $conditions = array_merge($conditions, $this->settings['scope']); From 3629925a40f6085c50324d593cbe52046e16d2ec Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 5 Feb 2011 18:41:00 -0500 Subject: [PATCH 62/65] Refactoring authentication objects so basic and digest authentication can work without cookies. This makes non browser clients able to use Basic and Digest auth. Updating test cases. --- cake/libs/controller/components/auth.php | 25 +++++++++++- .../components/auth/base_authenticate.php | 11 +++++ .../components/auth/basic_authenticate.php | 29 +++++++++----- .../components/auth/digest_authenticate.php | 40 ++++++++++++------- .../auth/basic_authenticate.test.php | 8 ++-- .../auth/digest_authenticate.test.php | 18 ++++----- 6 files changed, 89 insertions(+), 42 deletions(-) diff --git a/cake/libs/controller/components/auth.php b/cake/libs/controller/components/auth.php index 001bdeef1..41b3c5146 100644 --- a/cake/libs/controller/components/auth.php +++ b/cake/libs/controller/components/auth.php @@ -312,7 +312,7 @@ class AuthComponent extends Component { } } } else { - if (!$this->user()) { + if (!$this->_getUser()) { if (!$request->is('ajax')) { $this->flash($this->authError); $this->Session->write('Auth.redirect', Router::reverse($request)); @@ -558,6 +558,29 @@ class AuthComponent extends Component { } } +/** + * 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 diff --git a/cake/libs/controller/components/auth/base_authenticate.php b/cake/libs/controller/components/auth/base_authenticate.php index d34789729..575b7544e 100644 --- a/cake/libs/controller/components/auth/base_authenticate.php +++ b/cake/libs/controller/components/auth/base_authenticate.php @@ -87,4 +87,15 @@ abstract class BaseAuthenticate { * @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; + } } \ No newline at end of file diff --git a/cake/libs/controller/components/auth/basic_authenticate.php b/cake/libs/controller/components/auth/basic_authenticate.php index 08a358463..9bf68d386 100644 --- a/cake/libs/controller/components/auth/basic_authenticate.php +++ b/cake/libs/controller/components/auth/basic_authenticate.php @@ -83,20 +83,10 @@ class BasicAuthenticate extends BaseAuthenticate { * @return mixed Either false on failure, or an array of user data on success. */ public function authenticate(CakeRequest $request, CakeResponse $response) { - $username = env('PHP_AUTH_USER'); - $pass = env('PHP_AUTH_PW'); - - if (empty($username) || empty($pass)) { - $response->header($this->loginHeaders()); - $response->send(); - return false; - } - - $result = $this->_findUser($username, $pass); + $result = $this->getUser($request); if (empty($result)) { $response->header($this->loginHeaders()); - $response->header('Location', Router::reverse($request)); $response->statusCode(401); $response->send(); return false; @@ -104,6 +94,23 @@ class BasicAuthenticate extends BaseAuthenticate { return $result; } +/** + * 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) { + $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 * diff --git a/cake/libs/controller/components/auth/digest_authenticate.php b/cake/libs/controller/components/auth/digest_authenticate.php index 91d8ed270..0e5068246 100644 --- a/cake/libs/controller/components/auth/digest_authenticate.php +++ b/cake/libs/controller/components/auth/digest_authenticate.php @@ -108,28 +108,40 @@ class DigestAuthenticate extends BaseAuthenticate { * @return mixed Either false on failure, or an array of user data on success. */ public function authenticate(CakeRequest $request, CakeResponse $response) { - $digest = $this->_getDigest(); + $user = $this->getUser($request); - if (empty($digest)) { + if (empty($user)) { $response->header($this->loginHeaders()); - $response->send(); - return false; - } - - $result = $this->_findUser($digest['username'], null); - $password = $result[$this->settings['fields']['password']]; - unset($result[$this->settings['fields']['password']]); - - if (empty($result) || $digest['response'] !== $this->generateResponseHash($digest, $password)) { - $response->header($this->loginHeaders()); - $response->header('Location', Router::reverse($request)); $response->statusCode(401); $response->send(); return false; } - return $result; + return $user; } +/** + * 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) { + $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. * diff --git a/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php index 656ca0fbb..b1b2fc2e4 100644 --- a/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/basic_authenticate.test.php @@ -14,6 +14,7 @@ * @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'); @@ -114,6 +115,7 @@ class BasicAuthenticateTest extends CakeTestCase { 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') @@ -196,14 +198,10 @@ class BasicAuthenticateTest extends CakeTestCase { ->with('WWW-Authenticate: Basic realm="localhost"'); $this->response->expects($this->at(1)) - ->method('header') - ->with('Location', Router::reverse($request)); - - $this->response->expects($this->at(2)) ->method('statusCode') ->with(401); - $this->response->expects($this->at(3)) + $this->response->expects($this->at(2)) ->method('send'); $this->assertFalse($this->auth->authenticate($request, $this->response)); diff --git a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php index 40b8ef4c1..1ef4ccc5f 100644 --- a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php @@ -122,14 +122,10 @@ DIGEST; ->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); $this->response->expects($this->at(1)) - ->method('header') - ->with('Location', Router::reverse($request)); - - $this->response->expects($this->at(2)) ->method('statusCode') ->with(401); - $this->response->expects($this->at(3)) + $this->response->expects($this->at(2)) ->method('send'); $this->assertFalse($this->auth->authenticate($request, $this->response)); @@ -149,6 +145,10 @@ DIGEST; ->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); @@ -211,16 +211,12 @@ DIGEST; $this->response->expects($this->at(0)) ->method('header') ->with('WWW-Authenticate: Digest realm="localhost",qop="auth",nonce="123",opaque="123abc"'); - - $this->response->expects($this->at(1)) - ->method('header') - ->with('Location', Router::reverse($request)); - $this->response->expects($this->at(2)) + $this->response->expects($this->at(1)) ->method('statusCode') ->with(401); - $this->response->expects($this->at(3)) + $this->response->expects($this->at(2)) ->method('send'); $this->assertFalse($this->auth->authenticate($request, $this->response)); From e32f419d0a7d50447472e0f85cb0e455795d24a5 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 6 Feb 2011 00:01:52 -0500 Subject: [PATCH 63/65] Fixing comments. --- cake/libs/controller/components/auth/basic_authenticate.php | 3 +-- cake/libs/controller/components/auth/digest_authenticate.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cake/libs/controller/components/auth/basic_authenticate.php b/cake/libs/controller/components/auth/basic_authenticate.php index 9bf68d386..55bf48c40 100644 --- a/cake/libs/controller/components/auth/basic_authenticate.php +++ b/cake/libs/controller/components/auth/basic_authenticate.php @@ -95,8 +95,7 @@ class BasicAuthenticate extends BaseAuthenticate { } /** - * Get a user based on information in the request. Primarily used by stateless authentication - * systems like basic and digest auth. + * 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 diff --git a/cake/libs/controller/components/auth/digest_authenticate.php b/cake/libs/controller/components/auth/digest_authenticate.php index 0e5068246..26272894c 100644 --- a/cake/libs/controller/components/auth/digest_authenticate.php +++ b/cake/libs/controller/components/auth/digest_authenticate.php @@ -120,8 +120,7 @@ class DigestAuthenticate extends BaseAuthenticate { } /** - * Get a user based on information in the request. Primarily used by stateless authentication - * systems like basic and digest auth. + * 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 From fbd798da6c049bf7526a6adb0e013696c9af36ca Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 6 Feb 2011 16:59:47 -0500 Subject: [PATCH 64/65] Reordering parameters to make it easier to remember the order. --- cake/libs/controller/components/auth/digest_authenticate.php | 4 +++- .../controller/components/auth/digest_authenticate.test.php | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cake/libs/controller/components/auth/digest_authenticate.php b/cake/libs/controller/components/auth/digest_authenticate.php index 26272894c..cde347110 100644 --- a/cake/libs/controller/components/auth/digest_authenticate.php +++ b/cake/libs/controller/components/auth/digest_authenticate.php @@ -231,10 +231,12 @@ class DigestAuthenticate extends BaseAuthenticate { /** * 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, $realm, $password) { + public static function password($username, $password, $realm) { return md5($username . ':' . $realm . ':' . $password); } diff --git a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php index 1ef4ccc5f..16b175890 100644 --- a/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php +++ b/cake/tests/cases/libs/controller/components/auth/digest_authenticate.test.php @@ -47,7 +47,7 @@ class DigestAuthenticateTest extends CakeTestCase { 'opaque' => '123abc' )); - $password = DigestAuthenticate::password('mariano', 'localhost', 'cake'); + $password = DigestAuthenticate::password('mariano', 'cake', 'localhost'); ClassRegistry::init('User')->updateAll(array('password' => '"' . $password . '"')); $_SERVER['REQUEST_METHOD'] = 'GET'; @@ -296,7 +296,7 @@ DIGEST; * @return void */ function testPassword() { - $result = DigestAuthenticate::password('mark', 'localhost', 'password'); + $result = DigestAuthenticate::password('mark', 'password', 'localhost'); $expected = md5('mark:localhost:password'); $this->assertEquals($expected, $result); } From 33390070d34b4987e00c23704e18e5fba8c26a2c Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 8 Feb 2011 22:09:57 -0500 Subject: [PATCH 65/65] Removing HTTP authentication from SecurityComponent. There are new Authentication objects to fill this hole now. --- cake/libs/controller/components/security.php | 219 ------------- .../controller/components/security.test.php | 296 ------------------ 2 files changed, 515 deletions(-) diff --git a/cake/libs/controller/components/security.php b/cake/libs/controller/components/security.php index daf0bc8bb..ada4f3eaf 100644 --- a/cake/libs/controller/components/security.php +++ b/cake/libs/controller/components/security.php @@ -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 * diff --git a/cake/tests/cases/libs/controller/components/security.test.php b/cake/tests/cases/libs/controller/components/security.test.php index cb7197392..f3d1240e6 100644 --- a/cake/tests/cases/libs/controller/components/security.test.php +++ b/cake/tests/cases/libs/controller/components/security.test.php @@ -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 = <<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 = << '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 = << '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 = << '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.