忍び歩く男 - SLYWALKER

大阪のこっそりPHPer

AuthComponent + AclComponent + AclBehavior CakePHP1.2RC2

以下のサイトを参考にして、とりあえず動くものを作ってみた。

他にもいろいろ見ましたが、調べすぎて覚えてません(;><)
そのままでは、動かないものがあったので、修正したソースをとりあえず公開しておきます。
viewは、scaffoldでいけるものは作ってません。
変なとこがあれば、突っ込んでください!

テーブル

まずは、データベース。Acl関連のテーブルは後でコンソールから作成する。

--
-- テーブルの構造 `groups`
--

CREATE TABLE `groups` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) collate utf8_unicode_ci NOT NULL,
  `parent_id` int(11) default NULL,
  PRIMARY KEY  (`id`),
  KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

-- --------------------------------------------------------

--
-- テーブルの構造 `users`
--

CREATE TABLE `users` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(255) collate utf8_unicode_ci default NULL,
  `password` varchar(255) collate utf8_unicode_ci default NULL,
  `group_id` int(11) NOT NULL,
  `disabled` tinyint(1) NOT NULL,
  `created` datetime default NULL,
  `modified` datetime default NULL,
  PRIMARY KEY  (`id`),
  KEY `group_id` (`group_id`),
  KEY `disabled` (`disabled`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

モデル

AclBehaviorは、createの時は役に立ってくれるが、updateの時は役立たず・・・
あと、aliasにも何も入れてくれないので、saveを上書きしてます。


app/models/group.php

<?php
class Group extends AppModel {

    public $actsAs = array('Acl');

    function parentNode(){
        if (!$this->id) {
          return null;
        }
        $data = $this->read();
        if (!$data['Group']['parent_id']){
          return null;
        } else {
          return array('model' => 'Group', 'foreign_key' => $data['Group']['parent_id']);
        }
    }

    // 更新時に親IDを変更する
    // この辺の処理おかしいです。下のコメント欄を参照してください。
    // 2010-02-28 コメントを受け修正
    function save($data = null, $validate = true, $fieldList = array())
    {
        if (parent::save($data, $validate, $fieldList)) {
            App::import('Component', 'Acl');
            $Aro = new Aro;
            $conditions = array(
                'model' => $this->name,
                'foreign_key' => $data['Group']['parent_id'],
            );
            $parent_id = $Aro->field('id', $conditions);
            $conditions = array(
                'model' => $this->name,
                'foreign_key' => $data['Group']['id'],
            );
            $Aro->id = $Aro->field('id', $conditions);
            $Aro->saveField('parent_id', $parent_id);
            $Aro->saveField('alias', $this->name . '::' . $data['Group']['id']);
            return true;
        }
        return false;
    }
}
?>

app/models/user.php

<?php
class User extends AppModel {

    public $actsAs = array('Acl');
    public $belongsTo = array('Group');

    function parentNode(){
        if (!$this->id) {
          return null;
        }
        $data = $this->read();
        if (!$data['User']['group_id']){
          return null;
        } else {
          return array('model' => 'Group', 'foreign_key' => $data['User']['group_id']);
        }
    }

    // 更新時に親IDを変更する
    // この辺の処理おかしいです。下のコメント欄を参照してください。
    // 2010-02-28 コメントを受け修正
    function save($data = null, $validate = true, $fieldList = array())
    {
        if (parent::save($data, $validate, $fieldList)) {
            App::import('Component', 'Acl');
            $Aro = new Aro;
            $conditions = array(
                'model' => 'Group',
                'foreign_key' => $data['User']['group_id'],
            );
            $parent_id = $Aro->field('id', $conditions);
            $conditions = array(
                'model' => $this->name,
                'foreign_key' => $data['User']['id'],
            );
            $Aro->id = $Aro->field('id', $conditions);
            $Aro->saveField('parent_id', $parent_id);
            $Aro->saveField('alias', $this->name . '::' . $data['User']['id']);
            return true;
        }
        return false;
    }
}
?>

コントローラ

app/controllers/groups_controller.php

<?php
class GroupsController extends AppController {

    public $scaffold;

}
?>

app/controllers/users_controller.php

<?php
class UsersController extends AppController {

    public $scaffold;

    function login()
    {

    }

    function logout()
    {
        $this->Auth->logout();
        $this->redirect(array('action' => 'login'));
    }
}
?>

ビュー

ログアウトしたり、メッセージを確認するために以下を追加
app/views/layouts/default.ctp

<?php echo $auth['User']['username']; ?>
<?php echo $html->link('Logout', array('controller' => 'users', 'action' => 'logout')); ?>
<?php
    if ($session->check('Message.auth')) {
        $session->flash('auth');
    }
?>

app/views/users/login.ctp

<h2>ログイン</h2>
<?php
    echo $form->create('User', array('action' => 'login'));
    echo $form->input('username');
    echo $form->input('password');
    echo $form->end('Login');
?>

コンソールで

Acl関連のテーブル作成

cake acl initdb

ブラウザで

とりあえず、認証を効かす前にユーザをつくっておく。
http://yourwebroot/groups/index
にアクセスして、「Admin」と「User」くらいをつくっておく。
(parent_id は 0 でいい)


次に
http://yourwebroot/users/index
にアクセスして、「testAdmin」「testUser」なんかをつくっておく。
それぞれ、グループは「Admin」「User」にしとく。


パスワードは暗号化されてないので、コレをいれてAppControllerを書く。

app/controllers/components/no_hash.php

<?php
class NoHashComponent extends Object {
    /**
     * 暗号化しないでそのまま返す
     */
    function hashPasswords($data){
        return $data;
    }
}
?>

app/app_controller.php

<?php
class AppController extends Controller {

    public $components = array('Acl', 'Auth', 'NoHash');
    public $publicControllers = array('pages');


    function beforeFilter()
    {
        if (isset($this->Auth)) {
            $this->Auth->userScope = array('User.disabled' => 0);
            $this->Auth->authenticate = $this->NoHash;
            $this->Auth->loginAction = '/users/login';
            $this->Auth->loginRedirect = '/users/index';
            $this->Auth->authorize = 'actions';

            if (in_array(low($this->params['controller']), $this->publicControllers)) {
                $this->Auth->allow();
            }
            $this->set('auth', $this->Auth->user());
        }
    }
}
?>

で、
http://yourwebroot/users/login
にアクセス、ログインしてみてダメ!って言われればとりあえすOK。

再びコンソールでAcoの登録

cake acl create aco root Users
cake acl create aco Users index
cake acl create aco Users logout

パーミッションの設定

こんな感じとか

cake acl grant Group.1 Users all

こんな感じとかで登録

cake acl grant User.1 Users/index all

つまり

cake acl grant {model}.{id} {controller}/{action} all

てことです。
ID忘れてしまったら、

cake acl view aro

で以下のように表示される。

Aro tree:
---------------------------------------------------------------
  [1]Group::1

    [7]Group::4

    [4]User::1

    [5]User::2

  [2]Group::2

    [8]Group::5

  [3]Group::3

    [6]User::3

---------------------------------------------------------------

で、
http://yourwebroot/users/login
にアクセス、権限を与えたユーザでログインしてみてindexが表示されればとりあえすOK。

とりあえず、自分が忘れないうちにバタバタっとまとめてみました。