CakePHP 1.3 - Simple ACL Example
================================
Public domain
********************************************************************************
### SQL
CREATE DATABASE blog;
USE blog;
CREATE TABLE users (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
password CHAR(40) NOT NULL,
group_id INT(11) NOT NULL,
created DATETIME,
modified DATETIME
);
CREATE TABLE groups (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
created DATETIME,
modified DATETIME
);
CREATE TABLE posts (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id INT(11) NOT NULL,
title VARCHAR(255) NOT NULL,
body TEXT,
created DATETIME,
modified DATETIME
);
CREATE TABLE widgets (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
part_no VARCHAR(12),
quantity INT(11)
);
********************************************************************************
### Commands
cp -a cakephp-cakephp-8236c7e/ weblog
cd weblog
chown -R apache app/tmp/
********************************************************************************
### Create app/config/database.php
'mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'blog',
'prefix' => '',
//'encoding' => 'utf8',
);
}
********************************************************************************
### Check
http://localhost/weblog/
********************************************************************************
### Bake
cake/console/cake bake all group
cake/console/cake bake all post
cake/console/cake bake all user
cake/console/cake bake all widget
chown -R apache app/tmp/
********************************************************************************
### Add to app/controllers/users_controller.php
function login() {
if ($this->Session->read('Auth.User')) {
$this->Session->setFlash('You are logged in!');
$this->redirect('/', null, false);
}
}
function logout() {
$this->Session->setFlash('Good-Bye');
$this->redirect($this->Auth->logout());
}
********************************************************************************
### Create app/views/users/login.ctp
Login
Session->flash('auth');
echo $this->Form->create('User', array('url' => array('controller' => 'users', 'action' =>'login')));
echo $this->Form->inputs(array('legend' => __('Login', true),'username','password'));
echo $this->Form->end('Login');
?>
********************************************************************************
### Prepare customized default layout
cp cake/libs/view/layouts/default.ctp app/views/layouts/
********************************************************************************
### In app/views/layouts/default.ctp , add to body
Session->flash('auth'); ?>
********************************************************************************
### Create app/app_controller.php
Auth->authorize = 'actions';
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'logout');
$this->Auth->loginRedirect = array('controller' => 'posts', 'action' => 'add');
}
}
?>
********************************************************************************
### Temp add to app/controllers/users_controller.php
### Temp add to app/controllers/groups_controller.php
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow(array('*'));
}
********************************************************************************
### Initialize the Db Acl tables
cake/console/cake schema create DbAcl
chown -R apache app/tmp/
********************************************************************************
### Acts As a Requester
0. Add to app/models/user.php
var $actsAs = array('Acl' => 'requester');
function parentNode() {
if (!$this->id && empty($this->data)) {
return null;
}
$data = $this->data;
if (empty($this->data)) {
$data = $this->read();
}
if (empty($data['User']['group_id'])) {
return null;
} else {
return array('Group' => array('id' => $data['User']['group_id']));
}
}
0. Add to app/models/group.php
var $actsAs = array('Acl' => array('type' => 'requester'));
function parentNode() {
return null;
}
********************************************************************************
### Add new groups: administrators, managers, users
http://localhost/weblog/groups/add
********************************************************************************
### Add new users in each group: admin, operator, test
http://localhost/weblog/users/add
********************************************************************************
### Add to app/models/user.php
/**
* After save callback
*
* Update the aro for the user.
*
* @access public
* @return void
*/
function afterSave($created) {
if (!$created) {
$parent = $this->parentNode();
$parent = $this->node($parent);
$node = $this->node();
$aro = $node[0];
$aro['Aro']['parent_id'] = $parent[0]['Aro']['id'];
$this->Aro->save($aro);
}
}
********************************************************************************
### Creating ACOs (Access-Control-Objects)
cake/console/cake acl create aco root controllers
cake/console/cake acl create aco controllers Posts
cake/console/cake acl create aco Posts add
cake/console/cake acl create aco Posts edit
cake/console/cake acl create aco controllers Widgets
cake/console/cake acl create aco Widgets add
cake/console/cake acl create aco Widgets edit
chown -R apache app/tmp/
********************************************************************************
### In app/app_controller.php add the following to the beforeFilter:
$this->Auth->actionPath = 'controllers/';
********************************************************************************
### An Automated tool for creating ACOs, add to app/app_controller.php
function build_acl() {
if (!Configure::read('debug')) {
return $this->_stop();
}
$log = array();
$aco =& $this->Acl->Aco;
$root = $aco->node('controllers');
if (!$root) {
$aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers'));
$root = $aco->save();
$root['Aco']['id'] = $aco->id;
$log[] = 'Created Aco node for controllers';
} else {
$root = $root[0];
}
App::import('Core', 'File');
$Controllers = App::objects('controller');
$appIndex = array_search('App', $Controllers);
if ($appIndex !== false ) {
unset($Controllers[$appIndex]);
}
$baseMethods = get_class_methods('Controller');
$baseMethods[] = 'build_acl';
$Plugins = $this->_getPluginControllerNames();
$Controllers = array_merge($Controllers, $Plugins);
// look at each controller in app/controllers
foreach ($Controllers as $ctrlName) {
$methods = $this->_getClassMethods($this->_getPluginControllerPath($ctrlName));
// Do all Plugins First
if ($this->_isPlugin($ctrlName)){
$pluginNode = $aco->node('controllers/'.$this->_getPluginName($ctrlName));
if (!$pluginNode) {
$aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginName($ctrlName)));
$pluginNode = $aco->save();
$pluginNode['Aco']['id'] = $aco->id;
$log[] = 'Created Aco node for ' . $this->_getPluginName($ctrlName) . ' Plugin';
}
}
// find / make controller node
$controllerNode = $aco->node('controllers/'.$ctrlName);
if (!$controllerNode) {
if ($this->_isPlugin($ctrlName)){
$pluginNode = $aco->node('controllers/' . $this->_getPluginName($ctrlName));
$aco->create(array('parent_id' => $pluginNode['0']['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginControllerName($ctrlName)));
$controllerNode = $aco->save();
$controllerNode['Aco']['id'] = $aco->id;
$log[] = 'Created Aco node for ' . $this->_getPluginControllerName($ctrlName) . ' ' . $this->_getPluginName($ctrlName) . ' Plugin Controller';
} else {
$aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName));
$controllerNode = $aco->save();
$controllerNode['Aco']['id'] = $aco->id;
$log[] = 'Created Aco node for ' . $ctrlName;
}
} else {
$controllerNode = $controllerNode[0];
}
//clean the methods. to remove those in Controller and private actions.
foreach ($methods as $k => $method) {
if (strpos($method, '_', 0) === 0) {
unset($methods[$k]);
continue;
}
if (in_array($method, $baseMethods)) {
unset($methods[$k]);
continue;
}
$methodNode = $aco->node('controllers/'.$ctrlName.'/'.$method);
if (!$methodNode) {
$aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method));
$methodNode = $aco->save();
$log[] = 'Created Aco node for '. $method;
}
}
}
if(count($log)>0) {
debug($log);
}
}
function _getClassMethods($ctrlName = null) {
App::import('Controller', $ctrlName);
if (strlen(strstr($ctrlName, '.')) > 0) {
// plugin's controller
$num = strpos($ctrlName, '.');
$ctrlName = substr($ctrlName, $num+1);
}
$ctrlclass = $ctrlName . 'Controller';
$methods = get_class_methods($ctrlclass);
// Add scaffold defaults if scaffolds are being used
$properties = get_class_vars($ctrlclass);
if (array_key_exists('scaffold',$properties)) {
if($properties['scaffold'] == 'admin') {
$methods = array_merge($methods, array('admin_add', 'admin_edit', 'admin_index', 'admin_view', 'admin_delete'));
} else {
$methods = array_merge($methods, array('add', 'edit', 'index', 'view', 'delete'));
}
}
return $methods;
}
function _isPlugin($ctrlName = null) {
$arr = String::tokenize($ctrlName, '/');
if (count($arr) > 1) {
return true;
} else {
return false;
}
}
function _getPluginControllerPath($ctrlName = null) {
$arr = String::tokenize($ctrlName, '/');
if (count($arr) == 2) {
return $arr[0] . '.' . $arr[1];
} else {
return $arr[0];
}
}
function _getPluginName($ctrlName = null) {
$arr = String::tokenize($ctrlName, '/');
if (count($arr) == 2) {
return $arr[0];
} else {
return false;
}
}
function _getPluginControllerName($ctrlName = null) {
$arr = String::tokenize($ctrlName, '/');
if (count($arr) == 2) {
return $arr[1];
} else {
return false;
}
}
/**
* Get the names of the plugin controllers ...
*
* This function will get an array of the plugin controller names, and
* also makes sure the controllers are available for us to get the
* method names by doing an App::import for each plugin controller.
*
* @return array of plugin names.
*
*/
function _getPluginControllerNames() {
App::import('Core', 'File', 'Folder');
$paths = Configure::getInstance();
$folder =& new Folder();
$folder->cd(APP . 'plugins');
// Get the list of plugins
$Plugins = $folder->read();
$Plugins = $Plugins[0];
$arr = array();
// Loop through the plugins
foreach($Plugins as $pluginName) {
// Change directory to the plugin
$didCD = $folder->cd(APP . 'plugins'. DS . $pluginName . DS . 'controllers');
// Get a list of the files that have a file name that ends
// with controller.php
$files = $folder->findRecursive('.*_controller\.php');
// Loop through the controllers we found in the plugins directory
foreach($files as $fileName) {
// Get the base file name
$file = basename($fileName);
// Get the controller name
$file = Inflector::camelize(substr($file, 0, strlen($file)-strlen('_controller.php')));
if (!preg_match('/^'. Inflector::humanize($pluginName). 'App/', $file)) {
if (!App::import('Controller', $pluginName.'.'.$file)) {
debug('Error importing '.$file.' for plugin '.$pluginName);
} else {
/// Now prepend the Plugin name ...
// This is required to allow us to fetch the method names.
$arr[] = Inflector::humanize($pluginName) . "/" . $file;
}
}
}
}
return $arr;
}
********************************************************************************
### Create acos almos automaticly
http://localhost/weblog/groups/build_acl
********************************************************************************
### Setting up permissions
cake/console/cake acl grant group.1 controllers all
cake/console/cake acl deny group.2 controllers all
cake/console/cake acl grant group.2 controllers posts
********************************************************************************
### Setting up permissions, add to app/controllers/users_controller.php
function initDB() {
$group =& $this->User->Group;
//Allow admins to everything
$group->id = 1;
$this->Acl->allow($group, 'controllers');
//allow managers to posts and widgets
$group->id = 2;
$this->Acl->deny($group, 'controllers');
$this->Acl->allow($group, 'controllers/Posts');
$this->Acl->allow($group, 'controllers/Widgets');
//allow users to only add and edit on posts and widgets
$group->id = 3;
$this->Acl->deny($group, 'controllers');
$this->Acl->allow($group, 'controllers/Posts/add');
$this->Acl->allow($group, 'controllers/Posts/edit');
$this->Acl->allow($group, 'controllers/Widgets/add');
$this->Acl->allow($group, 'controllers/Widgets/edit');
//we add an exit to avoid an ugly "missing views" error message
echo "all done";
exit;
}
********************************************************************************
### Visit this link once
http://localhost/weblog/users/initdb
********************************************************************************
### In app/controllers/users_controller.php change beforeFilter
### In app/controllers/groups_controller.php change beforeFilter
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allowedActions = array('index', 'view');
}
********************************************************************************
### In app/app_controller.php add to beforeFilter
$this->Auth->allowedActions = array('display');
********************************************************************************
### Cleanup
* In app/app_controller.php remove
build_acl, _getClassMethods, _isPlugin, _getPluginControllerPath, _getPluginName, _getPluginControllerName, _getPluginControllerNames functions
* In app/controllers/users_controller.php remove
initDB function
********************************************************************************
_BY: Pejman Moghadam_
_TAG: php, cakephp_
_DATE: 2011-11-15 18:03:09_