CakePHP2 ドキュメントの「グループだけのACL」を検証しました♪軽くですけれども。

ACLを制御するシンプルなアプリケーション – CakePHP Cookbook v2.x documentation のグループだけのACL に書かれている関数を User モデルに追加すると、ユーザー単位ではなく、グループ単位の ACL とできる、とあります。

しかし、この関数をつけてもつけなくても「 aros テーブルは以下のようになるでしょう」とあるようなテーブル構造になりません。ナンデ?ウェブからユーザーを登録しても、aros テーブルにデータが登録されてしまいます。ナンデ?

そこで、この状態ですこしだけ検証してみましたのでメモします。

検証内容

  • グループだけのACL にある関数のあるなしで、任意のページにアクセスしたときに発行される SQL の違いを見ます。
  • グループだけのACL にある関数のあるなしで、MySQL に直接 INSERT したユーザーのアクセス権がどうなるか、違いを見ます。

検証するのにそもそも CakePHP でサイトを作る必要がありますが、それは公式ドキュメントのチュートリアルを手順どおりに進めて作成したものを使用しました。つまり、次の2ページをこなしてからのスタートとなります。

  1. ACLを制御するシンプルなアプリケーション — CakePHP Cookbook v2.x documentation
  2. ACLを制御するシンプルなアプリケーション – パート2 — CakePHP Cookbook v2.x documentation

グループだけのACL にある関数のあるなしで、任意のページにアクセスしたときに発行される SQL の違いを見ます。

User コントローラーの index アクションにアクセスしたときに発行される SQL で確認しました。URL を一応張っておくと、http://localhost/cakesample4/users となりました。

グループだけのACLじゃない場合は4つ、グループだけのACLの場合は3つの SQL が発行されていました。早速違いありますね!

1つ目

グループだけのACLじゃない場合

SELECT `Aro`.`id`, `Aro`.`parent_id`, `Aro`.`model`, `Aro`.`foreign_key`, `Aro`.`alias` FROM `sampledb4`.`aros` AS `Aro` LEFT JOIN `sampledb4`.`aros` AS `Aro0` ON (`Aro`.`lft` <= `Aro0`.`lft` AND `Aro`.`rght` >= `Aro0`.`rght`) WHERE `Aro0`.`model` = 'User' AND `Aro0`.`foreign_key` = 1 ORDER BY `Aro`.`lft` DESC;

+----+-----------+-------+-------------+-------+
| id | parent_id | model | foreign_key | alias |
+----+-----------+-------+-------------+-------+
|  4 |         1 | User  |           1 | NULL  |
|  1 |      NULL | Group |           1 | NULL  |
+----+-----------+-------+-------------+-------+

グループだけのACLの場合

SELECT `Aro`.`id`, `Aro`.`parent_id`, `Aro`.`model`, `Aro`.`foreign_key`, `Aro`.`alias` FROM `sampledb4`.`aros` AS `Aro` LEFT JOIN `sampledb4`.`aros` AS `Aro0` ON (`Aro`.`lft` <= `Aro0`.`lft` AND `Aro`.`rght` >= `Aro0`.`rght`) WHERE `Aro0`.`model` = 'Group' AND `Aro0`.`foreign_key` = 1 ORDER BY `Aro`.`lft` DESC;

+----+-----------+-------+-------------+-------+
| id | parent_id | model | foreign_key | alias |
+----+-----------+-------+-------------+-------+
|  1 |      NULL | Group |           1 | NULL  |
+----+-----------+-------+-------------+-------+

SQL の違いとしては、WHERE 区でした。

  • グループだけじゃない → ` Aro0 ` . ` model ` = ‘User’
  • グループだけ → ` Aro0 ` . ` model ` = ‘Group’

取得結果の違いは model が User の行がグループだけじゃない、にあることでした。これは大きな違いだと思います。つまり、 model が User の行があることで、ユーザー単位の ACL を有効にしている、そう考えることができます。

2つ目

グループだけのACLじゃない場合も、グループだけのACLの場合も、次のようにまったく同じでした。

