หลังจากที่ใช้ ACL กับ AuthComponent ได้แล้ว … ก็จะลองเปลี่ยนตัวอย่างเก่าให้ใช้ Group ได้ดู ก็แก้เยอะเหมือน :-P.
ผมเริ่มจากการแก้ database schema ก่อน. หลังจากที่ลองใช้ migration อยู่พักใหญ่แล้วผมก็งงๆ เลยตัดสินใจสละ table: aros และ users ไป.
เริ่มจากลบของใน table: aros และ aros_acos ก่อน ด้วยคำสั่ง:
mysql -u<your_username> -p<your_password> my_project -e 'delete from aros;' mysql -u<your_username> -p<your_password> my_project -e 'delete from aros_acos;'
แล้วก็แก้ CakeSchema ของ user ใน folder: app/config/sql, ไฟล์: users.php แก้เป็นแบบนี้:
class UsersSchema extends CakeSchema { var $name = 'users'; function before($event = array()) { return true; } function after($event = array()) { } var $users = array('id' => array('type'=>'integer', 'null' => false, 'key' => 'primary', 'extra' => 'auto_increment'), 'username' => array('type'=>'string', 'null' => false, 'length' => 255), 'password' => array('type'=>'string', 'null' => false, 'length' => 255), 'group_id' => array('type' => 'integer', 'null' => false), 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))); var $groups = array('id' => array('type' => 'integer', 'null' => false, 'key' => 'primary', 'extra' => 'auto_increment'), 'name' => array('type' => 'string', 'null' => 'false', 'length' => 255, 'unique' => true), 'parent_id' => array('type' => 'integer'), 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true), 'GRP_NAME_KEY' => array('column' => 'name', 'unique' => true)));
ของใหม่อีกอย่างหนึ่งในตัวอย่างนี้คือ AclBehavior ที่จะช่วย sync ระหว่าง users, groups และ acl. แต่เท่าที่ใช้มา AclBehavior ไม่แก้ค่า alias ใน Aro ก็เลยสร้าง AclaBehavior จากการดัดแปลง AclBehavior นิดๆ หน่อยๆ โดยเพิ่มไฟล์ acla.php ลงใน folder: app/model/behaviors แบบข้างล่าง (จริงแล้วต้องเปิด <?php และปิดด้วย ?> ด้วย … แต่ว่าใส่ใน wordpress แล้วเหมือนเพี้ยนๆ ผมก็แก้ไม่เป็นด้วย.)
/* SVN FILE: $Id: acl.php 6311 2008-01-02 06:33:52Z phpnut $ */ /** * ACL behavior class. * * Enables objects to easily tie into an ACL system * * PHP versions 4 and 5 * * CakePHP : Rapid Development Framework * Copyright 2006-2008, Cake Software Foundation, Inc. * 1785 E. Sahara Avenue, Suite 490-204 * Las Vegas, Nevada 89104 * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. * * @filesource * @copyright Copyright 2006-2008, Cake Software Foundation, Inc. * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project * @package cake * @subpackage cake.cake.libs.model.behaviors * @since CakePHP v 1.2.0.4487 * @version $Revision: 6311 $ * @modifiedby $LastChangedBy: phpnut $ * @lastmodified $Date: 2008-01-02 00:33:52 -0600 (Wed, 02 Jan 2008) $ * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ /** * Short description for file * * Long description for file * * @package cake * @subpackage cake.cake.libs.model.behaviors */ class AclaBehavior extends ModelBehavior { /** * Maps ACL type options to ACL models * * @var array * @access protected */ var $__typeMaps = array('requester' => 'Aro', 'controlled' => 'Aco'); /** * Sets up the configuation for the model, and loads ACL models if they haven't been already * * @param mixed $config */ function setup(&$model, $config = array()) { if (is_string($config)) { $config = array('type' => $config); } $this->settings[$model->alias] = array_merge(array('type' => 'requester'), (array)$config); $type = $this->__typeMaps[$this->settings[$model->alias]['type']]; if (!ClassRegistry::isKeySet($type)) { uses('model' . DS . 'db_acl'); $object =& new $type(); } else { $object =& ClassRegistry::getObject($type); } $model->{$type} =& $object; if (!method_exists($model, 'parentNode')) { trigger_error("Callback parentNode() not defined in {$model->alias}", E_USER_WARNING); } } /** * Retrieves the Aro/Aco node for this model * * @param mixed $ref * @return array */ function node(&$model, $ref = null) { $type = $this->__typeMaps[strtolower($this->settings[$model->alias]['type'])]; if (empty($ref)) { $ref = array('model' => $model->alias, 'foreign_key' => $model->id); } return $model->{$type}->node($ref); } /** * Creates a new ARO/ACO node bound to this record * * @param boolean $created True if this is a new record */ function afterSave(&$model, $created) { if ($created) { $type = $this->__typeMaps[strtolower($this->settings[$model->alias]['type'])]; $parent = $model->parentNode(); if (!empty($parent)) { $parent = $this->node($model, $parent); } else { $parent = null; } $acl_node_alias = null; if(method_exists($model, "aclNodeAlias")) $acl_node_alias = $model->aclNodeAlias(); $model->{$type}->create(); $model->{$type}->save(array( 'parent_id' => Set::extract($parent, "0.{$type}.id"), 'model' => $model->alias, 'foreign_key' => $model->id, 'alias' => $acl_node_alias )); } } /** * Destroys the ARO/ACO node bound to the deleted record * */ function afterDelete(&$model) { $type = $this->__typeMaps[strtolower($this->settings[$model->alias]['type'])]; $node = Set::extract($this->node($model), "0.{$type}.id"); if (!empty($node)) { $model->{$type}->delete($node); } } }
ไม่พอก็ยังต้องแก้ CakeShell ต่อใน folder: app/vendors/shells
แก้ user.php:
uses ('controller'.DS.'components'.DS.'auth'); class UserShell extends Shell { var $uses = array("User", "Group"); function add() { $auth = new AuthComponent(); $username = $this->args[0]; $password = $auth->password($this->args[1]); $grp_name = $this->args[2]; $grp = $this->get_group($grp_name); $data = array("User" => array("username" => $username, "password" => $password, "group_id" => $grp["id"])); if($this->User->save($data)) print "Success: $username was added\n"; else print "Fail\n"; } function get_group($name) { $data = $this->Group->findByName($name); if(!$data) return null; else return $data["Group"]; } }
สร้าง group.php ขึ้นมาใหม่:
class GroupShell extends Shell { var $uses = array("Group", "Aro"); function add() { $name = $this->args[1]; $parent_name = $this->args[0]; if($parent_name == '/') $parent_id = null; else $parent_id = $this->get_parent($parent_name); $data = array("Group" => array("parent_id" => $parent_id, "name" => $name)); if($this->Group->save($data)) print "Success: group: $name has been added\n"; else print "Fail\n"; } function get_parent($name) { $data = $this->Group->findByName($name, null, null, False); if(!$data['Group']['id']) return null; else return $data['Group']['id']; } }
ยังไม่หมดต้องมาแก้ model ต่อใน folder: app/model
แก้ user.php:
class User extends AppModel { var $actsAs = array('Acla'); var $belongsTo = array('Group'); function parentNode() { if(!$this->id) return null; $data = $this->read(); if(!$data['Group']['id']) return null; else return array('model' => 'Group', 'foreign_key' => $data['Group']['id']); } }
ตามด้วยเขียน group.php ขึ้นมาใหม่:
class Group extends AppModel { var $actsAs = array('Acla'); var $hasMany = array('User'); function parentNode() { if(!$this->id) return null; $data = $this->read(); if(!$data['Group']['parent_id']) return null; else return array('model' => 'Group', 'foreign_key' => $data['Group']['parent_id']); } function aclNodeAlias() { if(!$this->id) return null; $data = $this->read(); if(!$data['Group']['name']) return null; else return $data['Group']['name']; } }
จาก code ที่แก้มามากมายก็จะเริ่มเรียกใช้แล้ว. โดย cd เข้าไปใน cake/console สั่ง: ./cake schema run create users แล้วก็กด y ไปเรื่อยๆ. เพื่อที่จะ drop table: users เก่าทิ้งไป แล้วสร้าง table: users และ groups ขึ้นมาใหม่.
ตามด้วยสร้าง group ขึ้นมาใหม่แบ่งเป็น admins กับ users ตามนี้:
./cake group add ‘/’ users
./cake group add ‘/’ admins
แล้วก็ add user ไว้ทดลอง group ละ 1 user:
./cake user add myadmin mypassword admins
./cake user add myuser mypassword users
grant ให้ admins ดูได้ทั้ง site (ทุกหน้า, ทุก action):
./cake acl grant admins site ‘*’
grant ให้ users ดู Pages/display (เอาไว้ redirect มาเวลาดูหน้าอื่นไม่ได้). และ ดู Books/display1:
./cake acl grant users ‘Pages/display’ ‘*’
./cake acl grant users ‘Books/display1’ ‘*’
ถ้าลอง login ด้วย myadmin ก็จะดูได้ทุกหน้า. แต่ถ้า login ด้วย myuser ก็จะดู http://localhost/my_project/books/display2 ไม่ได้.
ก็เป็นอันว่า users แต่ละกลุ่ม ก็ดูหน้าเว็บได้ต่างๆ กันตามที่กำหมดไว้ :-).
อ้างอิง:
http://lemoncake.wordpress.com/2007/07/19/acl-with-groups/