CakePHP ACL – アクセス制御リストを攻略する

ACLとは?

Authコンポーネントは、ユーザ認証を実現する非常にシンプルで簡単なコンポーネントですが、あくまでも「そのユーザがログインしているか否か」ということのみです。
それぞれのユーザに応じて各種機能にアクセス権を設定するためのコンポーネント、それが「ACL – アクセス制御リスト(Access Control List)」です。

さて、このACL、CookBookにもチュートリアルがありますが、はっきり言って、これを読んでも理解不能です。むしろ読まないほうがいいと思います。
これが、ACLが難解と言われる要因でしょうか。

ACLのイメージ

ACLは3つのデータベースから成り立っています。

ARO – ユーザ(ユーザやグループ)
ACO – 対象物(ページやアクション)
ARO_ACO – アクセス権の対応表

それぞれの関係を図で表すと、こんなイメージです。

要するに、ACLでは、ユーザ(ARO)と対象物(ACO)を用意して、その組み合わせにそれぞれアクセス権を設定している(ARO_ACO) と考えれば良いのです。

ACL導入の方針

CookBookのチュートリアルや、Cake関連の書籍を読むと、「ACO、ARO、ARO_ACO は、手動もしくは自作関数で構築せよ」と言われます。
出来ないことはありませんが、この方法はとても面倒で、構築し終わった後にぐったりしてしまうこと間違いなしです。
本記事でのACL導入の方針は、ACO、ARO、ARO_ACO を自動的に生成し、簡単にアクセス権の管理を行えることを目的とします。

ACL導入の準備

設定ファイルの修正

> app > config > core.php

以下のコードを有効(コメントを外す)にする。

Configure::write('Acl.classname', 'DbAcl');
Configure::write('Acl.database', 'default');

テーブルの作成(aros、acos、aros_acos、users、groups)

ユーザ、グループ単位でアクセス権の設定を行うのが望ましいので、ACLの3つのテーブルの他に、users、groups のテーブルも作成します。ここはさらっとSQL文を書いておきます。

