Help us understand the problem. What is going on with this article?

CakePHP3のACLでアクセス管理

More than 1 year has passed since last update.

環境

  • PHP 5.5.24
  • CakePHP 3.0.11

やりたいこと

  • CakePHP3でログイン管理する(Auth)

  • CakePHP3でロール毎に権限を管理する(ACL)

アプリケーションの準備

  1. CakePHP3をインストールする。config/app.phpのDBを設定する

  2. ユーザーとグループのテーブルを作成する

  3. usersとgroupsをbake allする

プラグインをインストール

  • インストールするプラグイン

  • composer.jsonに「"cakephp/acl": "dev-master"」「"jcpires/cakephp3-aclmanager": "dev-master"」を追記しcomposer updateする

    "require": {
        "php": ">=5.4.16",
        "cakephp/cakephp": "~3.0",
        "mobiledetect/mobiledetectlib": "2.*",
        "cakephp/migrations": "~1.0",
        "cakephp/plugin-installer": "*",
+       "cakephp/acl": "dev-master",
+       "jcpires/cakephp3-aclmanager": "dev-master"
    },

Configの設定

  • config/bootstrap.phpの適当な箇所に「Plugin::load('Acl', ['bootstrap' => true]);」を追記する

Controllerの設定

AppController.php

<?php
namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Controller\ComponentRegistry;
use Cake\Event\Event;
use Acl\Controller\Component\AclComponent;

class AppController extends Controller
{
    public function beforeFilter(Event $event)
    {
        // グループ、ユーザー登録後コメントアウトする
        $this->Auth->allow();
    }

    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Acl.Acl');
        $this->loadComponent('Flash');
        $this->loadComponent('Auth', [
            'authorize' => 'Controller',

            // 権限無しページに飛ぶと無限ループになったり、変なURLにリダイレクトされるのを防ぐ
            'unauthorizedRedirect' => false,

            'authError' => 'アクセス権限がありません',

            //'loginRedirect' => [
            //    'controller' => 'Users',
            //    'action' => 'index'
            //],
        ]);
    }

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

}

GroupsController.php

  • 下記を追記
use Cake\Event\Event;
use JcPires\AclManager\Event\PermissionsEditor;
    public $helpers = [            
        'AclManager' => [
            'className' => 'JcPires/AclManager.AclManager'
        ]
    ];

    public function add()
    {
        $group = $this->Groups->newEntity();
        if ($this->request->is('post')) {
            $group = $this->Groups->patchEntity($group, $this->request->data);
            if ($this->Groups->save($group)) {
                // デフォルトアクセス全許可にしたい場合は下記のコメントアウトを外す
                //if (isset($this->request->data['parent_id'])) {
                //    $parent = $this->request->data['parent_id'];
                //} else {
                //    $parent = null;
                //}
                //$this->eventManager()->on(new PermissionsEditor());
                //$perms = new Event('Permissions.addAro', $this, [
                //    'Aro' => $group,
                //    'Parent' => $parent,
                //    'Model' => 'Groups'
                //]);
                //$this->eventManager()->dispatch($perms);

                $this->Flash->success(__('The group has been saved.'));
                return $this->redirect(['action' => 'index']);
            } else {
                $this->Flash->error(__('The group could not be saved. Please, try again.'));
            }
        }
        $this->set(compact('group'));
        $this->set('_serialize', ['group']);
    }

    public function edit($id = null)
    {
        $group = $this->Groups->get($id, [
            'contain' => []
        ]);

        $this->loadComponent('JcPires/AclManager.AclManager');
        $EditablePerms = $this->AclManager->getFormActions();

        if ($this->request->is(['patch', 'post', 'put'])) {
            $group = $this->Groups->patchEntity($group, $this->request->data);
            if ($this->Groups->save($group)) {

                $this->eventManager()->on(new PermissionsEditor());
                $perms = new Event('Permissions.editPerms', $this, [
                    'Aro' => $group,
                    'datas' => $this->request->data
                ]);
                $this->eventManager()->dispatch($perms);

                $this->Flash->success(__('The group has been saved.'));
                return $this->redirect(['action' => 'index']);
            } else {
                $this->Flash->error(__('The group could not be saved. Please, try again.'));
            }
        }
        $this->set(compact('group', 'EditablePerms'));
        $this->set('_serialize', ['group', 'EditablePerms']);
    }

