Qbilinux 日記

Linux に関係することだけではなく,最近は一般的なコンピュータやガジェット関係についても記載してます.

cakephp3 での Group ACL の設定方法

cakephp3 になって ACL 周りの説明が何もなくなっていますね.

検索して出てくるページの記述も説明文中で使われている plugin がなくなったりしているのでそのままでは使えないようですね.ちょっと不便なので自分の備忘録もかねてメモ代わりに説明を書いてみようかと思います.

ACL とはアクセスコントロールリストのことですね.下記は cakephp3 で Controller を Group ID ごとにアクセス制限したい場合の記述になります.ACL とは何ぞやなどの詳細については cakephp2 の cookbook などを参考にしてください.

ACL plugin に関してですが,詳細は確認していませんが,ぱっと見た感じ cakephp2 まであった Acl と AclExtras がマージされて,cakephp3 用には cakephp acl plugin として配布されているみたいですね.基本的に関数仕様などは変更されていないようなので,cakephp2 のチュートリアルを踏襲すれば設定できますが,若干の記述修正が必要です.

まず,cakephp acl plugin をインストール.ドキュメントどおり実行すればオッケーです.

composer.json ファイルに

"require": {
    "cakephp/acl": "dev-master"
}

を追加して

php composer.phar update

その後,config/bootstrap.php に下記を追加します.

Plugin::load('Acl', ['bootstrap' => true]);

次,acl 用のテーブルの作成.これも acl plugin のドキュメントどおりかな.下記コマンドを実行します.

bin/cake migrations migrate -p Acl

ここから acl plugin のページにはドキュメントがないですね.

えーっと,説明を省くために,通常通り,ユーザーのログイン,ログアウト,パスワードのエンコード処理などは設定されていて,実行できる状態になっている状態を想定します.今回は Group ID でアクセス制限をかけたいので,Users->Groups という依存関係があって,Users に group_id が設定されているとします.

この状態で src/Model/Entity/Group.php に下記を追加します.

public function parentNode()
{
    return null;
}

src/Model/Entity/User.php に下記を追加します.

public function parentNode()
{
    if (!$this->id) {
        return null;
    }
    if (isset($this->group_id)) {
        $group_id = $this->group_id;
    } else {
        $users_table = TableRegistry::get('Users');
        $user = $users_table->find('all', ['fields' => ['group_id']])->where(['id' => $this->id])->first();
        $group_id = $user->group_id;
    }
    if (!$group_id) {
        return null;
    }

    return ['Groups' => ['id' => $group_id]];

}

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

このあたりのコードは cakephp3 用に若干修正していますけど,cakephp2 のチュートリアルそのままです.

次,src/Model/Table/GroupsTable.php の initialize(array $config) に下記を追加.

$this->addBehavior('Acl.Acl', ['type' => 'requester']);
$this->hasMany('Users', [
    'foreignKey' => 'group_id'
]);

src/Model/Table/UsersTable.php に下記を追加.

$this->addBehavior('Acl.Acl', ['type' => 'requester']);
$this->belongsTo('Groups', [
    'className' => 'Groups',
    'foreignKey' => 'group_id',
    'joinType' => 'INNER'
]);

これも,ほとんど cakephp2 のチュートリアルそのままですね.

この状態で,Group 追加,User 追加を行って,aros テーブルが cakephp2 のチュートリアルのようになってることを確認します.

次,group id でパーミッションをかけたいので Auth->User での戻り値に group_id を含めておきたいと思います.なので,user の login 関数を少し細工して変更します.

public function login()
{
    if ($this->request->is('post')) {
        $user = $this->Auth->identify();
        if ($user) {
            $me = $this->Users->find()
                ->where(['id' => $user['id']])
                ->select(['id', 'username', 'group_id'])
                ->first()
                ->toArray();
            $this->Auth->setUser($me);
            return $this->redirect($this->Auth->redirectUrl());
        }
        $this->Flash->error('メールアドレスかパスワードが間違えています');
    }
}

もともとは $this->Auth->setUser($user) の記述になっていましたが,find 文を追加して group_id を含んだユーザー情報を $me として取り出して,setUser で $me を session に保存する形に書き換えてあります.これで session の Auth に group_id が含まれた形で保存されるようになります.もっと良い方法ないのかなぁ?あればどなたか教えてください....

ここまでで User 周り,Group 周りの設定は終了かな.

次,ACO 周り.

src/AppController.php の initialize() に下記を追加します.

$this->loadComponent('Acl.Acl');
$this->loadComponent('Auth', [
    'authorize' => [
    'Acl.Actions' => ['actionPath' => 'controllers/'],
    ]
]);

Auth 設定に 'loginRedirect' や 'logoutRedirect' などその他の設定が必要ならば,適宜追加をしてください.また,下記関数も追加します.

public function isAuthorized($user)
{
    $Collection = new ComponentRegistry();
    $acl = new AclComponent($Collection);
    $controller = $this->request->controller;
    $action     = $this->request->action;
    return $acl->check(['Users' => ['id' => $user['id']]], "$controller/$action");
}

で,acos db に controller 周りのデータを登録するため,コマンドラインから下記を実行します.

php bin/cake.php acl_extras aco_sync

その後,パーミッションの設定をします.Group id と controller を見比べながら,随時,コマンドラインから

php bin/cake.php acl grant Groups.1 controllers
php bin/cake.php acl deny Groups.2 controllers
php bin/cake.php acl grant Groups.2 controllers/Posts
php bin/cake.php acl grant Groups.2 controllers/Widgets
php bin/cake.php acl deny Groups.3 controllers
php bin/cake.php acl grant Groups.3 controllers/Posts/index
php bin/cake.php acl grant Groups.3 controllers/Posts/view
php bin/cake.php acl grant Groups.3 controllers/Widgets/index
php bin/cake.php acl grant Groups.3 controllers/Widgets/view

のように設定していきます.

controller を追加したり,group の変更などが入ってメンテナンスするには

php bin/cake.php acl_extras aco_update

などを使ってください.

php bin/cake.php acl_extras
php bin/cake.php acl

を実行すると acl 関係の様々なオプションの説明が出ますので必要なコマンドを適宜使ってください.まぁ,メンテナンスに関しては cakephp2 のときと変わらないと思います.実行コマンドを shell script などにまとめておくと便利かもしれないですね.

上記内容に関しては,https://github.com/mattmemmesheimer/cakephp-3-acl-example を少しだけ参考にさせていただきました.そこにはコードもあるのでそのコードも参考になるかもしれないです.私はコードの中身は見てないし,動作させてもないですけど....

ちょっと昔の記憶を頼りに書いている部分もありますので,間違っている箇所や記述の過不足などありましたら申し訳ありません.

以上,参考まで.