LoginSignup
43

More than 5 years have passed since last update.

CakePHP3のACLでアクセス管理

Last updated at Posted at 2015-08-11

環境

  • 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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43