Correcting behavior of layering allow/deny calls in AuthComponent, fixes #5595, formatting fixes for RequestHandler

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@7743 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
nate 2008-10-15 02:52:19 +00:00
parent ccb83c341e
commit 1b9357f5e8
3 changed files with 153 additions and 49 deletions

View file

@ -216,6 +216,13 @@ class AuthComponent extends Object {
* @access public * @access public
*/ */
var $params = array(); var $params = array();
/**
* Method list for bound controller
*
* @var array
* @access protected
*/
var $_methods = array();
/** /**
* Initializes AuthComponent for use in the controller * Initializes AuthComponent for use in the controller
* *
@ -227,6 +234,7 @@ class AuthComponent extends Object {
$this->params = $controller->params; $this->params = $controller->params;
$crud = array('create', 'read', 'update', 'delete'); $crud = array('create', 'read', 'update', 'delete');
$this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud)); $this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
$this->_methods = get_class_methods($controller);
$admin = Configure::read('Routing.admin'); $admin = Configure::read('Routing.admin');
if (!empty($admin)) { if (!empty($admin)) {
@ -256,17 +264,20 @@ class AuthComponent extends Object {
* @access public * @access public
*/ */
function startup(&$controller) { function startup(&$controller) {
if (strtolower($controller->name) == 'app' || (strtolower($controller->name) == 'tests' && Configure::read() > 0)) { $isBaseOrTests = (
return; strtolower($controller->name) == 'app' ||
(strtolower($controller->name) == 'tests' && Configure::read() > 0)
);
if ($isBaseOrTests) {
return true;
} }
if (!$this->__setDefaults()) { if (!$this->__setDefaults()) {
return false; return false;
} }
$this->data = $controller->data = $this->hashPasswords($controller->data); $this->data = $controller->data = $this->hashPasswords($controller->data);
$url = ''; $url = '';
if (is_array($this->loginAction)) { if (is_array($this->loginAction)) {
$params = $controller->params; $params = $controller->params;
$keys = array('pass', 'named', 'controller', 'action', 'plugin'); $keys = array('pass', 'named', 'controller', 'action', 'plugin');
@ -294,7 +305,7 @@ class AuthComponent extends Object {
); );
if ($loginAction != $url && $isAllowed) { if ($loginAction != $url && $isAllowed) {
return false; return true;
} }
if ($loginAction == $url) { if ($loginAction == $url) {
@ -354,14 +365,25 @@ class AuthComponent extends Object {
if (isset($controller->Acl)) { if (isset($controller->Acl)) {
$this->Acl =& $controller->Acl; $this->Acl =& $controller->Acl;
} else { } else {
trigger_error(__('Could not find AclComponent. Please include Acl in Controller::$components.', true), E_USER_WARNING); $err = 'Could not find AclComponent. Please include Acl in ';
$err .= 'Controller::$components.';
trigger_error(__($err, true), E_USER_WARNING);
} }
break; break;
case 'model': case 'model':
if (!isset($object)) { if (!isset($object)) {
if (isset($controller->{$controller->modelClass}) && is_object($controller->{$controller->modelClass})) { $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; $object = $controller->modelClass;
} elseif (!empty($controller->uses) && isset($controller->{$controller->uses[0]}) && is_object($controller->{$controller->uses[0]})) { } elseif ($isUses) {
$object = $controller->uses[0]; $object = $controller->uses[0];
} }
} }
@ -408,15 +430,19 @@ class AuthComponent extends Object {
return true; return true;
} }
/** /**
* Determines whether the given user is authorized to perform an action. The type of authorization * Determines whether the given user is authorized to perform an action. The type of
* used is based on the value of AuthComponent::$authorize or the passed $type param. * authorization used is based on the value of AuthComponent::$authorize or the
* passed $type param.
* *
* Types: * Types:
* 'controller' will validate against Controller::isAuthorized() if controller instance is passed in $object * 'controller' will validate against Controller::isAuthorized() if controller instance is
* passed in $object
* 'actions' will validate Controller::action against an AclComponent::check() * 'actions' will validate Controller::action against an AclComponent::check()
* 'crud' will validate mapActions against an AclComponent::check() * 'crud' will validate mapActions against an AclComponent::check()
* array('model'=> 'name'); will validate mapActions against model $name::isAuthorize(user, controller, mapAction) * array('model'=> 'name'); will validate mapActions against model
* 'object' will validate Controller::action against object::isAuthorized(user, controller, action) * $name::isAuthorize(user, controller, mapAction)
* 'object' will validate Controller::action against
* object::isAuthorized(user, controller, action)
* *
* @param string $type Type of authorization * @param string $type Type of authorization
* @param mixed $object object, model object, or model name * @param mixed $object object, model object, or model name
@ -448,9 +474,18 @@ class AuthComponent extends Object {
case 'crud': case 'crud':
$this->mapActions(); $this->mapActions();
if (!isset($this->actionMap[$this->params['action']])) { if (!isset($this->actionMap[$this->params['action']])) {
trigger_error(sprintf(__('Auth::startup() - Attempted access of un-mapped action "%1$s" in controller "%2$s"', true), $this->params['action'], $this->params['controller']), E_USER_WARNING); $err = 'Auth::startup() - Attempted access of un-mapped action "%1$s" in';
$err .= ' controller "%2$s"';
trigger_error(
sprintf(__($err, true), $this->params['action'], $this->params['controller']),
E_USER_WARNING
);
} else { } else {
$valid = $this->Acl->check($user, $this->action(':controller'), $this->actionMap[$this->params['action']]); $valid = $this->Acl->check(
$user,
$this->action(':controller'),
$this->actionMap[$this->params['action']]
);
} }
break; break;
case 'model': case 'model':
@ -519,8 +554,8 @@ class AuthComponent extends Object {
*/ */
function allow() { function allow() {
$args = func_get_args(); $args = func_get_args();
if (empty($args)) { if (empty($args) || $args == array('*')) {
$this->allowedActions = array('*'); $this->allowedActions = $this->_methods;
} else { } else {
if (isset($args[0]) && is_array($args[0])) { if (isset($args[0]) && is_array($args[0])) {
$args = $args[0]; $args = $args[0];

View file

@ -3,9 +3,9 @@
/** /**
* Request object for handling alternative HTTP requests * Request object for handling alternative HTTP requests
* *
* Alternative HTTP requests can come from wireless units like mobile phones, palmtop computers, and the like. * Alternative HTTP requests can come from wireless units like mobile phones, palmtop computers,
* These units have no use for Ajax requests, and this Component can tell how Cake should respond to the different * and the like. These units have no use for Ajax requests, and this Component can tell how Cake
* needs of a handheld computer and a desktop machine. * should respond to the different needs of a handheld computer and a desktop machine.
* *
* CakePHP(tm) : Rapid Development Framework <http://www.cakephp.org/> * CakePHP(tm) : Rapid Development Framework <http://www.cakephp.org/>
* Copyright 2005-2008, Cake Software Foundation, Inc. * Copyright 2005-2008, Cake Software Foundation, Inc.
@ -94,7 +94,11 @@ class RequestHandlerComponent extends Object {
'rss' => 'application/rss+xml', 'rss' => 'application/rss+xml',
'atom' => 'application/atom+xml', 'atom' => 'application/atom+xml',
'amf' => 'application/x-amf', 'amf' => 'application/x-amf',
'wap' => array('text/vnd.wap.wml', 'text/vnd.wap.wmlscript', 'image/vnd.wap.wbmp'), 'wap' => array(
'text/vnd.wap.wml',
'text/vnd.wap.wmlscript',
'image/vnd.wap.wbmp'
),
'wml' => 'text/vnd.wap.wml', 'wml' => 'text/vnd.wap.wml',
'wmlscript' => 'text/vnd.wap.wmlscript', 'wmlscript' => 'text/vnd.wap.wmlscript',
'wbmp' => 'image/vnd.wap.wbmp', 'wbmp' => 'image/vnd.wap.wbmp',
@ -188,10 +192,15 @@ class RequestHandlerComponent extends Object {
if (!$this->enabled) { if (!$this->enabled) {
return; return;
} }
$this->__initializeTypes(); $this->__initializeTypes();
$controller->params['isAjax'] = $this->isAjax(); $controller->params['isAjax'] = $this->isAjax();
$isRecognized = (
!in_array($this->ext, array('html', 'htm')) &&
in_array($this->ext, array_keys($this->__requestContent))
);
if (!empty($this->ext) && !in_array($this->ext, array('html', 'htm')) && in_array($this->ext, array_keys($this->__requestContent))) { if (!empty($this->ext) && $isRecognized) {
$this->renderAs($controller, $this->ext); $this->renderAs($controller, $this->ext);
} elseif ($this->isAjax()) { } elseif ($this->isAjax()) {
$this->renderAs($controller, 'ajax'); $this->renderAs($controller, 'ajax');
@ -202,6 +211,7 @@ class RequestHandlerComponent extends Object {
App::import('Core', 'Xml'); App::import('Core', 'Xml');
} }
$xml = new Xml(trim(file_get_contents('php://input'))); $xml = new Xml(trim(file_get_contents('php://input')));
if (is_object($xml->child('data')) && count($xml->children) == 1) { if (is_object($xml->child('data')) && count($xml->children) == 1) {
$controller->data = $xml->child('data'); $controller->data = $xml->child('data');
} else { } else {
@ -572,7 +582,8 @@ class RequestHandlerComponent extends Object {
if (empty($this->__renderType)) { if (empty($this->__renderType)) {
$controller->viewPath .= '/' . $type; $controller->viewPath .= '/' . $type;
} else { } else {
$controller->viewPath = preg_replace("/(?:\/{$type})$/", '/' . $type, $controller->viewPath); $remove = preg_replace("/(?:\/{$type})$/", '/' . $type, $controller->viewPath);
$controller->viewPath = $remove;
} }
$this->__renderType = $type; $this->__renderType = $type;
$controller->layoutPath = $type; $controller->layoutPath = $type;
@ -582,18 +593,23 @@ class RequestHandlerComponent extends Object {
} }
$helper = ucfirst($type); $helper = ucfirst($type);
if (!in_array($helper, $controller->helpers) && !array_key_exists($helper, $controller->helpers)) { $isAdded = (
in_array($helper, $controller->helpers) ||
array_key_exists($helper, $controller->helpers)
);
if (!$isAdded) {
if (App::import('Helper', $helper)) { if (App::import('Helper', $helper)) {
$controller->helpers[] = $helper; $controller->helpers[] = $helper;
} }
} }
} }
/** /**
* Sets the response header based on type map index name. If DEBUG is greater * Sets the response header based on type map index name. If DEBUG is greater than 2, the header
* than 2, the header is not set. * is not set.
* *
* @param mixed $type Friendly type name, i.e. 'html' or 'xml', or a full * @param mixed $type Friendly type name, i.e. 'html' or 'xml', or a full content-type,
* content-type, like 'application/x-shockwave'. * like 'application/x-shockwave'.
* @param array $options If $type is a friendly type name that is associated with * @param array $options If $type is a friendly type name that is associated with
* more than one type of content, $index is used to select * more than one type of content, $index is used to select
* which content-type to use. * which content-type to use.
@ -611,7 +627,8 @@ class RequestHandlerComponent extends Object {
if (!array_key_exists($type, $this->__requestContent) && strpos($type, '/') === false) { if (!array_key_exists($type, $this->__requestContent) && strpos($type, '/') === false) {
return false; return false;
} }
$options = array_merge(array('index' => 0, 'charset' => null, 'attachment' => false), $options); $defaults = array('index' => 0, 'charset' => null, 'attachment' => false);
$options = array_merge($defaults, $options);
if (strpos($type, '/') === false && isset($this->__requestContent[$type])) { if (strpos($type, '/') === false && isset($this->__requestContent[$type])) {
$cType = null; $cType = null;
@ -624,6 +641,7 @@ class RequestHandlerComponent extends Object {
} else { } else {
return false; return false;
} }
if (is_array($cType)) { if (is_array($cType)) {
if ($this->prefers($cType)) { if ($this->prefers($cType)) {
$cType = $this->prefers($cType); $cType = $this->prefers($cType);
@ -642,7 +660,7 @@ class RequestHandlerComponent extends Object {
$header .= '; charset=' . $options['charset']; $header .= '; charset=' . $options['charset'];
} }
if (!empty($options['attachment'])) { if (!empty($options['attachment'])) {
header('Content-Disposition: attachment; filename="' . $options['attachment'] . '"'); header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
} }
if (Configure::read() < 2 && !defined('CAKEPHP_SHELL')) { if (Configure::read() < 2 && !defined('CAKEPHP_SHELL')) {
@header($header); @header($header);
@ -650,7 +668,6 @@ class RequestHandlerComponent extends Object {
$this->__responseTypeSet = $cType; $this->__responseTypeSet = $cType;
return true; return true;
} }
return false; return false;
} }
/** /**

View file

@ -294,6 +294,20 @@ class AuthTestController extends Controller {
} }
return true; 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);
}
}
} }
/** /**
* AjaxAuthController class * AjaxAuthController class
@ -556,7 +570,6 @@ class AuthTest extends CakeTestCase {
$this->assertTrue($this->Controller->Session->check('Message.auth')); $this->assertTrue($this->Controller->Session->check('Message.auth'));
$result = $this->Controller->Auth->isAuthorized(); $result = $this->Controller->Auth->isAuthorized();
$this->assertFalse($result); $this->assertFalse($result);
} }
/** /**
@ -576,19 +589,21 @@ class AuthTest extends CakeTestCase {
$this->Controller->Acl->name = 'DbAclTest'; $this->Controller->Acl->name = 'DbAclTest';
$this->Controller->Acl->Aro->id = null; $this->Controller->Acl->Aro->id = null;
$this->Controller->Acl->Aro->create(array('alias'=>'Roles')); $this->Controller->Acl->Aro->create(array('alias' => 'Roles'));
$result = $this->Controller->Acl->Aro->save(); $result = $this->Controller->Acl->Aro->save();
$this->assertTrue($result); $this->assertTrue($result);
$parent = $this->Controller->Acl->Aro->id; $parent = $this->Controller->Acl->Aro->id;
$this->Controller->Acl->Aro->create(array('parent_id'=> $parent, 'alias'=>'Admin')); $this->Controller->Acl->Aro->create(array('parent_id' => $parent, 'alias' => 'Admin'));
$result = $this->Controller->Acl->Aro->save(); $result = $this->Controller->Acl->Aro->save();
$this->assertTrue($result); $this->assertTrue($result);
$parent = $this->Controller->Acl->Aro->id; $parent = $this->Controller->Acl->Aro->id;
$this->Controller->Acl->Aro->create(array('model' => 'AuthUser', 'parent_id' => $parent, 'foreign_key' => 1, 'alias'=> 'mariano')); $this->Controller->Acl->Aro->create(array(
'model' => 'AuthUser', 'parent_id' => $parent, 'foreign_key' => 1, 'alias'=> 'mariano'
));
$result = $this->Controller->Acl->Aro->save(); $result = $this->Controller->Acl->Aro->save();
$this->assertTrue($result); $this->assertTrue($result);
@ -612,15 +627,30 @@ class AuthTest extends CakeTestCase {
$this->Controller->Auth->actionPath = 'Root/'; $this->Controller->Auth->actionPath = 'Root/';
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
$this->assertTrue($this->Controller->Auth->isAuthorized()); $this->assertTrue($this->Controller->Auth->isAuthorized());
$this->Controller->Session->del('Auth'); $this->Controller->Session->del('Auth');
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
$this->assertTrue($this->Controller->Session->check('Message.auth')); $this->assertTrue($this->Controller->Session->check('Message.auth'));
} }
/**
* Tests that deny always takes precedence over allow
*
* @access public
* @return void
*/
function testAllowDenyAll() {
$this->Controller->Auth->initialize($this->Controller);
$this->Controller->Auth->allow('*');
$this->Controller->Auth->deny('add');
$this->Controller->action = 'delete';
$this->assertTrue($this->Controller->Auth->startup($this->Controller));
$this->Controller->action = 'add';
$this->assertFalse($this->Controller->Auth->startup($this->Controller));
}
/** /**
* testLoginRedirect method * testLoginRedirect method
* *
@ -636,13 +666,17 @@ class AuthTest extends CakeTestCase {
$_SERVER['HTTP_REFERER'] = false; $_SERVER['HTTP_REFERER'] = false;
$this->Controller->Session->write('Auth', array('AuthUser' => array('id'=>'1', 'username'=>'nate'))); $this->Controller->Session->write('Auth', array(
'AuthUser' => array('id' => '1', 'username' => 'nate')
));
$this->Controller->params['url']['url'] = 'users/login'; $this->Controller->params['url']['url'] = 'users/login';
$this->Controller->Auth->initialize($this->Controller); $this->Controller->Auth->initialize($this->Controller);
$this->Controller->Auth->userModel = 'AuthUser'; $this->Controller->Auth->userModel = 'AuthUser';
$this->Controller->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'display', 'welcome'); $this->Controller->Auth->loginRedirect = array(
'controller' => 'pages', 'action' => 'display', 'welcome'
);
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
$expected = Router::normalize($this->Controller->Auth->loginRedirect); $expected = Router::normalize($this->Controller->Auth->loginRedirect);
$this->assertEqual($expected, $this->Controller->Auth->redirect()); $this->assertEqual($expected, $this->Controller->Auth->redirect());
@ -666,14 +700,18 @@ class AuthTest extends CakeTestCase {
putenv('HTTP_REFERER='); putenv('HTTP_REFERER=');
$url = '/posts/view/1'; $url = '/posts/view/1';
$this->Controller->Session->write('Auth', array('AuthUser' => array('id'=>'1', 'username'=>'nate'))); $this->Controller->Session->write('Auth', array(
'AuthUser' => array('id' => '1', 'username' => 'nate'))
);
$this->Controller->testUrl = null; $this->Controller->testUrl = null;
$this->Controller->params = Router::parse($url); $this->Controller->params = Router::parse($url);
$this->Controller->Auth->initialize($this->Controller); $this->Controller->Auth->initialize($this->Controller);
$this->Controller->Auth->authorize = 'controller'; $this->Controller->Auth->authorize = 'controller';
$this->Controller->params['testControllerAuth'] = true; $this->Controller->params['testControllerAuth'] = true;
$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login'); $this->Controller->Auth->loginAction = array(
'controller' => 'AuthTest', 'action' => 'login'
);
$this->Controller->Auth->userModel = 'AuthUser'; $this->Controller->Auth->userModel = 'AuthUser';
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);
$expected = Router::normalize('/'); $expected = Router::normalize('/');
@ -683,8 +721,9 @@ class AuthTest extends CakeTestCase {
$this->Controller->Session->del('Auth'); $this->Controller->Session->del('Auth');
$_SERVER['HTTP_REFERER'] = '/admin/'; $_SERVER['HTTP_REFERER'] = '/admin/';
$this->Controller->Session->write('Auth', array('AuthUser' => array('id'=>'1', 'username'=>'nate'))); $this->Controller->Session->write('Auth', array(
'AuthUser' => array('id'=>'1', 'username'=>'nate'))
);
$this->Controller->params['url']['url'] = 'auth_test/login'; $this->Controller->params['url']['url'] = 'auth_test/login';
$this->Controller->Auth->initialize($this->Controller); $this->Controller->Auth->initialize($this->Controller);
$this->Controller->Auth->loginAction = 'auth_test/login'; $this->Controller->Auth->loginAction = 'auth_test/login';
@ -834,14 +873,24 @@ class AuthTest extends CakeTestCase {
*/ */
function testCustomRoute() { function testCustomRoute() {
Router::reload(); Router::reload();
Router::connect('/:lang/:controller/:action/*', array('lang' => null), array('lang' => '[a-z]{2,3}')); Router::connect(
'/:lang/:controller/:action/*',
array('lang' => null),
array('lang' => '[a-z]{2,3}')
);
$url = '/en/users/login'; $url = '/en/users/login';
$this->Controller->params = Router::parse($url); $this->Controller->params = Router::parse($url);
Router::setRequestInfo(array($this->Controller->passedArgs, array('base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array()))); Router::setRequestInfo(array($this->Controller->passedArgs, array(
'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(),
'argSeparator' => ':', 'namedArgs' => array()
)));
$this->AuthUser =& new AuthUser(); $this->AuthUser =& new AuthUser();
$user = array('id' => 1, 'username' => 'felix', 'password' => Security::hash(Configure::read('Security.salt') . 'cake')); $user = array(
'id' => 1, 'username' => 'felix',
'password' => Security::hash(Configure::read('Security.salt') . 'cake'
));
$user = $this->AuthUser->save($user, false); $user = $this->AuthUser->save($user, false);
$this->Controller->data['AuthUser'] = array('username' => 'felix', 'password' => 'cake'); $this->Controller->data['AuthUser'] = array('username' => 'felix', 'password' => 'cake');
@ -871,8 +920,9 @@ class AuthTest extends CakeTestCase {
$this->Controller->params['url']['url'] = ltrim($url, '/'); $this->Controller->params['url']['url'] = ltrim($url, '/');
Router::setRequestInfo(array( Router::setRequestInfo(array(
array( array(
'pass' => array(), 'action' => 'index', 'plugin' => null, 'controller' => 'something', 'pass' => array(), 'action' => 'index', 'plugin' => null,
'admin' => true, 'url' => array('url' => $this->Controller->params['url']['url']), 'controller' => 'something', 'admin' => true,
'url' => array('url' => $this->Controller->params['url']['url'])
), ),
array( array(
'base' => null, 'here' => $url, 'base' => null, 'here' => $url,
@ -881,7 +931,9 @@ class AuthTest extends CakeTestCase {
)); ));
$this->Controller->Auth->initialize($this->Controller); $this->Controller->Auth->initialize($this->Controller);
$this->Controller->Auth->loginAction = array('admin' => true, 'controller' => 'auth_test', 'action' => 'login'); $this->Controller->Auth->loginAction = array(
'admin' => true, 'controller' => 'auth_test', 'action' => 'login'
);
$this->Controller->Auth->userModel = 'AuthUser'; $this->Controller->Auth->userModel = 'AuthUser';
$this->Controller->Auth->startup($this->Controller); $this->Controller->Auth->startup($this->Controller);