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('*');
}
............
ขอบคุณค่ะ