มาสร้าง Access Control List กันดีกว่าจัดการง่ายมากระบบง่ายมากครับ

ACL เป็นการสร้างและตรวจสอบสิทธิ์ในการเข้าถึง Action และ Controller ใน CakePHP ซึ่งทำให้เราสามารถกำหนดสิทธิ์ในการเข้าใช้งานเว็บในส่วนต่างๆ ของเราได้ เช่นนายก ให้ เพิ่ม ดู แต่ไม่สามารถให้ลบได้ นายข ให้เพิ่ม ดู ลบ แก้ไข ได้ซึ่งทั้งสองคนก็จะมีสิทธิ์ที่ไม่เหมือนกัน นายก สิทธิ์คือ สมาชิก นายข สิทธิ์คือ ผู้ตรวจแก้ เป็นต้น

ขั้นที่ 1 เตรียมฐานข้อมูลด้วยการใช้ Console

ในขั้นนี้จะไม่ขอกล่าวถึงการตั้งค่าในการใช้นะครับ
ขออธิบายการใช้ Console พอเข้าใจนะครับ Console คือการใช้งาน Console mode ซึ่งเป็นตัวช่วยให้เราเขียนโปรแกรมได้รวดเร็วมากยิ่งขึ้น ผมว่าช่วยได้กว่า 30% ของเวลา Coding เลยล่ะ

มาเริ่มกันเลยล่ะกันคับ
cake acl initdb

