21

CakePHP で ACL を実装するのに苦労しています。Cake マニュアルのドキュメントや他のいくつかのチュートリアル、ブログ投稿などを読んだ後、多くのギャップを埋めるのに役立つ Aran Johnson の優れたチュートリアルを見つけました。彼の例は、いくつかの場所で、特に彼が使用する ARO ツリー構造で、私が見た他の例と矛盾しているようです。

彼のでは、ユーザー グループはカスケード ツリーとして設定されています。最も一般的なユーザー タイプがツリーの一番上にあり、その子はより制限されたアクセス タイプごとに分岐しています。他の場所では、通常、各ユーザー タイプを同じ汎用ユーザー タイプの子として見てきました。

CakePHP で ARO と ACO をどのようにセットアップしますか? どんなヒントでも大歓迎です!

4

1 に答える 1

51

CakePHP のビルトイン ACL システムは非常に強力ですが、実際の実装の詳細については十分に文書化されていません。多くの CakePHP ベースのプロジェクトで使用してある程度の成功を収めたシステムは次のとおりです。

これは、他の場所で文書化されているいくつかのグループ レベルのアクセス システムを変更したものです。私たちのシステムの目的は、ユーザーがグループ レベルで承認されるシンプルなシステムを持つことですが、ユーザーが作成したアイテムに対して、またはユーザーごとに特定の追加権限を持つことができます。aros_acosテーブル内の各ユーザー (より具体的には各 ARO) に対して特定のエントリを作成する必要がないようにしたかったのです。

ユーザー テーブルとロール テーブルがあります。

ユーザー

user_id, user_name, role_id

役割

id, role_name

各役割の ARO ツリーを作成します (通常、4 つの役割があります - 無許可のゲスト (id 1)、許可されたユーザー (id 2)、サイト モデレーター (id 3)、および管理者 (id 4))。

cake acl create aro / Role.1

cake acl create aro 1 Role.2 ... etc ...

この後、SQL や phpMyAdmin などを使用して、これらすべてのエイリアスを追加する必要があります。cake コマンド ライン ツールでは実行できないためです。すべてのロールに「Role-{id}」と「User-{id}」を使用します。

次に、ROOT ACO を作成します -

cake acl create aco / 'ROOT'

次に、この ROOT の下にあるすべてのコントローラーの ACO を作成します。

cake acl create aco 'ROOT' 'MyController' ... etc ...

ここまでは普通。_editown ACL コンポーネントの actionMap で追加のアクションとして使用できるaros_acos テーブルに追加のフィールドを追加します。

CREATE TABLE IF NOT EXISTS `aros_acos` (
`id` int(11) NOT NULL auto_increment,
`aro_id` int(11) default NULL,
`aco_id` int(11) default NULL,
`_create` int(11) NOT NULL default '0',
`_read` int(11) NOT NULL default '0',
`_update` int(11) NOT NULL default '0',
`_delete` int(11) NOT NULL default '0',
`_editown` int(11) NOT NULL default '0',
PRIMARY KEY  (`id`),
KEY `acl` (`aro_id`,`aco_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

次に、AclComponent::check() に対して要求されたコントローラー/アクションを検証する「crud」メソッドを使用するように Auth コンポーネントをセットアップできます。app_controller には、次のようなものがあります。

private function setupAuth() {
    if(isset($this->Auth)) {
        ....
        $this->Auth->authorize = 'crud';
        $this->Auth->actionMap = array( 'index'     => 'read',
                        'add'       => 'create',
                        'edit'      => 'update'
                        'editMine'  => 'editown',
                        'view'      => 'read'
                        ... etc ...
                        );
        ... etc ...
    }
}

繰り返しますが、これはかなり標準的な CakePHP のものです。次に、AppController に checkAccess メソッドがあり、グループ レベルのものを追加して、グループ ARO またはユーザー ARO のアクセスをチェックするかどうかをチェックします。

private function checkAccess() {
    if(!$user = $this->Auth->user()) {
        $role_alias = 'Role-1';
        $user_alias = null;
    } else {
        $role_alias = 'Role-' . $user['User']['role_id'];
        $user_alias = 'User-' . $user['User']['id'];
    }

    // do we have an aro for this user?
    if($user_alias && ($user_aro = $this->User->Aro->findByAlias($user_alias))) {
        $aro_alias = $user_alias;
    } else {
        $aro_alias = $role_alias;
    }

    if ('editown' == $this->Auth->actionMap[$this->action]) {
        if($this->Acl->check($aro_alias, $this->name, 'editown') and $this->isMine()) {
            $this->Auth->allow();
        } else {
            $this->Auth->authorize = 'controller';
            $this->Auth->deny('*');
        }
    } else {
        // check this user-level aro for access
        if($this->Acl->check($aro_alias, $this->name, $this->Auth->actionMap[$this->action])) {
            $this->Auth->allow();
        } else {
            $this->Auth->authorize = 'controller';
            $this->Auth->deny('*');
        }
    }
}

setupAuth()およびcheckAccess()メソッドは の ) コールバックで呼び出されますAppControllerbeforeFilter(要求された項目の user_id が現在認証されているユーザーと同じであることを確認するだけのisMineメソッドが AppControler にもあります (以下を参照)。明確にするために、これを省略しました。

本当にそれだけです。次に、特定の acos への特定のグループ アクセスを許可/拒否できます。

cake acl grant 'Role-2' 'MyController' 'read'

cake acl grant 'Role-2' 'MyController' 'editown'

cake acl deny 'Role-2' 'MyController' 'update'

cake acl deny 'Role-2' 'MyController' 'delete'

私はあなたが絵を手に入れると確信しています。

とにかく、この答えは私が意図したよりもずっと長く、おそらくほとんど意味がありませんが、それがあなたの助けになることを願っています...

- 編集 -

isMine()リクエストに応じて、AppController にある編集済みのメソッドを次に示します(わかりやすくするためだけに、定型コードにはここでは意味のないものがたくさんあります) 。私は多くのエラーチェックも削除しましたが、これがその本質です:

function isMine($model=null, $id=null, $usermodel='User', $foreignkey='user_id') {
    if(empty($model)) {
        // default model is first item in $this->uses array
        $model = $this->uses[0];
    }

    if(empty($id)) {
        if(!empty($this->passedArgs['id'])) {
        $id = $this->passedArgs['id'];
        } elseif(!empty($this->passedArgs[0])) {
            $id = $this->passedArgs[0];
        }
    }

    if(is_array($id)) {
        foreach($id as $i) {
            if(!$this->_isMine($model, $i, $usermodel, $foreignkey)) {
                return false;
            }
        }

        return true;
    }

    return $this->_isMine($model, $id, $usermodel, $foreignkey);
}


function _isMine($model, $id, $usermodel='User', $foreignkey='user_id') {
    $user = Configure::read('curr.loggedinuser'); // this is set in the UsersController on successful login

    if(isset($this->$model)) {
        $model = $this->$model;
    } else {
        $model = ClassRegistry::init($model);
    }

    //read model
    if(!($record = $model->read(null, $id))) {
        return false;
    }

    //get foreign key
    if($usermodel == $model->alias) {
        if($record[$model->alias][$model->primaryKey] == $user['User']['id']) {
            return true;
        }
    } elseif($record[$model->alias][$foreignkey] == $user['User']['id']) {
        return true;
    }

    return false;
}
于 2008-09-11T13:36:18.010 に答える