Adding GET/PUT/DELETE method checks to Security component, refactoring adding tests, closes #4231. Thanks joelmoss.

git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@6703 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
nate 2008-04-19 19:25:49 +00:00
parent d412d7094f
commit 296e8989ba
2 changed files with 230 additions and 99 deletions

View file

@ -50,6 +50,30 @@ class SecurityComponent extends Object {
* @see SecurityComponent::requirePost()
*/
var $requirePost = array();
/**
* List of controller actions for which a GET request is required
*
* @var array
* @access public
* @see SecurityComponent::requireGet()
*/
var $requireGet = array();
/**
* List of controller actions for which a PUT request is required
*
* @var array
* @access public
* @see SecurityComponent::requirePut()
*/
var $requirePut = array();
/**
* List of controller actions for which a DELETE request is required
*
* @var array
* @access public
* @see SecurityComponent::requireDelete()
*/
var $requireDelete = array();
/**
* List of actions that require an SSL-secured connection
*
@ -137,7 +161,7 @@ class SecurityComponent extends Object {
*/
function startup(&$controller) {
$this->__action = strtolower($controller->action);
$this->__postRequired($controller);
$this->__methodsRequired($controller);
$this->__secureRequired($controller);
$this->__authRequired($controller);
$this->__loginRequired($controller);
@ -154,10 +178,35 @@ class SecurityComponent extends Object {
* @access public
*/
function requirePost() {
$this->requirePost = func_get_args();
if (empty($this->requirePost)) {
$this->requirePost = array('*');
$args = func_get_args();
$this->__requireMethod('Post', $args);
}
/**
* Sets the actions that require a GET request, or empty for all actions
*
* @access public
*/
function requireGet() {
$args = func_get_args();
$this->__requireMethod('Get', $args);
}
/**
* Sets the actions that require a PUT request, or empty for all actions
*
* @access public
*/
function requirePut() {
$args = func_get_args();
$this->__requireMethod('Put', $args);
}
/**
* Sets the actions that require a DELETE request, or empty for all actions
*
* @access public
*/
function requireDelete() {
$args = func_get_args();
$this->__requireMethod('Delete', $args);
}
/**
* Sets the actions that require a request that is SSL-secured, or empty for all actions
@ -165,10 +214,8 @@ class SecurityComponent extends Object {
* @access public
*/
function requireSecure() {
$this->requireSecure = func_get_args();
if (empty($this->requireSecure)) {
$this->requireSecure = array('*');
}
$args = func_get_args();
$this->__requireMethod('Secure', $args);
}
/**
* Sets the actions that require an authenticated request, or empty for all actions
@ -176,10 +223,8 @@ class SecurityComponent extends Object {
* @access public
*/
function requireAuth() {
$this->requireAuth = func_get_args();
if (empty($this->requireAuth)) {
$this->requireAuth = array('*');
}
$args = func_get_args();
$this->__requireMethod('Auth', $args);
}
/**
* Sets the actions that require an HTTP-authenticated request, or empty for all actions
@ -190,18 +235,14 @@ class SecurityComponent extends Object {
$args = func_get_args();
$base = $this->loginOptions;
foreach ($args as $arg) {
foreach ($args as $i => $arg) {
if (is_array($arg)) {
$this->loginOptions = $arg;
} else {
$this->requireLogin[] = $arg;
unset($args[$i]);
}
}
$this->loginOptions = array_merge($base, $this->loginOptions);
if (empty($this->requireLogin)) {
$this->requireLogin = array('*');
}
$this->__requireMethod('Login', $args);
if (isset($this->loginOptions['users'])) {
$this->loginUsers =& $this->loginOptions['users'];
@ -332,24 +373,37 @@ class SecurityComponent extends Object {
}
}
/**
* Check if post is required
* Sets the actions that require a $method HTTP request, or empty for all actions
*
* @param object $controller Instantiating controller
* @return bool true if post is requred
* @param string $method The HTTP method to assign controller actions to
* @param array $actions Controller actions to set the required HTTP method to.
* @access private
*/
function __postRequired(&$controller) {
if (is_array($this->requirePost) && !empty($this->requirePost)) {
$requirePost = array_map('strtolower', $this->requirePost);
function __requireMethod($method, $actions = array()) {
$this->{'require' . $method} = ife(empty($actions), array('*'), $actions);
}
/**
* Check if HTTP methods are required
*
* @param object $controller Instantiating controller
* @return bool true if $method is required
* @access private
*/
function __methodsRequired(&$controller) {
foreach (array('Post', 'Get', 'Put', 'Delete') as $method) {
$property = 'require' . $method;
if (is_array($this->$property) && !empty($this->$property)) {
$require = array_map('strtolower', $this->$property);
if (in_array($this->__action, $requirePost) || $this->requirePost == array('*')) {
if (!$this->RequestHandler->isPost()) {
if (!$this->blackHole($controller, 'post')) {
if (in_array($this->__action, $require) || $this->$property == array('*')) {
if (!$this->RequestHandler->{'is' . $method}()) {
if (!$this->blackHole($controller, strtolower($method))) {
return null;
}
}
}
}
}
return true;
}
/**

View file

@ -38,6 +38,12 @@ class SecurityTestController extends Controller {
var $name = 'SecurityTest';
var $components = array('Security');
var $failed = false;
function fail() {
$this->failed = true;
}
function redirect($option, $code, $exit) {
return $code;
}
@ -53,9 +59,8 @@ class SecurityComponentTest extends CakeTestCase {
function setUp() {
$this->Controller =& new SecurityTestController();
restore_error_handler();
@$this->Controller->_initComponents();
set_error_handler('simpleTestErrorHandler');
$this->Controller->_initComponents();
$this->Controller->Security->blackHoleCallback = 'fail';
}
function testStartup() {
@ -65,44 +70,117 @@ class SecurityComponentTest extends CakeTestCase {
$this->assertTrue($this->Controller->Session->check('_Token'));
}
function testRequirePost()
{
$this->Controller->action = 'posted';
$this->Controller->Security->startup($this->Controller);
$this->Controller->Security->requirePost('posted');
$this->assertNull($this->Controller->Security->__methodsRequired($this->Controller));
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->assertTrue($this->Controller->Security->__methodsRequired($this->Controller));
}
function testRequireGet()
{
$this->Controller->action = 'getted';
$this->Controller->Security->startup($this->Controller);
$this->Controller->Security->requireGet('getted');
$this->assertNull($this->Controller->Security->__methodsRequired($this->Controller));
function testRequirePostFail() {
$_SERVER['REQUEST_METHOD'] = 'GET';
$this->assertTrue($this->Controller->Security->__methodsRequired($this->Controller));
$this->Controller->action = 'posted';
$this->Controller->Security->requirePost('posted');
$this->Controller->Security->startup($this->Controller);
$this->assertTrue($this->Controller->failed);
}
function testRequirePut()
{
function testRequirePostSucceed() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->action = 'posted';
$this->Controller->Security->requirePost('posted');
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
function testRequirePostSucceedWrongMethod() {
$_SERVER['REQUEST_METHOD'] = 'GET';
$this->Controller->action = 'getted';
$this->Controller->Security->requirePost('posted');
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
function testRequireGetFail() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->action = 'getted';
$this->Controller->Security->requireGet('getted');
$this->Controller->Security->startup($this->Controller);
$this->assertTrue($this->Controller->failed);
}
function testRequireGetSucceed() {
$_SERVER['REQUEST_METHOD'] = 'GET';
$this->Controller->action = 'getted';
$this->Controller->Security->requireGet('getted');
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
function testRequireGetSucceedWrongMethod() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->action = 'posted';
$this->Controller->Security->requireGet('getted');
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
function testRequirePutFail() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->action = 'putted';
$this->Controller->Security->startup($this->Controller);
$this->Controller->Security->requirePut('putted');
$this->assertNull($this->Controller->Security->__methodsRequired($this->Controller));
$_SERVER['REQUEST_METHOD'] = 'PUT';
$this->assertTrue($this->Controller->Security->__methodsRequired($this->Controller));
$this->Controller->Security->startup($this->Controller);
$this->assertTrue($this->Controller->failed);
}
function testRequireDelete()
{
$this->Controller->action = 'deleted';
function testRequirePutSucceed() {
$_SERVER['REQUEST_METHOD'] = 'PUT';
$this->Controller->action = 'putted';
$this->Controller->Security->requirePut('putted');
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
function testRequirePutSucceedWrongMethod() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->action = 'posted';
$this->Controller->Security->requirePut('putted');
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
function testRequireDeleteFail() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->action = 'deleted';
$this->Controller->Security->requireDelete('deleted');
$this->assertNull($this->Controller->Security->__methodsRequired($this->Controller));
$this->Controller->Security->startup($this->Controller);
$this->assertTrue($this->Controller->failed);
}
function testRequireDeleteSucceed() {
$_SERVER['REQUEST_METHOD'] = 'DELETE';
$this->assertTrue($this->Controller->Security->__methodsRequired($this->Controller));
$this->Controller->action = 'deleted';
$this->Controller->Security->requireDelete('deleted');
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
function testRequireDeleteSucceedWrongMethod() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->action = 'posted';
$this->Controller->Security->requireDelete('deleted');
$this->Controller->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}
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'));
}
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'));
}
function testValidatePostNoModel() {
@ -111,18 +189,14 @@ class SecurityComponentTest extends CakeTestCase {
$data['anything'] = 'some_data';
$data['__Token']['key'] = $key;
$fields = array('anything',
'__Token' => array('key' => $key));
$fields = $this->__sortFields($fields);
$fields = $this->__sortFields(array('anything', '__Token' => array('key' => $key)));
$fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt')));
$data['__Token']['fields'] = $fields;
$this->Controller->data = $data;
$result = $this->Controller->Security->__validatePost($this->Controller);
$this->assertTrue($result);
$this->assertTrue($this->Controller->data == $data);
$this->assertEqual($this->Controller->data, $data);
}
function testValidatePostSimple() {
@ -133,9 +207,7 @@ class SecurityComponentTest extends CakeTestCase {
$data['Model']['password'] = '';
$data['__Token']['key'] = $key;
$fields = array('Model' => array('username','password'),
'__Token' => array('key' => $key));
$fields = array('Model' => array('username','password'), '__Token' => array('key' => $key));
$fields = $this->__sortFields($fields);
$fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt')));
@ -143,11 +215,10 @@ class SecurityComponentTest extends CakeTestCase {
$this->Controller->data = $data;
$result = $this->Controller->Security->__validatePost($this->Controller);
$this->assertTrue($result);
$this->assertTrue($this->Controller->data == $data);
$this->assertEqual($this->Controller->data, $data);
}
function testValidatePostCheckbox() {
$this->Controller->Security->startup($this->Controller);
$key = $this->Controller->params['_Token']['key'];
@ -156,10 +227,11 @@ class SecurityComponentTest extends CakeTestCase {
$data['_Model']['valid'] = '0';
$data['__Token']['key'] = $key;
$fields = array('Model' => array('username', 'password', 'valid'),
$fields = array(
'Model' => array('username', 'password', 'valid'),
'_Model' => array('valid' => '0'),
'__Token' => array('key' => $key));
'__Token' => array('key' => $key)
);
$fields = $this->__sortFields($fields);
$fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt')));
@ -171,7 +243,7 @@ class SecurityComponentTest extends CakeTestCase {
unset($data['_Model']);
$data['Model']['valid'] = '0';
$this->assertTrue($this->Controller->data == $data);
$this->assertEqual($this->Controller->data, $data);
}
function testValidatePostHidden() {
@ -183,10 +255,11 @@ class SecurityComponentTest extends CakeTestCase {
$data['_Model']['hidden'] = '0';
$data['__Token']['key'] = $key;
$fields = array('Model' => array('username', 'password', 'hidden'),
$fields = array(
'Model' => array('username', 'password', 'hidden'),
'_Model' => array('hidden' => '0'),
'__Token' => array('key' => $key));
'__Token' => array('key' => $key)
);
$fields = $this->__sortFields($fields);
$fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt')));
@ -212,17 +285,17 @@ class SecurityComponentTest extends CakeTestCase {
$data['_Model3']['valid'] = '0';
$data['__Token']['key'] = $key;
$fields = array('Model' => array('username', 'password', 'valid'),
$fields = array(
'Model' => array('username', 'password', 'valid'),
'Model2'=> array('valid'),
'Model3'=> array('valid'),
'_Model2'=> array('valid' => '0'),
'_Model3'=> array('valid' => '0'),
'_Model' => array('valid' => '0'),
'__Token' => array('key' => $key));
'__Token' => array('key' => $key)
);
$fields = $this->__sortFields($fields);
$fields = urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt')));
$fields = urlencode(Security::hash(serialize($this->__sortFields($fields)) . Configure::read('Security.salt')));
$data['__Token']['fields'] = $fields;
$this->Controller->data = $data;
@ -236,6 +309,10 @@ class SecurityComponentTest extends CakeTestCase {
$this->assertTrue($this->Controller->data == $data);
}
function testLoginValidation() {
}
function testValidateHasManyModel() {
$this->Controller->Security->startup($this->Controller);
$key = $this->Controller->params['_Token']['key'];