Cake2.4.2 Simple ACL tutorial メモ

Tutorialでのお勉強。

ACL – Access Control List
aro – Access Request Object
aco – Accecc Controll Object

1.サンプルテーブル。これでBakeする。

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)
);

これで簡易CRUDの完成。

2.ログイン制御の基礎

まずUsersController.phpに以下を追加。

public function login() {
    if ($this->request->is('post')) {
        if ($this->Auth->login()) {
        	$this->Session->setFlash('You are logged in!');
            return $this->redirect($this->Auth->redirect());
        }
        $this->Session->setFlash(__('Your username or password was incorrect.'));
    }
}

public function logout() {
    //Leave empty for now.
}

次にView/Users/login.ctpを作成。

echo $this->Form->create('User', array('action' => 'login'));
echo $this->Form->inputs(array(
    'legend' => __('Login'),
    'username',
    'password'
));
echo $this->Form->end('Login');

そしてapp/Model/User.phpにPWハッシュ化の為に下の様に修正。

App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
    // other code.

    public function beforeSave($options = array()) {
        $this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
        return true;
    }
}

その後app/Controller/AppController.phpに下を追加して、ログイン制御。
※チュートリアルにはこう書いてあるのだが、この状態でもログイン出来ないので、初回グループ・ユーザ作成時は以下をコメントアウトすることを推奨。(2.4.2で発生)

class AppController extends Controller {
    public $components = array(
        'Acl',
        'Auth' => array(
            'authorize' => array(
                'Actions' => array('actionPath' => 'controllers')
            )
        ),
        'Session'
    );
    public $helpers = array('Html', 'Form', 'Session');

    public function beforeFilter() {
        //Configure AuthComponent
        $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
        $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
        $this->Auth->loginRedirect = array('controller' => 'posts', 'action' => 'add');
    }
}

GroupsController.php, UsersController.phpに下を追加してアクセス可に。

public function beforeFilter() {
    parent::beforeFilter();

    // For CakePHP 2.0
    $this->Auth->allow('*');

    // For CakePHP 2.1 and up
    $this->Auth->allow();
}

まだグループ・ユーザは作らない。。

3.Acl用データベースのイニシャライズ

最初にテーブル作成。このコマンドか、

./Console/cake schema create DbAcl

以下のSQLを実行。

app/Config/Schema/db_acl.sql

そして、グループとユーザの紐付けを行う。
この紐付けにはAclBehaviorを使う。
これを使うため、ユーザモデル内のparentNode()を以下のように定義。

Model/User.php

class User extends AppModel {
    public $belongsTo = array('Group');
    public $actsAs = array('Acl' => array('type' => 'requester'));

    public function parentNode() {
        if (!$this->id && empty($this->data)) {
            return null;
        }
        if (isset($this->data['User']['group_id'])) {
            $groupId = $this->data['User']['group_id'];
        } else {
            $groupId = $this->field('group_id');
        }
        if (!$groupId) {
            return null;
        } else {
            return array('Group' => array('id' => $groupId));
        }
    }
}

続いて、グループ。

Model/Group.php

class Group extends AppModel {
    public $actsAs = array('Acl' => array('type' => 'requester'));

    public function parentNode() {
        return null;
    }
}

これはグループモデルとユーザーモデルをACLでつなぎ、どちらかを作成・削除した時にはAROテーブルもアップデートする様にしている。

ここまで来たら、グループを作る。

・administrators
・managers
・users

また、それぞれのユーザも作る。

この時、一旦認証を切るために以下をコメント。

・AppController

    public $components = array(
        // 'Acl',
        // 'Auth' => array(
        //     'authorize' => array(
        //         'Actions' => array('actionPath' => 'controllers')
        //     )
        // ),
        'Session'
    );
    public $helpers = array('Html', 'Form', 'Session');

    public function beforeFilter() {
        //Configure AuthComponent
        // $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
        // $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
        // $this->Auth->loginRedirect = array('controller' => 'posts', 'action' => 'add');
    }

・UsersController

	// public function beforeFilter() {
	//     parent::beforeFilter();

	//     // For CakePHP 2.0
	//     $this->Auth->allow('*');

	//     // For CakePHP 2.1 and up
	//     $this->Auth->allow();
	// }

・GroupsController

	// public function beforeFilter() {
	//     parent::beforeFilter();

	//     // For CakePHP 2.0
	//     $this->Auth->allow('*');

	//     // For CakePHP 2.1 and up
	//     $this->Auth->allow();
	// }

 

そして今回はグループ毎に権限を持たせるので、ACLのチェックをグループのaroだけにする。

ユーザモデルに下を追加。

Model/Users.php

public function bindNode($user) {
    return array('model' => 'Group', 'foreign_key' => $user['User']['group_id']);
}

4.ACOテーブルへの登録。

テーブルarosはグループ、ユーザの追加時に自動でアップデートされるが、ここではacosをアップデートする。

下のプラグインを使う。

https://github.com/markstory/acl_extras/

こいつを使う場合は、解凍して以下に設置して、
Plugin/AclExtras

app/Config/boostrap.phpに追記。

CakePlugin::load('AclExtras');

そしてシェルからコマンド。

cake AclExtras.AclExtras aco_sync
cake AclExtras.AclExtras verify
// for help
//cake AclExtras.AclExtras aco_sync -h

これで各ルートの情報がacoテーブルにアップデートされる。
下層をアップデートするには、AclShellを使う。

下が詳しい。
http://book.cakephp.org/2.0/en/console-and-shells/acl-shell.html

例えば、以下のコマンドを打つ。
※もっと簡単な方法がないのだろうか。


// Users, Groups, Postsの配下。
cake acl create aco Users index
cake acl create aco Users add
cake acl create aco Users edit
cake acl create aco Users delete

cake acl create aco Groups index
cake acl create aco Groups add
cake acl create aco Groups edit
cake acl create aco Groups delete

...続く

5.パーミッションの設定。

userの挙動を制御するaros_acosテーブルのアップデートをおこなう。
ここではシェルを使わずに、AclComponent を使う。

パーミッションの設定の為、一時的に以下を追加する。

Controller/UsersController.php

public function beforeFilter() {
    parent::beforeFilter();
    $this->Auth->allow('initDB'); // We can remove this line after we're finished
}

public 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;
}

この状態で、一度ブラウザでusers/initdb へ行く。アクセス前にaros_acosが空な事を確認しよう。
でコメントしてる場合は、コメント解除してからアクセスしよう
アクセス後にaros_acos を見ると、テーブルが更新されていることがわかるはず。それを確認したら、initDBへのアクセスを外す。

この場合のパーミッションはこう。

■管理者(1)
・全てのページの操作・閲覧

■マネージャ(2)
・ポスト・ウィジェットの全ての操作・閲覧

■ユーザ(3)
・ポスト・ウィジェットの追加、編集

aclでindex, viewを指定しないことでこれらはpublicにされている。

次に、Posts,Widgetsは閲覧可能なので、それぞれのコントローラ内 beforeFilter()に以下を追加。

public function beforeFilter() {
    parent::beforeFilter();
    $this->Auth->allow('index', 'view');
}

更にAppController::beforeFilter() に以下を追加

$this->Auth->allow('display');

あと、先に追加したlogoutメソッドを埋める。

UsersController::logout() に、下記を追加。

$this->Session->setFlash('Good-Bye');
$this->redirect($this->Auth->logout());

これでチュートリアルが完成したと思う。
後は自分で好きなように変更していくだけ!