CREATE TABLE `acos` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `parent_id` int(10) DEFAULT NULL,
  `model` varchar(255) DEFAULT NULL,
  `foreign_key` int(10) DEFAULT NULL,
  `alias` varchar(255) DEFAULT NULL,
  `lft` int(10) DEFAULT NULL,
  `rght` int(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

CREATE TABLE `aros` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `parent_id` int(10) DEFAULT NULL,
  `model` varchar(255) DEFAULT NULL,
  `foreign_key` int(10) DEFAULT NULL,
  `alias` varchar(255) DEFAULT NULL,
  `lft` int(10) DEFAULT NULL,
  `rght` int(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

CREATE TABLE `aros_acos` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `aro_id` int(10) NOT NULL,
  `aco_id` int(10) NOT NULL,
  `_create` varchar(2) NOT NULL DEFAULT '0',
  `_read` varchar(2) NOT NULL DEFAULT '0',
  `_update` varchar(2) NOT NULL DEFAULT '0',
  `_delete` varchar(2) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `ARO_ACO_KEY` (`aro_id`,`aco_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

CREATE TABLE `users` (
  `id` int(4) NOT NULL AUTO_INCREMENT,
  `username` varchar(128) NOT NULL,
  `password` varchar(128) NOT NULL,
  `group_id` int(4) NOT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

CREATE TABLE `groups` (
  `id` int(4) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

UserとgroupのMVCモデルを作成

ユーザを管理する User と、グループを管理する Group について、それぞれMVCモデルを作成します。
一覧表示、追加、編集、削除など、基本的な機能が揃えば、それで構いません。
Bakeでサクっと作ってしまうといいと思います。

Bakeについてはこちら→ CakePHP bakeによる高速開発

UserとgroupのMVCモデルが出来上がったら、

ACLビヘイビアで関連付け(arosとusers、arosとgroups)
UserモデルとGroupモデルで、parentNode()を作る
アソシエーションの設定(User belongsTo Group)

まとめると、こうなります↓

> Userモデル(user.php)

class User extends AppModel {
	var $name = 'User';
	var $belongsTo = array('Group');
	var $actsAs = array('Acl' => 'requester');
	
	function parentNode() {
		if (!$this->id && empty($this->data)) {
			return null;
		}
		$data = $this->data;
		if (empty($this->data)) {
			$data = $this->read();
		}
		if (!$data['User']['group_id']) {
			return null;
		} else {
			return array('Group' => array('id' => $data['User']['group_id']));
		}
	}
}

> Groupモデル(group.php)

class Group extends AppModel {
	var $name = 'Group';
	var $actsAs = array('Acl' => 'requester');
	
	function parentNode() {
		return null;
	}
}

Authコンポーネント、ログイン機能の追加(app_controller.php)

ログイン機能を作成して、

コントローラのパスを設定する(app_controlle.php)
外部オブジェクトを指定する(app_controlle.php)

まとめると、こうなります↓

> app_controlle.php

class AppController extends Controller {
	var $components = array('Auth', 'Acl');
	
	function beforeFilter() {
		
		$this->Auth->allow('*');

		$this->Auth->actionPath = 'controllers/';
		$this->Auth->authorize = 'actions';
		
		// 以下必要に応じてAuthコンポーネントのメソッドを設定してください
		
		$this->Auth->authError = 'ログインしてください';
		$this->Auth->loginError = 'ログインに失敗しました。';
		
		$this->Auth->loginRedirect = array('controller' => '', 'action' => '');
		$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
	}
}

まだグループもユーザもなにも登録していないので、一時的にすべてのアクションについて、認証なしでアクセスできるようにしておきます。

グループとユーザを追加

作成した UserとGroupのMVCモデルのフォームから、グループとユーザを追加します。

グループ – admin, member
ユーザ – admin_user1(adminグループ), member_user1(memberグループ)

usersテーブルトとgroupsテーブルにデータが追加されるのに加えて、arosテーブルにも、以下のように自動的にデータが追加されているのを確認してください。

プラグイン「Plugin ACL」の導入

ACLを超簡単にする素敵なプラグインを導入します。

プラグイン:Plugin ACL

導入

・ダウンロードした最新の「alaxos_acl_x.x.x.zip」の中にある「acl」フォルダを app/plugins に入れる。

・adminルーティングの設定

> app/config/core.php

88行目の以下のコードを有効(コメントを外す)にする。

Configure::write('Routing.prefixes', array('admin'));

・bootstrap.phpの編集

app/plugins/acl/config/bootstrap.php のソースコードをコピーして、app/config/bootstrap.php に追記する。

今回は “Group” を使うので、以下の部分を変更しておきます。

/*
 * The model name used for the user role (typically 'Role' or 'Group')
 */
Configure :: write('acl.aro.role.model', 'Group');

/*
 * The foreign key's name for the roles
 *
 * (can be left empty if your foreign key's name follows CakePHP conventions)(e.g. 'role_id')
 */
Configure :: write('acl.aro.role.foreign_key', 'group_id');

操作方法

ドメイン/admin/acl にアクセスします。

以下のエラーが表示されていたら、「Build missing ACOs」をクリックします。
すでに存在しているアクション(User、Groupの追加、編集、削除など)が、ACOに自動的に追加されます。

Some controllers have been modified, resulting in actions that are not referenced as ACO in the database. :

・操作方法

> Actions

Build actions ACOs – ACOの自動生成
Clear actions ACOs – ACOのクリア

> Permissions

Build missing AROs – AROの自動生成
Users roles – ユーザがどのグループに属するか
Roles permissions – ARO(グループ)のACOに対するアクセス権
Users permissions – ARO(ユーザ個別)のACOに対するアクセス権

アクセス権の設定

アクセス権の設定

では、先ほど作ったグループ(ARO)について、以下のようにアクセス権を設定します。

admin – すべてのアクションへのアクセスを許可
member – ユーザ一覧画面(Users->index)へのアクセスのみ許可

ACLプラグインの画面では、以下のような設定になるはずです。(Permissions > Roles permissions)

動作確認

動作確認の前に、すでにグループとユーザを登録して、アクセス権の設定を行いましたので、app_controller.php から、$this->Auth->allow(‘*’); を削除しておきます。

まず、adminグループに属するユーザ(admin_user1)でログインして、すべてのアクションへのアクセスが可能なことを確認してください。
そして今度は、memberグループに属するユーザ(member_user1)で再度ログインしてみてください。
ユーザ一覧画面にはアクセスできるが、それ以外のページやアクションにはアクセスできなくなっていれば、完璧ですッ!

認証なしでアクセス可能なアクションの設定

ご質問をいただいたので追記します。(2012/04/05)
認証なしでアクセス可能なアクションの設定は、Authコンポーネントのメソッドで指定します。

例えば、usersコントローラのindexアクションとaddアクションを認証なしでアクセス可能にする場合

> users_controller.php

function beforeFilter() {
	$this->Auth->allow('index', 'add');
}

Comments

  • […] CakePHP ACL ? アクセス制御リストを攻略する […]

  • […]   参考:http://w.builwing.info/2012/08/02/cakephp2-2%E3%81%AEacl%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E7%AE%A1%E7%90%86%EF%BC%88%E5%AE%9F%E8%B7%B5%E7%B7%A8%E3%81%9D%E3%81%AE10%EF%BC%89/ http://hijiriworld.com/web/cakephp-acl-%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E5%88%B6%E5%BE%A1%E3%83%… […]

  • […] CakePHP ACL – アクセス制御リストを攻略する | hijiriworld Web. […]

  • Shiwi_sye より:

    MacなのでBakeが使えないのですが(ターミナル)Bake後のそれぞれのソースはないでしょうか?

  • […] ここでACL(Access Control List)というコンポーネントを使えば、アクセスを制御出来る訳ですが、これがややこしい! ACL — Cookbook v2.x documentation そこでACLを攻略するという記事を見つけたのですが、やっぱり管理者と一般ユーザーで分けるのには向かない気がします。 […]

  • Mogamogamon より:

    何度も申し訳ありません。
    ACLの制御画面で、Roles permissionsで、すべての項目がloadingになってしまい、待っても表示されない原因は何が考えられるでしょうか。
    以前は、ちゃんと表示されました。
    ページ数が増えたので、ACOをプラグイン上からクリアし、またビルドしたのですが・・・。
    大変申し訳ないのですが、教えていただければ助かります。

    • hijiriworld より:

      Ajaxの読み込みが遅いということになるでしょうか。
      サーバ側かクライアント側のどちらに原因があるのか切り分ける必要がありそうです。
      どちらも正常な状態であれば、このAjaxが動かないということは考えにくいと思います。
      回答になっておらず、すみません。。

      • Mogamogamon より:

         返信ありがとうございます。
        もしかしたらなんですが、cakePHP1.3を使っていることも原因にあるのかな?と思いました。
        サーバ側もクライアント側も正常な状態ですので・・・。

        • hijiriworld より:

          同様の現象が再現されたら原因を探ってみますね。
          もしなんらかの事実が判明したら共有していただけると助かります^^

    • Hogehoge より:

      私も「Roles permissionsで、すべての項目がloadingになってしまい、待っても表示されない」と同じ事象がありましたが、以下のリンク先の設定をし直したらうまく動きました。
      もしかしたら、cakephp、plugin ACLのバージョンなどが合っていなかったのかもしれません。

      同じ事象が発生している方がいるかもしれないので一応貼っておきますね。
      http://takahashiyuya.hatenablog.com/entry/20120301/p1

  • Mogamogamon より:

     こちらのページを拝見し、プラグインの導入が出来ました。
    そこで相談なんですが、1ページだけオールユーザーに見えるようにする方法はございますでしょうか?
    お知恵をお借りしたいです。

    • Mogamogamon より:

       すみません、説明不足でした。
      あるページだけ、ログインなしでも見れるようにしたいのです。

      それと、先に書くべきでしたが、プラグインの導入が難しくて悩んでいたところ、こちらのサイトを拝見し、導入することができました。
      ありがとうございます。

      • hijiri より:

        コメントありがとうございます。

        例えば、ユーザ一覧ページ(コントローラ: users、アクション: index)と、ユーザ追加ページ(コントローラ: users、アクション: add)を、認証なしでアクセス可能にする場合は、ACL設定後、usersコントローラに以下のように記述します。

        function beforeFilter() {	$this->Auth->allow('index', 'add');}
        
        • Mogamogamon より:

           ありがとうございます。
          早速実装してみましたら、うまくいきました。
          本当にありがとうございます!

  • Sai より:

     分かりやすい紹介ありがとうございます。認証後、memberが全ページ見えてしまいますが、どうしたら良いのでしょうか??

    • hijiri より:

      > Sai さん
      コメントありがとうございます。ロールのパーミッションとユーザのパーミッションに相違がないか確認してみてください。ユーザ毎のパーミッションは「Permissions > User permissions」 から確認できます。ロールとユーザのパーミッションの相違が発生する条件としては、「ACL設定→ロールおよびユーザの作成」の後に、ACLの設定を変更したり、データベーステーブルの値を直に変更したりした場合などが考えられます。相違が発生したデータを手動で修正するのは困難なので、その場合は、ロールおよびユーザを一度削除し、「ACL設定→ロールおよびユーザの作成」の手順に従って、再度作成すれば解消されると思います。

      • Sai より:

         コメントありがとうございます。教えていただいた通り、ACLの設定をもう一度やり直したら、おかげさまでパーミッションがかかりました。ただ、下の画像のように○や×が出なくなり、困っています。よろしかったら原因を教えていただけますか。よろしくお願い致します。

  • […] サンプルで作成するWebアプリケーションは、「CakePHP ACL ? アクセス制御リストを攻略する」の記事の中で「bakeしてね」で済ませてしまった、ユーザ管理部分を作りたいと思います。 […]

コメントを残す