CakePHP 1.2.0.6311b: Group + ACL + AuthComponent

หลังจากที่ใช้ ACL กับ AuthComponent ได้แล้ว … ก็จะลองเปลี่ยนตัวอย่างเก่าให้ใช้ Group ได้ดู ก็แก้เยอะเหมือน😛.

ผมเริ่มจากการแก้ 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' =&gt; array('type'=&gt;'integer',
                            'null' =&gt; false,
                            'key' =&gt; 'primary',
                            'extra' =&gt; 'auto_increment'),
              'username' =&gt;  array('type'=&gt;'string',
                                   'null' =&gt; false,
                                   'length' =&gt; 255),
              'password' =&gt;  array('type'=&gt;'string',
                                   'null' =&gt; false,
                                   'length' =&gt; 255),
              'group_id' =&gt; array('type' =&gt; 'integer',
                                  'null' =&gt; false),
              'indexes' =&gt; array('PRIMARY' =&gt; array('column' =&gt; 'id',
                                                    'unique' =&gt; 1)));
    var $groups =
        array('id' =&gt; array('type' =&gt; 'integer',
                            'null' =&gt; false,
                            'key' =&gt; 'primary',
                            'extra' =&gt; 'auto_increment'),
              'name' =&gt; array('type' =&gt; 'string',
                              'null' =&gt; 'false',
                              'length' =&gt; 255,
                              'unique' =&gt; true),
              'parent_id' =&gt; array('type' =&gt; 'integer'),
              'indexes' =&gt; array('PRIMARY' =&gt; array('column' =&gt; 'id',
                                                    'unique' =&gt; true),
                                 'GRP_NAME_KEY' =&gt; array('column' =&gt; 'name',
                                                         'unique' =&gt; 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' =&gt; 'Aro', 'controlled' =&gt; 'Aco');
/**
 * Sets up the configuation for the model, and loads ACL models if they haven't been already
 *
 * @param mixed $config
 */
    function setup(&amp;$model, $config = array()) {
        if (is_string($config)) {
            $config = array('type' =&gt; $config);
        }
        $this-&gt;settings[$model-&gt;alias] = array_merge(array('type' =&gt; 'requester'), (array)$config);
        $type = $this-&gt;__typeMaps[$this-&gt;settings[$model-&gt;alias]['type']];

        if (!ClassRegistry::isKeySet($type)) {
            uses('model' . DS . 'db_acl');
            $object =&amp; new $type();
        } else {
            $object =&amp; ClassRegistry::getObject($type);
        }
        $model-&gt;{$type} =&amp; $object;
        if (!method_exists($model, 'parentNode')) {
            trigger_error("Callback parentNode() not defined in {$model-&gt;alias}", E_USER_WARNING);
        }
    }
/**
 * Retrieves the Aro/Aco node for this model
 *
 * @param mixed $ref
 * @return array
 */
    function node(&amp;$model, $ref = null) {
        $type = $this-&gt;__typeMaps[strtolower($this-&gt;settings[$model-&gt;alias]['type'])];
        if (empty($ref)) {
            $ref = array('model' =&gt; $model-&gt;alias, 'foreign_key' =&gt; $model-&gt;id);
        }

        return $model-&gt;{$type}-&gt;node($ref);
    }
/**
 * Creates a new ARO/ACO node bound to this record
 *
 * @param boolean $created True if this is a new record
 */
    function afterSave(&amp;$model, $created) {
        if ($created) {
            $type = $this-&gt;__typeMaps[strtolower($this-&gt;settings[$model-&gt;alias]['type'])];
            $parent = $model-&gt;parentNode();

            if (!empty($parent)) {
                $parent = $this-&gt;node($model, $parent);
            } else {
                $parent = null;
            }

            $acl_node_alias = null;

            if(method_exists($model, "aclNodeAlias"))
                $acl_node_alias = $model-&gt;aclNodeAlias();

            $model-&gt;{$type}-&gt;create();
            $model-&gt;{$type}-&gt;save(array(
                'parent_id'        =&gt; Set::extract($parent, "0.{$type}.id"),
                'model'            =&gt; $model-&gt;alias,
                'foreign_key'    =&gt; $model-&gt;id,
                'alias'         =&gt; $acl_node_alias
            ));
        }
    }
/**
 * Destroys the ARO/ACO node bound to the deleted record
 *
 */
    function afterDelete(&amp;$model) {
        $type = $this-&gt;__typeMaps[strtolower($this-&gt;settings[$model-&gt;alias]['type'])];
        $node = Set::extract($this-&gt;node($model), "0.{$type}.id");
        if (!empty($node)) {
            $model-&gt;{$type}-&gt;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-&gt;args[0];
        $password = $auth-&gt;password($this-&gt;args[1]);
        $grp_name = $this-&gt;args[2];
        $grp = $this-&gt;get_group($grp_name);
        $data = array("User" =&gt;
                      array("username" =&gt; $username,
                            "password" =&gt; $password,
                            "group_id" =&gt; $grp["id"]));
        if($this-&gt;User-&gt;save($data))
            print "Success: $username was added\n";
        else
            print "Fail\n";
    }

    function get_group($name) {
        $data = $this-&gt;Group-&gt;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-&gt;args[1];
        $parent_name = $this-&gt;args[0];
        if($parent_name == '/')
            $parent_id = null;
        else
            $parent_id = $this-&gt;get_parent($parent_name);
        $data = array("Group" =&gt;
                      array("parent_id" =&gt; $parent_id,
                            "name" =&gt; $name));
        if($this-&gt;Group-&gt;save($data))
            print "Success: group: $name has been added\n";
        else
            print "Fail\n";
    }

    function get_parent($name) {
        $data = $this-&gt;Group-&gt;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-&gt;id)
            return null;

        $data = $this-&gt;read();
        if(!$data['Group']['id'])
            return null;
        else
            return array('model' =&gt; 'Group',
                         'foreign_key' =&gt; $data['Group']['id']); 
    }
}

ตามด้วยเขียน group.php ขึ้นมาใหม่:

class Group extends AppModel {
    var $actsAs = array('Acla');
    var $hasMany = array('User');
    function parentNode() {
        if(!$this-&gt;id)
            return null;
        $data = $this-&gt;read();
        if(!$data['Group']['parent_id'])
            return null;
        else
            return array('model' =&gt; 'Group',
                         'foreign_key' =&gt; $data['Group']['parent_id']);
    }

    function aclNodeAlias() {
        if(!$this-&gt;id)
            return null;
        $data = $this-&gt;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/

2 thoughts on “CakePHP 1.2.0.6311b: Group + ACL + AuthComponent

  1. เจ๋งมากเลยครัีบ แต่ขอเป็น comment ภาษาไทยแทรกใน code จะดีกว่าครัีบ

    เพราะคำสั่งมันเยอะอะครับ นั่งวิเคราะห์เอง บางคำสั่งก็ไม่รู้จักเลยงงเลย

ใส่ความเห็น

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / เปลี่ยนแปลง )

Twitter picture

You are commenting using your Twitter account. Log Out / เปลี่ยนแปลง )

Facebook photo

You are commenting using your Facebook account. Log Out / เปลี่ยนแปลง )

Google+ photo

You are commenting using your Google+ account. Log Out / เปลี่ยนแปลง )

Connecting to %s