ECWorks Blog

ECWorks Blog

CakePHPを中心としたサイト開発情報をメインに公開。新しもの好きなので時々製品レポートなんかも。

【CakePHP】DarkAuthComponentを使ってみる

Auth+Aclはやっぱり良く分からないため、代替としてDarkAuthを研究してみることにしました。

結論から言いますと、DarkAuthは前回の記事で条件としてあげた「ログインしていなくてもサービスが使えるけど、ログインしていると使えるページや、さらにグループでパーミッションが指定できる」というニーズには向いていないことが分かりました。が、DarkAuthはそれはそれでシンプルに権限付きの認証の仕組みを取り入れられるので便利かなと。
また、不幸なことに日本語で解説されたドキュメントがネット上に見受けられないため、とりあえず紹介がてら書いてみることにしました。

【導入概要】
基本的に、DarkAuthコンポーネントを用いた認証は、次のような手順で行います。

0:DarkAuthComponentをBakeryから入手する(当たり前か)
1:DBにテーブルを作成
2:モデルの作成
3:コントローラの作成
4:ビューの作成
5:動作確認

【0:DarkAuthコンポーネントの入手】
DarkAuthコンポーネントは、Bakeryから入手します。

▼DarkAuth v1.3 – an alternative Auth
http://bakery.cakephp.org/articles/view/darkauth-v1-3-an-auth-component

この2ページ目が、実体となるコンポーネントのソースです。これを「dark_auth.php」としてapp/controllers/components/ディレクトリに保存します。

なお、該当ページ(3ページあります)は、一応一巡して内容を確認した方が良いと思います。

また、コメントにあるのですが、RC1以降は$controller->render()をechoするように書かれていますので、その修正をしておきます。ソース内に2箇所あります。

【1:DBにテーブルを作成】

DarkAuth認証には、UserモデルとGroupモデルの2つが必要です。また、UserとGroupはHABTMの関係ですので、テーブルとしては「users, groups, groups_users」の3つのテーブルが必要になります。

私はPostgreSQLユーザですので、MySQLユーザの方には申し訳ないのですが、例えばこのようなSQLを用意します。

app/config/sql/schema.sql

DROP TABLE IF EXISTS users;
DROP SEQUENCE IF EXISTS users_id_seq;
CREATE SEQUENCE users_id_seq START 1;
GRANT ALL ON users_id_seq TO PUBLIC;
CREATE TABLE users (
    id                 INTEGER DEFAULT nextval('users_id_seq') PRIMARY KEY,
    created         TIMESTAMP NOT NULL,
    modified         TIMESTAMP NOT NULL,

    loginid            VARCHAR(255) NOT NULL,
    password        VARCHAR(255) NOT NULL,
    name             VARCHAR(255) NOT NULL
);
GRANT ALL ON users TO PUBLIC;

DROP TABLE IF EXISTS groups;
DROP SEQUENCE IF EXISTS groups_id_seq;
CREATE SEQUENCE groups_id_seq START 1;
GRANT ALL ON groups_id_seq TO PUBLIC;
CREATE TABLE groups (
    id                 INTEGER DEFAULT nextval('groups_id_seq') PRIMARY KEY,
    created         TIMESTAMP NOT NULL,
    modified         TIMESTAMP NOT NULL,
   
    name            VARCHAR(255) NOT NULL
);
GRANT ALL ON groups TO PUBLIC;

DROP INDEX IF EXISTS groups_users_idx;
DROP TABLE IF EXISTS groups_users;
DROP SEQUENCE IF EXISTS groups_users_id_seq;
CREATE SEQUENCE groups_users_id_seq START 1;
GRANT ALL ON groups_users_id_seq TO PUBLIC;
CREATE TABLE groups_users (
    id                 INTEGER DEFAULT nextval('groups_users_id_seq') PRIMARY KEY,
    user_id         INTEGER NOT NULL,
    group_id         INTEGER NOT NULL
);
GRANT ALL ON groups_users TO PUBLIC;
CREATE UNIQUE INDEX groups_users_idx ON groups_users (user_id, group_id);

