Pejman Moghadam / PHP

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

    <?php
    class DATABASE_CONFIG {

            var $default = array(
                    'driver' => '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

    <h2>Login</h2>
    <?php
    echo $this->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

    <?php echo $this->Session->flash('auth'); ?>

Create app/app_controller.php

    <?php
    class AppController extends Controller {
            var $components = array('Acl', 'Auth', 'Session');
            var $helpers = array('Html', 'Form', 'Session');
            function beforeFilter() {
                    //Configure AuthComponent
                    $this->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

  1. 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']));
            }
    }
    
  2. 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


BY: Pejman Moghadam
TAG: php, cakephp
DATE: 2011-11-15 18:03:09


Pejman Moghadam / PHP [ TXT ]