Authコンポーネントを利用して、ユーザグループごとに利用できるアクションを割り当てる方法をご紹介します。 ※簡易ACLって感じで。

できること

  • ユーザグループごとに各アクションの利用可否を設定
  • アクションの認可情報は、コントローラ内に記述
  • 認証ユーザごとにログイン後の遷移先を変更(おまけ2)

(この方法、どこかの記事を参考にさせていただいたのですが参考元がわからなくなりました。。)

ここで紹介したソースをまとめたものはこちら

GroupテーブルとUserテーブルはいたって普通にこんな感じ。

CREATE  TABLE IF NOT EXISTS `groups` (
  `id` CHAR(36) NOT NULL ,
  `created` DATETIME NULL ,
  `modified` DATETIME NULL ,
  `name` VARCHAR(255) NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci;
CREATE  TABLE IF NOT EXISTS `users` (
  `id` CHAR(36) NOT NULL ,
  `created` DATETIME NULL ,
  `modified` DATETIME NULL ,
  `username` VARCHAR(50) NOT NULL ,
  `password` VARCHAR(40) NOT NULL ,
  `group_id` CHAR(36) NOT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci;

userモデルは、blongToを付け加えてgroupを引っ張ってこれるようにしておきます。

< ?php
class User extends AppModel {

    var $name = 'User';

	var $belongsTo = array(
		'Group' => array(
			'className' => 'Group',
			'foreignKey' => 'group_id',
			'conditions' => '',
			'fields' => '',
			'order' => ''));

}

[ad]

app_controller.phpに、beforeFilter、isAuthorizedメソッドを追加します。

< ?php
class AppController extends Controller
{
    public $components = array('Auth');

    /**
     * アクションのアクセスコントロール用
     *   array(
     *     'action' => '*', // すべてのグループで利用可能
     *     'action' => array('group1', 'group2') // group1とgroup2で利用可能
     *   )
     *
     * @var array
     */
    public $permissions = array();

    /**
     * @var AuthComponent
     */
    public $Auth;

    /**
     *
     * @var SessionComponent
     */
    var $Session;

    /**
     * (non-PHPdoc)
     * @see cake/libs/controller/Controller#beforeFilter()
     */
    function beforeFilter()
    {
        // Authコンポーネントの基本設定
        $this->Auth->fields = array('username' => 'username', 'password' => 'password'); // 認証に使うフィールドを指定
        $this->Auth->loginError = __('ログインに失敗しました。IDまたはパスワードが不正です。', true);
        $this->Auth->authError  = __('閲覧権限がありません。', true);
        $this->Auth->authorize = 'controller'; // 権限があるかどうかをAppController::isAuthorized()を使ってチェック

        if($this->isAdmin()){
            // adminルーティングの場合の処理
            $this->layout = 'admin'; // レイアウトを変更
            $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login', 'admin' => 'admin'); // loginアクションを UsersController::admin_login()にする
            $this->Auth->loginRedirect = '/admin/'; // ログイン後のデフォルト遷移先
        }

    }

    /**
     * Check Admin routing
     * @return boolean
     */
    function isAdmin()
    {
        return Configure::read('Routing.admin') && !empty($this->params['admin']);
    }

    /**
     * (non-PHPdoc)
     * @see cake/libs/controller/Controller#isAuthorized()
     */
    function isAuthorized()
    {
        // - group base auth
        $group = $this->Auth->user('group');
        $action = $this->action;
        if (!empty($this->permissions[$action])) {
            $permission = $this->permissions[$this->action];
            if (is_scalar($permission)) {
                $permission = array($permission);
            }
            if ($permission[0] == '*') {
                return true;
            }
            if (in_array($group, $permission)) {
                return true;
            }
        }

        return false;
    }
}

AppController::beforeFilter()ではAuthComponentのパラーメータを調節し、adminルーティングが行われている場合は、管理ページ側レイアウトへ変更するようにしています。

AppController::isAuthorized()では、認証されたユーザのグループと各アクションに割り振られた権限を比較してアクセスコントロールを行います。

各アクションが、どのユーザグループで利用可能かを定義するため、AppController::$permissions変数を追加しています。各コントローラではこの変数をオーバライドして、ユーザグループごとにアクションの利用を許可します。

[ad]

ログイン処理を行う、UsersControllerはこんな感じ。

< ?php
class UsersController extends AppController {

    var $name = 'Users';

    var $uses = array('User');

    /**
     *
     * @var User
     */
    var $User;

    /**
     * (non-PHPdoc)
     * @see cake/libs/controller/Controller#beforeFilter()
     */
    public function beforeFilter()
    {

        parent::beforeFilter();

        // 認証なしでアクセスできるアクション
        $this->Auth->allow('login', 'admin_login');

        // 表示に権限が必要なアクション
        $this->permissions = array(
            'admin_index' => ACL::$ACL_CLIENT, // 例えば、ユーザ一覧の表示は管理者とクライアントが可能
            'admin_edit' =>  ACL::$ACL_ADMIN // 例えば、ユーザ一覧の編集はは管理者グループのみ可能
        );

        // redirectを抑制
        if (in_array($this->action, array('login', 'admin_login'))) {
            $this->Auth->autoRedirect = false;
        }

    }

    /**
     * コントロールパネル クライアント一覧
     */
    public function admin_index()
    {
        // ごにょごにょ
    }

    /**
     * コントロールパネル クライアント編集
     */
    public function admin_edit()
    {
        // ごにょごにょ
    }
    
    /**
     * コントロールパネル ログイン処理
     */
    public function admin_login()
    {
        // ログイン処理は共通メソッド
        $this->_login();
    }

    /**
     * コントロールパネル ログアウト処理
     */
    public function admin_logout()
    {
        // ログアウト処理は共通メソッド
        $this->_logout();
        $this->redirect(array('action' => 'login', 'admin' => true));
    }

    /**
     * ログイン処理(一般ユーザ側)
     */
    public function login()
    {
        // ログイン処理は共通メソッド
        $this->_login();
    }

    /**
     * ログアウト処理(一般ユーザ側)
     */
    public function logout()
    {
        $this->_logout();
        $this->redirect(array('action' => 'login'));
    }
    
    /**
     * ログイン処理(共通)
     */
    protected function _login()
    {
        // ログインしていれば
        if ($this->Auth->user()) {
            // グループを取得
            $group = $this->User->find('first', array('conditions' => array('User.id' => $this->Auth->user('id')), 'recursive' => 0));
            // Authセッションにグループ名を追加
            $this->Session->write($this->Auth->sessionKey . '.group', $group['Group']['name']);
            // 画面を遷移
            $this->redirect($this->Auth->redirect());
        }
    }

    /**
     * ログアウト処理(共通)
     */
    protected function _logout()
    {
        $this->Auth->logout();
        $this->Session->del('Auth.redirect');
        $this->Session->setFlash(__('セッションを終了しました。', true), null, null, 'auth');
    }

}

UsersController::beforeFileter()で、ログインアクションが呼ばれた際に、Authコンポーネントの自動リダイレクト処理をキャンセルするよう指示し、リダイレクト処理はログインアクション中(今回は、UsersController::_login())で行うようにしておきます。

また、通常、Admin共通のログイン処理メソッド、UsersController::_login()を作成してそこで、Authセッションにグループ名を追加する処理を行います。

[ad]

おまけ: この方法で、Controller::$permissionsの設定を簡便化するために、config/acl.phpを作成してユーザグループのグループを作っておくとちょっと幸せになるかもしれません。

< ?php
// -- AUTH groups
/**
 * 管理者ユーザのグループ名
 * @var string
 */
define('AUTH_GROUP_ADMIN', 'admin');
/**
 * クライアントユーザーのグループ名
 * @var string
 */
define('AUTH_GROUP_CLIENT', 'client');
/**
 * 一般ユーザーのグループ名
 * @var string
 */
define('AUTH_GROUP_NORMAL', 'normal');

class ACL
{
    /**
     * 全てのユーザグループを許可
     * @var array
     */
    static $ACL_ALL = array('*');
    
    /**
     * クライアント機能にアクセス可能なユーザグループ
     * (管理者、クライアントユーザともにアクセス可能)
     * @var array
     */
    static $ACL_CLIENT = array(AUTH_GROUP_ADMIN, AUTH_GROUP_CLIENT);

    /**
     * 管理者機能にアクセス可能なユーザグループ
     * (管理者のみアクセス可能)
     * @var array
     */
    static $ACL_ADMIN = array(AUTH_GROUP_ADMIN);

    /**
     * ユーザ側機能にアクセス可能なユーザグループ
     * (一般ユーザのみアクセス可能)
     * @var array
     */
    static $ACL_USER = array(AUTH_GROUP_NORMAL);
}

作成したら、config/bootstrap.phpに以下を追記して読み込み。

require_once(‘acl.php’);

おまけ2: UsersController::beforeFileter()で、

		// redirectを抑制
        if (in_array($this->action, array('login', 'admin_login'))) {
            $this->Auth->autoRedirect = false;
        }

としておくと、認証時に自動リダイレクトされなくなるので、UsersController::login()でログイン後の遷移先を変更できます。(例えば、グループを見てadminの場合は遷移先を/admin/にするとか。