VARCHARに関しては、最適化できるとは思いますが、例ですので適当です(^^;。

【2:モデルの作成】

テーブルが出来ましたので、今度はモデルです。
前述の通り、HABTMの関係ですので、その設定が必要になります。

app/models/user.php

<?php
class User extends AppModel {
    var $name  = 'User';
   
    var $hasAndBelongsToMany = array(
    'Group' =>
        array(
            'className'             => 'Group',
            'joinTable'                  => 'groups_users',
            'foreignKey'             => 'user_id',
            'associationForeignKey'  => 'group_id',
        )
    );

}
?>

app/models/group.php

<?php
class Group extends AppModel {
    var $name  = 'Group';
}
?>

思いっきり手抜きをしてしまっているのですが、これでも動きます。実は、DarkAuth内では、GroupモデルからUserモデルへのアクセスが存在しないからです。DarkAuth以外で必要な場合は、しっかり設定してください。

【3:コントローラの作成】

とりあえずデータ関連の設定は終わったので、今度はページ制御です。
ビューから作るかコントローラから作るかは好みが分かれると思いますが、私はコントローラから派ですのでこちらを先に作ります。

app/controllers/users_controller.php

<?php
class UsersController extends AppController {

    var $name = 'Users';
    var $uses = array('User');
    var $components = array('DarkAuth');
    var $layout = 'users';

    var $_DarkAuth = array(
        'required' => array('Huga'),
        'onDeny' => '/',
    );
   
    function beforeFilter(){
        //DarkAuthの設定
        //
        $this->DarkAuth->session_secure_key = 'hogehogehoge';
        $this->DarkAuth->user_name_field = 'loginid';
        $this->DarkAuth->user_pass_field = 'password';
        $this->DarkAuth->user_live_field = null;
        $this->DarkAuth->superuser_group = null;
       
    }
   
    function index(){
    }
   
    function _login(){
        if(is_array($this->data) && array_key_exists('DarkAuth',$this->data)){
            $this->DarkAuth->authenticate_from_post($this->data['DarkAuth']);
            $this->data['DarkAuth']['password'] = '';
        }
    }
   
    function logout(){
        $this->DarkAuth->logout();
    }
   
  //あとは必要なページを記述
}
?>

ミソなのは、「login()」ではなく「_login()」であることです。ここを間違えると「コントローラがない」とエラーが出ます。

DarkAuthの設定は、beforeFilter()内に書きます。
DarkAuthは「live」とかいう生存フラグを標準でチェックするようになっているのですが、例のようにフィールドに存在しない場合はnullにします。
また、スーパユーザグループの設定が出来るようになっていますが、これも不要な場合はnullです。デフォルトは「Root」です。有効にした場合、これがgroupsテーブルに定義されていないとエラーになります。

【4:ビューの作成】

コントローラが出来たら、今度はビューです。
とりあえずログイン画面が必要なので、ログインビューを用意します。

app/views/login.ctp

<?php
    $this->pageTitle = 'Access Restricted';
    echo $form->create('DarkAuth',array('url'=>substr($this->here,strlen($this->base))));
    echo $form->input('DarkAuth.username');
    echo $form->password('DarkAuth.password');

    echo $form->end('login');
?>

これまたミソなのが、このファイルはapp/views/users/内ではなく、app/views/内に作成しなければならない点です。私が思うに、Userモデルの情報を使っているので、usersの中で良いんじゃないかと思うのですが。これではTplcutterで生成出来ないので、Tplcutterでページを作る場合は、例えばusers内でデザインし、手動でコピーする、等のイレギュラーな作業が発生します。

これ以外のページは、users内に作ります。上記の例ですとindex.ctpやlogout.ctpが必要になります。

【5:動作確認】

動作確認の前に、テストデータをテーブルに登録しなければなりません。
usersに適当なユーザを、groupsに適当なグループを、そしてgroups_usersにこれらをひも付けた番号を登録しておきます。後述しますが、これらの登録はSQLファイルなどにしておいた方が良いと思います。上記の例では、groupsに「Huga」という名前のグループが必要になります。

テストデータが準備できたら、いよいよ認証テストです。
ソースに問題なければ、/usersにアクセスすると粗末なログイン画面が出てくると思います(笑)。

ところが、実際に入力して実行すると、登録したIDとパスワードの筈なのに認証がうまくいかないことに気づくと思います。理由は、認証パスワードは平文ではなくてハッシュ化されているからです。
セキュリティ的にはあまり良くないのですが、テストということでTipsを紹介しますと、DEBUGを2にして認証させると、SQL内にハッシュ化されたパスワードが出てくると思いますので、それをコピーしてDBに再登録すれば、簡単に問題が解決できます。データ登録のSQLを作っておく、というのは、実はここで役に立ちます。今後作り込みをしていく場合、SQLを作っておけば再度パスワード絡みで手間を省くことが出来るからです。

以上、ざっくりとDarkAuthの導入を紹介してみました。Authコンポーネント並の手軽さで、Group毎のパーミッションがもてるので、Aclよりも導入は簡単だと思います。
いくつか作りが洗練されていないというのか、どきっとするようなエラーが出る場合があるので、そのへんは標準ツールであるAclの方が信頼度は高いと思いますが、十分に使えるツールだとは思いますので、是非検討してみてください。

というわけで、ここまでは現状のDarkAuthの話なのですが、当方で使いたかった機能の半分しか満たしていないため、改造するかほかのものを探すしかないです。
まず一つは、アクション毎にパーミッションが付けられるようにしたいです。この辺はかなり簡単にできる可能性が。
もう一つは、ゲスト状態でもログイン画面に行かない場合が作れるか? というところです。現在は問答無用で飛んでいってしまうので、この前に判定をして、問題なければそのまま表示するような仕組みを入れれば実現できそうです。

とりあえず、DarkAuthを改造する方向でチャレンジしてみたいと思います。
うまくできたらまた紹介します。




Comments are closed.