หรือมีโครงสร้างแบบนี้ครับ
CREATE TABLE IF NOT EXISTS `acos` (
  `id` int(10) NOT NULL auto_increment,
  `parent_id` int(10) default NULL,
  `model` varchar(255) collate utf8_unicode_ci default NULL,
  `foreign_key` int(10) default NULL,
  `alias` varchar(255) collate utf8_unicode_ci default NULL,
  `lft` int(10) default NULL,
  `rght` int(10) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

 

CREATE TABLE IF NOT EXISTS `aros` (
  `id` int(10) NOT NULL auto_increment,
  `parent_id` int(10) default NULL,
  `model` varchar(255) collate utf8_unicode_ci default NULL,
  `foreign_key` int(10) default NULL,
  `alias` varchar(255) collate utf8_unicode_ci default NULL,
  `lft` int(10) default NULL,
  `rght` int(10) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `aros_acos` (
  `id` int(10) NOT NULL auto_increment,
  `aro_id` int(10) NOT NULL,
  `aco_id` int(10) NOT NULL,
  `_create` varchar(2) collate utf8_unicode_ci NOT NULL default '0',
  `_read` varchar(2) collate utf8_unicode_ci NOT NULL default '0',
  `_update` varchar(2) collate utf8_unicode_ci NOT NULL default '0',
  `_delete` varchar(2) collate utf8_unicode_ci NOT NULL default '0',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `ARO_ACO_KEY` (`aro_id`,`aco_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

 

app/controllers/app_controller.php
<?php
class AppController extends Controller{
    var $helpers=array('Html','Form','Javascript');
    var $components=array('Acl', 'Auth', 'InheritAcl', 'RequestHandler'); 
   
    function beforeFilter(){
       
        if(isset($this->Auth)){
            if($this->viewPath == 'pages'){
                $this->Auth->allow('*');
            }else{
                $this->Auth->loginAction   = '/users/login';
                $this->Auth->authorize     = 'actions';
            }
        }
    } 
}
?>

 

app/controllers/components/controller_list.php
<?php

/*
 * List all method of all controllers
 *
 */
class ControllerListComponent extends Object
{
    function get()
    {
       return $this->_getControllers();
    }

    function _getControllers($controller = null)
    {
        $controllers = array();
        $conf = Configure::getInstance();

        $paths      = $conf->controllerPaths;
        $plugPaths  = $conf->pluginPaths;
        $pluglist   = Configure::listObjects('plugin');

        foreach($plugPaths as $l)
        {
            foreach($pluglist as $v)
                $paths[] = $l.inflector::underscore($v).DS.'controllers';
        }
       
        if(!$controller)
        {
            $pluglist = array_merge(array(''), $pluglist);
            $controllerList = Configure::listObjects('controller', $paths, false);

            foreach($controllerList as $file)
                $controllers[$file] = $this->_getControllerMethods($file, $pluglist);
        }
        else
            $controllers[$controller] = $this->_getControllerMethods($controller, $pluglist);
       
        return $controllers;
    }

    function _getControllerMethods($controllerName, $plugins)
    {
        $classMethodsCleaned = array();
        $found = false;
       
        foreach($plugins as $plugin)
        {
            if(App::import('Controller', empty($plugin) ? $controllerName : $plugin.'.'.$controllerName))
            {
                $found = true;
                break;
            }
        }
       
        if(!$found)
            return array();
           
        $parentClassMethods = get_class_methods(get_parent_class(Inflector::camelize($controllerName).'Controller'));
        $subClassMethods    = get_class_methods(Inflector::camelize($controllerName).'Controller');
        $classMethods       = array_diff($subClassMethods, $parentClassMethods);

        foreach($classMethods as $method)
        {
            if($method{0} <> "_")
            {
                $classMethodsCleaned[] = $method;
            }
        }
       
        return $classMethodsCleaned;
    }
}

?>

 

app/controllers/components/inherit_acl.php
<?php
/*
* small component the reset the parent Aco in case of inheritance
*
*/
class InheritAclComponent extends Object
{
    var $controller = null;

    function startup(&$controller)
    {
        $this->controller = $controller;
    }

    function checkAroParent($modelname, $id, $parentModel, $parentId)
    {
        // find the current aro for the model

        $aro = $this->controller->Acl->Aro->node(array('model' => $modelname, 'foreign_key' => $id));

        if(empty($aro))
        {
            trigger_error("checkAroParent, no Aro found for model: {$modelname}, id : {$id}", E_USER_WARNING);
            return false;
        }

        $aronode = $aro[0]['Aro'];

        // if parent did not change, perfect

        if($aronode['parent_id'] == $parentId)
            return true;

        // we find the Aro for the parent model

        $aroparent = $this->controller->Acl->Aro->node(array('model' => $parentModel, 'foreign_key' => $parentId));

        if(empty($aroparent))
        {
            trigger_error("checkAroParent, no Aro found for parent model: {$parentModel}, id : {$parentId}", E_USER_WARNING);
            return false;
        }

        // we get the aro id of the parent

        $newid = $aroparent[0]['Aro']['id'];

        // save aro with new parent id

        $aronode['parent_id'] = $newid;
        $this->controller->Acl->Aro->save($aronode);
        return true;
    }
}
?>

เพิ่มเติมส่วนของ Database
CREATE TABLE IF NOT EXISTS `roles` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(64) collate utf8_unicode_ci NOT NULL,
  `parent_id` int(10) unsigned default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;


CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL auto_increment,
  `role_id` int(11) NOT NULL default '2',
  `username` varchar(255) collate utf8_unicode_ci NOT NULL,
  `password` varchar(45) collate utf8_unicode_ci NOT NULL,
  `fullname` varchar(255) collate utf8_unicode_ci NOT NULL,
  `email` varchar(255) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

 

app/controllers/roles_controller.php
<?php
class RolesController extends AppController {

    var $name = 'Roles';
    var $helpers    = array('Html', 'Form', 'Ajax');
    var $components = array('ControllerList', 'RequestHandler', 'InheritAcl');
   
    function beforeFilter()
    {
        //$this->Auth->allow('acl', 'index');
        parent::beforeFilter();
    }
   
    function acl()
    {
       $data           = $this->Role->findAll();
       $controllerList = $this->ControllerList->get(); 

       // we loop on all action for all roles
       $inidbg =  Configure::read( 'debug');

       Configure::write( 'debug', '0' );      

       foreach($controllerList as $controller => $actions )               
       {
             foreach($actions as $key => $action)
             {
             $controllerList[$controller][$action] = array();         
             unset($controllerList[$controller][$key]);                             
             foreach($data as $p)
             {
                   $controllerList[$controller][$action][$p['Role']['id']] = $this->Acl->check($p, $controller . '/'. $action, '*');                
             }    
          }  
       }

       Configure::write( 'debug', $inidbg);         
       $this->set('ctlist', $controllerList);  
       $this->set('data', $data);    
    }

        // we set unset the permission
   
    function adjustperm($roleid, $controller, $action, $permission)
    {
       // we read the role again
      
       $role = $this->Role->read(null, $roleid);

       if($action == 'all')
       {
          $controllerList = $this->ControllerList->get();

          foreach($controllerList[$controller] as $action )
              $this->setPermissions($role, $controller, $action, $permission );
             
          $this->set('ctlist', $controllerList[$controller]);        
       }
       else
       {
          $this->setPermissions($role, $controller, $action, $permission );       
          $this->set('ctlist',     array($action));             
       }
         
       $this->set('controller', $controller);
       $this->set('permission', $permission);
       $this->set('roleid',     $roleid);      
    }
   
    private function setPermissions($role, $controller, $action, $permission )
    {
          // First check to make sure that the controller is already set up as an ACO

          $aco = new Aco(  );

          $rootAco = $aco->findByAlias( 'ROOT' );

          // Set up $controllerAco if it's not present.

          $controllerAco = $aco->findByAlias( $controller );//$this->Administrator->query( 'SELECT Aco.* From acos AS Aco LEFT JOIN acos AS Aco0 ON Aco0.alias = "'.$controller.'" LEFT JOIN acos AS Aco1 ON Aco1.lft > Aco0.lft && Aco1.rght < Aco0.rght AND Aco1.alias = "ROOT" WHERE Aco.lft <= Aco0.lft AND Aco.rght >= Aco0.rght ORDER BY Aco.lft DESC' ) );
 
          if( empty( $controllerAco ) )
          {
               $aco->create(  );
               $aco->save( array ('alias' => $controller, 'parent_id' => $rootAco['Aco']['id']));
               $controllerAco = $aco->findByAlias( $controller );//$this->Administrator->query( 'SELECT Aco.* From acos AS Aco LEFT JOIN acos AS Aco0 ON Aco0.alias = "'.$controller.'" LEFT JOIN acos AS Aco1 ON Aco1.lft > Aco0.lft && Aco1.rght < Aco0.rght AND Aco1.alias = "ROOT" WHERE Aco.lft <= Aco0.lft AND Aco.rght >= Aco0.rght ORDER BY Aco.lft DESC' ) );
          }

          // Set up $actionAcoif it's not present.
 
          $actionAco = $aco->find( array( 'parent_id' => $controllerAco['Aco']['id'], 'alias' => $action ) );
 
          if ( empty( $actionAco ) )
          {
               $aco->create(  );
               $aco->save( array ('alias' => $action, 'parent_id' => $controllerAco['Aco']['id']));
               $actionAco = $aco->find( array( 'parent_id' => $controllerAco['Aco']['id'], 'alias' => $action ) );
          }

          // Set up perms now.
 
          if ( $permission == 'allow' )
              $this->Acl->allow( array( 'model' => 'Role', 'foreign_key' => $role['Role']['id'] ), $controller . '/' . $action );
          else
              $this->Acl->deny( array( 'model' => 'Role', 'foreign_key' => $role['Role']['id'] ), $controller . '/' . $action );
    }

    function index() {
        $this->Role->recursive = 0;
        $this->set('roles', $this->paginate());
    }
   
    function view($id = null) {
        if (!$id) {
            $this->Session->setFlash('Invalid Role.');
            $this->redirect(array('action'=>'index'), null, true);
        }
        $this->set('role', $this->Role->read(null, $id));
    }

    function add() {
        if (!empty($this->data)) {
            //$this->cleanUpFields();
            $this->Role->create();
            if ($this->Role->save($this->data)) {
                $lid = $this->Role->getLastInsertId();
                $this->Session->setFlash('The Role has been saved');
                $this->redirect(array('action'=>'index'), null, true);
            } else {
                $this->Session->setFlash('The Role could not be saved. Please, try again.');
            }
        }

        $roles = $this->Role->find('list');
        $this->set(compact('roles')); 
    }

    function edit($id = null) {
        if (!$id && empty($this->data)) {
            $this->Session->setFlash('Invalid Role');
            $this->redirect(array('action'=>'index'), null, true);
        }
        if (!empty($this->data)) {
            //$this->cleanUpFields();
            if ($this->data['Role']['id'] != $this->data['Role']['parent_id'] &&
                 $this->Role->save($this->data)) {
                $this->InheritAcl->checkAroParent('Role', $this->data['Role']['id'], 'Role', $this->data['Role']['parent_id']);               
                $this->Session->setFlash('The Role has been saved');
                $this->redirect(array('action'=>'index'), null, true);
            } else {
                $this->Session->setFlash('The Role could not be saved (check parent role). Please, try again.');
            }
        }
        if (empty($this->data)) {
            $this->data = $this->Role->read(null, $id);
        }

        $roles = $this->Role->find('list');
        $this->set(compact('roles'));       
    }

    function delete($id = null) {
        if (!$id) {
            $this->Session->setFlash('Invalid id for Role');
            $this->redirect(array('action'=>'index'), null, true);
        }
        if ($this->Role->del($id)) {
            $this->Session->setFlash('Role #'.$id.' deleted');
            $this->redirect(array('action'=>'index'), null, true);
        }
    }

}
?>

 

app/controllers/users_controller.php
<?php
class UsersController extends AppController {

    var $name       = 'Users';
    var $helpers    = array('Html', 'Form' );
    var $components = array('InheritAcl');
   
   
    function beforeFilter()
    {
        parent::beforeFilter();
        $this->Auth->allow('login', 'logout', 'register');
       
    }
   
    function login() {
        if(!empty($this->data)) {
            if($this->Auth->login()) {
                $this->Session->setFlash('Login success!');
                $this->redirect($this->Auth->redirect(), null, true);
            } else {
                $this->Session->setFlash('Login incorrect!');
            }
        } else if($this->referer() != '/') {
            $this->Session->write('Auth.redirect', $this->referer());
        }
    }

    function register(){
        if(!empty($this->data)){
            if ($this->ThaiCaptchas->validate()){
                $this->User->create();
                if($this->User->save($this->data)){
                    $this->Session->setFlash('บันทึกข้อมูลเรียบร้อย',null,true); 
                    $this->redirect(array('controller'=>'users','action'=>'main'));
                }else{
                    $this->Session->setFlash('ไม่สามารถบันทึกข้อมูลได้',null,true);     
                    $this->redirect(array('controller'=>'users','action'=>'register'));
                }
            }
        }   
    }
    /*
    function login()
    {
        if(!empty($this->data))
        {
            $data = $this->User->find(array('username' => $this->data['User']['username'],
                                            'password' => sha1(Configure::read('Security.salt').$this->data['User']['password'])));
            if(!empty($data))
            {
                $this->Session->write('User', $data['User']);
                $this->Session->setFlash('User logged in');
                $this->redirect('/', null, true);
            }
            else
                $this->Session->setFlash("Check your account");
        }
    }  
*/
    function logout()
    {
        $this->Auth->logout();
        $this->redirect('/', null, true);       
    }
   
    function index() {
        $this->User->recursive = 0;
        $this->set('users', $this->paginate());
    }

    function add() {
        if (!empty($this->data)) {
            //$this->cleanUpFields();
            $this->User->create();
            if ($this->User->save($this->data)) {
                $this->Session->setFlash('The User has been saved');
                $this->redirect(array('action'=>'index'), null, true);
            } else {
                $this->Session->setFlash('The User could not be saved. Please, try again.');
            }
        }
        //$roles = $this->User->Role->find('list');
        $roles = $this->User->Role->find('list');
        $this->set(compact('roles'));
    }

    function edit($id = null) {
        if (!$id && empty($this->data)) {
            $this->Session->setFlash('Invalid User');
            $this->redirect(array('action'=>'index'), null, true);
        }
        if (!empty($this->data)) {
            //$this->cleanUpFields();
            if ($this->User->save($this->data))
            {
                // we might have to reset the parent aro
                $this->InheritAcl->checkAroParent('User', $this->data['User']['id'], 'Role', $this->data['User']['role_id']);
                $this->Session->setFlash('The User has been saved');
                $this->redirect(array('action'=>'index'), null, true);
            } else {
                $this->Session->setFlash('The User could not be saved. Please, try again.');
            }
        }
        if (empty($this->data))
        {
            $this->data = $this->User->read(null, $id);
        }
        $roles = $this->User->Role->find('list');
        $this->set(compact('roles'));
    }
   
    function panel(){
       
    }
    function _setParentAro($userid, $parent_id)
    {
        // we first retrieve the Aro of this use
       
        $aro = $this->Acl->Aro->node(array('model' => 'User', 'foreign_key' => $userid));
       
        if(empty($aro))
            return;
           
        $aronode = $aro[0]['Aro'];
        // parent did not change

        if($aronode['parent_id'] == $userid)
            return;
        // we first need to find the aro id of the new parent
       
        $arop = $this->Acl->Aro->node(array('model' => 'Role', 'foreign_key' => $parent_id));
       
        if(!empty($arop))
        {
            $newid = $arop[0]['Aro']['id'];
            $aronode['parent_id'] = $newid;
            $this->Acl->Aro->save($aronode);
        }
        else
            echo "Error resetting the parent ARO";
    }
   
    function delete($id = null) {
        if (!$id) {
            $this->Session->setFlash('Invalid id for User');
            $this->redirect(array('action'=>'index'), null, true);
        }
        if ($this->User->del($id)) {
            $this->Session->setFlash('User #'.$id.' deleted');
            $this->redirect(array('action'=>'index'), null, true);
        }
    }

}
?>

app/models/role.php
<?php
class Role extends AppModel {

    var $name   = 'Role';   
    var $hasMany = array('User' => array('className' => 'User',
                                'foreignKey' => 'role_id',
                                'conditions' => '',
                                'fields' => '',
                                'order' => '',
                                'counterCache' => '')
                        );

    var $actsAs = array('Acl' => 'requester');                       
    function parentNode()
    {
        if($this->id)
        {
            $data = $this->read();

            if($data['Role']['parent_id'])
                return array('model' => 'Role', 'foreign_key' => $data['Role']['parent_id']);
        }
       
        return null;
    }
}
?>

app/models/user.php
<?php
class User extends AppModel {

    var $name = 'User';

    //The Associations below have been created with all possible keys, those that are not needed can be removed
    var $belongsTo = array(
            'Role' => array('className' => 'Role',
                                'foreignKey' => 'role_id',
                                'conditions' => '',
                                'fields' => '',
                                'order' => '',
                                'counterCache' => ''),
    );

    var $actsAs = array('Acl'=>array('type'=> 'requester'));
   
    function parentNode()
    {
        if($this->id)
        {
            $data = $this->read();

            if($data['User']['role_id'])
                return array('model' => 'Role', 'foreign_key' => $data['User']['role_id']);
        }
       
        return null;
    }
}
?>

การใช้งาน
app/controllers/posts_controller.php

............
function beforeFilter(){
        parent::beforeFilter();
        $this->Auth->allow('*');  
    }
............