SELECT `Aco`.`id`, `Aco`.`parent_id`, `Aco`.`model`, `Aco`.`foreign_key`, `Aco`.`alias` FROM `sampledb4`.`acos` AS `Aco` LEFT JOIN `sampledb4`.`acos` AS `Aco0` ON (`Aco0`.`alias` = 'controllers') LEFT JOIN `sampledb4`.`acos` AS `Aco1` ON (`Aco1`.`lft` > `Aco0`.`lft` AND `Aco1`.`rght` < `Aco0`.`rght` AND `Aco1`.`alias` = 'Users' AND `Aco0`.`id` = `Aco1`.`parent_id`) LEFT JOIN `sampledb4`.`acos` AS `Aco2` ON (`Aco2`.`lft` > `Aco1`.`lft` AND `Aco2`.`rght` < `Aco1`.`rght` AND `Aco2`.`alias` = 'index' AND `Aco1`.`id` = `Aco2`.`parent_id`) WHERE ((`Aco`.`lft` <= `Aco0`.`lft` AND `Aco`.`rght` >= `Aco0`.`rght`) OR (`Aco`.`lft` <= `Aco2`.`lft` AND `Aco`.`rght` >= `Aco2`.`rght`)) ORDER BY `Aco`.`lft` DESC;

+----+-----------+-------+-------------+-------------+
| id | parent_id | model | foreign_key | alias       |
+----+-----------+-------+-------------+-------------+
| 19 |        16 | NULL  |        NULL | index       |
| 16 |         1 | NULL  |        NULL | Users       |
|  1 |      NULL | NULL  |        NULL | controllers |
+----+-----------+-------+-------------+-------------+

3つ目

グループだけのACLじゃない場合

SELECT `Permission`.`id`, `Permission`.`aro_id`, `Permission`.`aco_id`, `Permission`.`_create`, `Permission`.`_read`, `Permission`.`_update`, `Permission`.`_delete`, `Aro`.`id`, `Aro`.`parent_id`, `Aro`.`model`, `Aro`.`foreign_key`, `Aro`.`alias`, `Aro`.`lft`, `Aro`.`rght`, `Aco`.`id`, `Aco`.`parent_id`, `Aco`.`model`, `Aco`.`foreign_key`, `Aco`.`alias`, `Aco`.`lft`, `Aco`.`rght` FROM `sampledb4`.`aros_acos` AS `Permission` LEFT JOIN `sampledb4`.`aros` AS `Aro` ON (`Permission`.`aro_id` = `Aro`.`id`) LEFT JOIN `sampledb4`.`acos` AS `Aco` ON (`Permission`.`aco_id` = `Aco`.`id`) WHERE `Permission`.`aro_id` = 4 AND `Permission`.`aco_id` IN (19, 16, 1) ORDER BY `Aco`.`lft` desc;

Empty set (0.00 sec)

グループだけのACLの場合

こちらには対応する SQL はありませんでした。これも大きな違いですが、グループだけじゃない場合で結果が取得できていないので、どういう意味があるのか、よくわかりません。残念。たぶん別のページやユーザーで同じように調べれば何かわかるのかもしれませんが、面倒ですので今はそこまで掘り下げません。

4つ目

こちらもグループだけのACLじゃない場合も、グループだけのACLの場合も、次のようにまったく同じでした。

SELECT `Permission`.`id`, `Permission`.`aro_id`, `Permission`.`aco_id`, `Permission`.`_create`, `Permission`.`_read`, `Permission`.`_update`, `Permission`.`_delete`, `Aro`.`id`, `Aro`.`parent_id`, `Aro`.`model`, `Aro`.`foreign_key`, `Aro`.`alias`, `Aro`.`lft`, `Aro`.`rght`, `Aco`.`id`, `Aco`.`parent_id`, `Aco`.`model`, `Aco`.`foreign_key`, `Aco`.`alias`, `Aco`.`lft`, `Aco`.`rght` FROM `sampledb4`.`aros_acos` AS `Permission` LEFT JOIN `sampledb4`.`aros` AS `Aro` ON (`Permission`.`aro_id` = `Aro`.`id`) LEFT JOIN `sampledb4`.`acos` AS `Aco` ON (`Permission`.`aco_id` = `Aco`.`id`) WHERE `Permission`.`aro_id` = 1 AND `Permission`.`aco_id` IN (19, 16, 1) ORDER BY `Aco`.`lft` desc;