UsersController.php

  • 下記を追記
use Cake\Event\Event;
    public function beforeFilter(Event $event)
    {
        parent::beforeFilter($event);
        $this->Auth->allow(['login']);
    }

    public function login()
    {
        if ($this->request->is('post')) {
            $user = $this->Auth->identify();

            if ($user) {
                $this->Auth->setUser($user);
                return $this->redirect($this->Auth->redirectUrl());
            }
            $this->Flash->error(__('Invalid username or password, try again'));
        }
    }

    public function logout()
    {
        return $this->redirect($this->Auth->logout());
    }

Modelの設定

Entity/Group.php

  • 下記を追記
    public function parentNode()
    {
        return null;
    }

Entity/User.php

  • 下記を追記
use Cake\ORM\TableRegistry;
use Cake\Auth\DefaultPasswordHasher;
    protected function _setPassword($password)
    {
        return (new DefaultPasswordHasher)->hash($password);
    }

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

Table/UsersTable.php

  • public function initialize内に追記
        $this->addBehavior('Acl.Acl', ['type' => 'requester']);

Table/GroupsTable.php

  • public function initialize内に追記
        $this->addBehavior('Acl.Acl', ['type' => 'requester']);

Templateの設定

Groups/edit.ctp

  • Form内に下記を追記
        <?php foreach ($EditablePerms as $Acos) :?>
            <?php foreach ($Acos as $controllerPath => $actions) :?>
                <?php if (!empty($actions)) :?>
                    <h4><?= $controllerPath ;?></h4>
                    <?php foreach ($actions as $action) :?>
                        <?php ($this->AclManager->checkGroup($group, 'App/'.$controllerPath.'/'.$action)) ? $val = 1 : $val = 0 ?>
                        <?= $this->Form->label('App/'.$controllerPath.'/'.$action, $action);?>
                        <?= $this->Form->select('App/'.$controllerPath.'/'.$action, [0 => 'No', 1 => 'Yes'], ['value' => $val]) ;?>
                    <?php endforeach ;?>
                <?php endif;?>
            <?php endforeach ;?>
        <?php endforeach ;?>

Users/login.ctp

  • 新規作成
<div class="users form">
<?= $this->Flash->render('auth') ?>
<?= $this->Form->create() ?>
    <fieldset>
        <legend><?= __('Please enter your username and password') ?></legend>
        <?= $this->Form->input('username') ?>
        <?= $this->Form->input('password') ?>
    </fieldset>
<?= $this->Form->button(__('Login')); ?>
<?= $this->Form->end() ?>
</div>

ACLプラグインの修正

  • /vendor/cakephp/acl/src/AclExtras.phpのrootNodeの値をAppに変更する
+    public $rootNode = 'App';
-    public $rootNode = 'controllers';

ACLテーブルの作成

   $ bin/cake Migrations.migrations migrate -p Acl

ACOデータの作成

   $ bin/cake acl_extras aco_sync

グループとユーザーを作成

  1. 「http://アプリ名/groups/add」にアクセスしてグループを追加する
  2. 「http://アプリ名/groups/edit/1」にアクセスして権限を設定する
  3. 「http://アプリ名/users/add」にアクセスしてユーザーを追加する

動作確認

  1. 「http://アプリ名/users/logout」にアクセスしてログアウトする
  2. AppController.phpの「$this->Auth->allow();」をコメントアウトする
  3. 「http://アプリ名/」にアクセスしてログイン画面にリダイレクトされるか確認する
  4. 登録したユーザでログインし権限通りのACLができているか確認する

補足

  • 新しくControllerやActionを追加した場合は下記のコマンドで更新する
   $ bin/cake acl_extras aco_update
HorieH
自分用のメモが多い ReactNativeでゲーム作ってます 猫が好き 履歴: Perl→PHP(CakePHP2,3)→AWS(ソリューションアーキテクト)、Lambda(Python)→JS(React/ReactNative/Redux)
https://horieapp.firebaseapp.com
admin-guild
「Webサービスの運営に必要なあらゆる知見」を共有できる場として作られた、運営者のためのコミュニティです。
https://admin-guild.slack.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away