yiiのアクセス制御を学ぶ アクションで権限チェックしないの巻

前エントリで触れなかったupdateOwnPost関連のアクセス制御について。アクションに書いてしまいがちな権限チェックをフィルタに書いてアクションをシンプルに保っていきたい。

パラメータ付き権限アイテム

ここの図でupdateOwnPostを再確認する。updateOwnPost自身はパラメータ付き権限アイテムで、updatePost がぶら下がっている。

updateOwnPostの権限チェックは次のように実装する。

if(!Yii::app()->user->checkAccess('updateOwnPost', array('post'=>$post)))
  Yii::app()->end();
// または
if(!Yii::app()->user->checkAccess('updatePost', array('post'=>$post)))
  Yii::app()->end();

CAccessControlFilterの権限チェックを調べる

CAccessControlFilterがユーザーと権限をどう扱うか調べた。ユーザーと権限は前エントリ参照。

検証コードの一部

protected/controllers/PostController.php

<?php
public function filters()
{
    return array(
        'accessControl', // perform access control for CRUD operations
    );
}
public function accessRules()
{
    return array(
        array('allow', 
            'actions'=>array('update'),
            'roles'=>array('author'),
            //'roles'=>array('editor'),
            //'roles'=>array('updatePost'),
            //'roles'=>array('updateOwnPost'),

           // ユーザー毎にrolesを切り替えて検証する

        ),
    );
}
検証結果
filter のroles \ user usera userb userc userd
author x o x o
editor x x o o
updatePost x x o o
updateOwnPost x x x x

o : 200 OK, x : 403 forbidden

ロールに updateOwnPost を設定したとき、全ユーザーがアクセス不可。フィルタリング時点では updateOwnPostに必要なパラメータを渡せないから仕方ないか。

問題は userb の updatePost。userbは自身のポストはupdatePostしたいのだが、フィルタにupdatePostを指定すると403になってしまう。これではロールにupdatePost指定できない。
フィルタで権限チェックせず、アクションで権限チェックする手もあるが。。。

<?php
public function accessRules()
{
    return array(
        array('allow',
            'actions'=>array('update'),
            'roles'=>array(), //ロールなし
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
        ),
    );
}
public function update()
{
    // 複雑な権限チェック
    if(!Yii::app()->user->checkAccess('updatePost', array('post'=>$post)))
        Yii::app()->end();

    // updateの仕事
    ...
}

嫌だなー権限チェックはフィルタに任せたい。アクションはアクションの仕事に集中したい。

フィルタでもパラメータ付き権限チェックできる

expressionを使えば!

<?php
public function accessRules()
{
    return array(
        array('allow',
            'actions'=>array('update'),
            'expression'=>array($this, "checkUpdatePost"), 
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
        ),
    );
}
protected function checkUpdatePost($user, $rule)
{
    // 複雑な権限チェック  $user は Yii::app()->user のこと
    return $user->checkAccess('updatePost', array('post'=>$this->loadModel());
}
public function actionUpdate()
{
    // updateの仕事
    ...
}
protected function loadModel() 
{
    ...
}

これでいける。checkUpdatePostでloadしたモデルはアクションで利用できるオマケつきつき。

beforeActionでも

「アクションはアクションの仕事に集中」するならば、beforeActionでチェックしてもいいか。