+----+--------+--------+---------+-------+---------+---------+------+-----------+-------+-------------+-------+------+------+------+-----------+-------+-------------+-------------+------+------+
| id | aro_id | aco_id | _create | _read | _update | _delete | id   | parent_id | model | foreign_key | alias | lft  | rght | id   | parent_id | model | foreign_key | alias       | lft  | rght |
+----+--------+--------+---------+-------+---------+---------+------+-----------+-------+-------------+-------+------+------+------+-----------+-------+-------------+-------------+------+------+
| 10 |      1 |      1 | 1       | 1     | 1       | 1       |    1 |      NULL | Group |           1 | NULL  |    1 |    4 |    1 |      NULL | NULL  |        NULL | controllers |    1 |  116 |
+----+--------+--------+---------+-------+---------+---------+------+-----------+-------+-------------+-------+------+------+------+-----------+-------+-------------+-------------+------+------+

振り返ると

aros テーブルから取得するデータに model が User の行がある(グループだけのACLじゃない場合)かないかが違いでした。

次の検証に行きましょう。

グループだけのACL にある関数のあるなしで、MySQL に直接 INSERT したユーザーのアクセス権がどうなるか、違いを見ます。

SQL 文で直接  INSERT するユーザーは次のようにしました。

私の場合、SQL 文は次のようになりました。各環境で app/Config/core.php の Security.salt 値が異なるでしょうから、INSERT 文も違うものになります。

INSERT INTO `users` (`id`, `username`, `password`, `group_id`, `created`, `modified`) VALUES (4, 'inoue', '8e6a59da616035715aa7bc4b25bd461b5866549e', 1, NULL, NULL);

これで準備が整いました。

グループだけのACLじゃない場合

  • ログイン画面や $this->Auth->allow で許可しているページにはアクセスできました。つまり認証不要のページにはアクセス可能です。
  • ログイン後に ACL を設定しているページにアクセスすると、エラーとなりました。
    上の1つ目のSQL  を発行したときに「そんな行ないよ!」って言われました。無理やり登録した報いですね。インガオホー。
    エラーの原文はこれです。「AclNode::node() – Couldn’t find Aro node identified by “Array ( [Aro0.model] => User [Aro0.foreign_key] => 4 ) “」
    ちなみに問題となった User [Aro0.foreign_key] => 4 の「4」は users テーブルの id に当たります

 グループだけのACLの場合

  •  ログイン画面や $this->Auth->allow で許可しているページには、こちらの場合もアクセスできました。
  • ログイン後に ACL を設定しているページにアクセスすると、グループだけの ACL の場合は、何の問題もなくアクセスできました。アクセスだけでなく、データの追加、削除も問題なく行えました。

ということで、グループだけの ACL の場合は aros テーブルの model カラムの、すでに登録されている Group だけ見るのでエラーになりません。よって強引にCakePHP の ACL をかませたウェブ以外から登録しても問題が起きません。

データとしては、気持ち悪いですけどね。

終わりに、そもそも

ACL をはじめるにあたり、他の方のやってみたこと、やり方などを参考にさせていただいた中で、こちらのページでも、訝しがっておられたのがきっかけでした。

引用させていただきますと、次のようになります。

パーミッションをグループのみに単純化する
ACLによるAROのチェックをグループのみにします。

「app/Model/User.php」に追加

function bindNode($user) {
return array(‘model’ => ‘Group’, ‘foreign_key’ => $user[‘User’][‘group_id’]);
}
※これ追加するだけで出来るらしいけど、なぜかうまくいかないんですよねー。特に問題はないからいいんですが(;´Д`)誰かおしえてください、あーモヤモヤする

こちらのページ、ACL の使い方の勉強に、とても参考にさせていただきました。ありがとうございます。

次の課題としてぱっと思いついたのは、ACL を使用した状態で、一度に多くのユーザーを登録するのに SQL を直に使うことができないのでどうするか?という課題です。これを調べてみるのは面白そうです。

ディスカッションに参加

2件のコメント

  1. 同じ疑問でモヤモヤしていたので参考になりました。いろいろ検索した結果、似たような質問&回答らしきものがありましたのでシェアします(間違っていたらコメントごと削除して下さい…)。

    ■URL
    http://stackoverflow.com/questions/4003565/cakephp-acl-how-to-get-group-only-permissions

    ■回答抜粋
    > I assume that in order to realize your solution just remove the acts_as and parent_node declaration in your user model.
    # UserモデルのactsAsとparentNodeを消すだけ?

コメントを残す

コメントを残す