CakePHPのLDAPサポートプラグイン(Yaldap)


(2014/07/20) 本プラグインは廃止されました。CakePHPのLDAPサポートプラグイン(Yaldap2)を参照してください。

(2014/01/06) CakePHP(最新の安定版は 2.4.4)の標準では、データベースへのアクセスレイヤー(データソース)としては LDAP がサポートされていません。そのため LDAP 認証をしたい場合は、自分で LDAP アクセス部分のコードを書く必要があります。

探してみたところCakePHP 2.0 Ldap Authentication)という実装を見つけましたが、使ってみるといろいろと不具合が散見されたため、結局自前でデータソース Yaldap およびそれを利用した ldapAuthetication を実装しました。全機能が実装できているわけではないのでご注意願います。

例示におけるプラットフォームは VMware vSphere 上の VM に入れた CentOS 6.5(64bit) です。Eclipse+PDT+Xdebug でコンソールアプリのステップ実行ができるように、インストール時のパッケージグループとしては Software Development Workstation を選択しました。また webtatic.com を参照して php 5.5.x を入れています。CakePHP のインストールに関してはCakePHP のインストールを参照してください。

目次

Go previousメニューに戻る

LDAP 環境

LDAP 環境がない方のために、後述のサンプルアプリを動かすための必要最小限の LDAP 環境作成の手順を示します。すでに LDAP 環境がある方はスキップしてください。

サンプルデータでは、LDAP の管理者パスワードや Root DN 等は後述の database.php に合わせています。実環境ではUnixPower on Networking - LDAPの概要などを参照の上、適切なパスワードを設定してください。

# yum install openldap-servers openldap-clients
# cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG
# wget http://net-newbie.com/cakephp/Yaldap/openldap-example.tar.gz -P /tmp
# cd /etc/openldap
# tar xzf /tmp/openldap-example.tar.gz
# slapadd < ./example.com.ldif(若干の警告が出ます)
# chown -R ldap.ldap .
# chown -R ldap.ldap /var/lib/ldap
# service slapd start

LDAP サーバ単体の動作確認

m-hotta@nigeria:$ ldapsearch -x -LLL -b dc=example,dc=com '(uid=user1)' dn
dn: uid=user1,ou=Users,dc=example,dc=com

結果が表示されれば成功です。LDAP にどういうデータが登録されているかは、/etc/openldap/example.com.ldif を参照してください。

使い方 - コンソールアプリを例にして

コンソールアプリの設置

まずは前述のサンプル LDAP 構成、もしくは自組織の LDAP サーバとの整合性の確認を兼ねて、コンソールアプリによる動作確認を行ってみます。

ただここで、CakePHP のひとつのインスタンス内でコンソールアプリと Web アプリを共存させると、キャッシュやセッション情報(ファイル)の所有者の関係でトラブルの元になります。ここではコンソールアプリを動かすため、CakePHP のインスタンスを別に作ることにします。

# exit(一般ユーザに戻る)
m-hotta@nigeria:$ cd
m-hotta@nigeria:$ git clone https://github.com/cakephp/cakephp
m-hotta@nigeria:$ mv cakephp cake
m-hotta@nigeria:$ cd cake
m-hotta@nigeria:~/cake$ ls -F
CONTRIBUTING.md  app/              build.xml  lib/      vendors/
README.md        build.properties  index.php  plugins/

自分のホームディレクトリ配下に cake という名前で CakePHP のインスタンスができました。

YaLdap プラグインの導入

m-hotta@nigeria:~/cake$ cd app/Plugin
m-hotta@nigeria:~/cake/app/Plugin$ git clone https://github.com/hotta/Yaldap

プラグインを読み込む設定

app/Config/bootstrap.php の末尾に以下を追加します。

CakePlugin::load('Yaldap');         //  app/Plugin/Yaldap の登録

DB/LDAP データソース設定

database.php はデフォルトでは存在しないので database.php.default をコピーして作るか1から作成します。値は環境に合わせて変更してください。

m-hotta@nigeria:~/cake/app/Config$ cat database.php
<?php
class DATABASE_CONFIG {
    //  OpenLDAP を使う場合は指定します
    public  $ldap = array(
        'datasource'=> 'Yaldap.LdapSource',             //  Plugin.DataSource
        'host'      => 'ldap://localhost',
        'base_dn'   => 'dc=example,dc=com',
        'login'     => 'cn=Manager,dc=example,dc=com',  //  管理者ユーザID
        'password'  => 'password',                      //  管理者パスワード
        'userdn'    => 'ou=Users,dc=example,dc=com',
        'groupdn'   => 'ou=Groups,dc=example,dc=com',
        'tls'       => false,
        'type'      => 'OpenLDAP',
    );
    //  AD を使う場合は指定します
    public  $ad = array(
        'datasource'=> 'Yaldap.LdapSource',             //  Plugin.DataSource
        'host'      => 'ldap://ad.example.com',
        'base_dn'   => 'dc=example,dc=local',
        'login'     => 'Administrator@example.local',   //  管理者ユーザID
        'password'  => 'password',                      //  管理者パスワード
        'userdn'    => 'ou=Users,dc=example,dc=local',
        'groupdn'   => 'ou=Groups,dc=example,dc=local',
        'tls'       => true,
        'type'      => 'ActiveDirectory',
    );
}

念のため、単体の文法チェックをしておきましょう。

m-hotta@nigeria:~/as-user/app/Config$ php -l database.php
No syntax errors detected in database.php

実行結果(使用法表示)

コンソールアプリを起動する場合は、コマンドとして 'インスタンス/app/Console/cake' を指定します。'インスタンス/app/Console' にパスが通っていない場合は相対パスで指定します。cake コマンドを引数なしで実行した場合は、以下のようにヘルプメッセージを表示します。

m-hotta@nigeria:~/cake$ ./app/Console/cake

Welcome to CakePHP v2.4.4 Console
---------------------------------------------------------------
App : app
Path: /home/m-hotta/cake/app/
---------------------------------------------------------------
Current Paths:

 -app: app
 -working: /home/m-hotta/cake/app
 -root: /home/m-hotta/cake
 -core: /home/m-hotta/cake/lib

Changing Paths:

Your working path should be the same as your application path. To change your path use the '-app' param.
Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp

Available Shells:

[Yaldap] sample_app

[CORE] acl, api, bake, command_list, console, i18n, schema, server, test, testsuite, upgrade

To run an app or core command, type cake shell_name [args]
To run a plugin command, type cake Plugin.shell_name [args]
To get help on a specific command, type cake shell_name --help

先頭から6行分は CakePHP のコンソールアプリを動かすと必ず表示されるヘッダです。以後の記述ではヘッダ表示は省略します。

ヘルプメッセージの中にある "[Yaldap] sample_app" は、当該 Yaldap プラグインに付属する動作確認用のサンプルプログラムです。実体は ~/cake/app/Plugin/Yaldap/Console/Command/SampleAppShell.php です。それでは早速実行してみます。

m-hotta@nigeria:~/cake$ ./app/Console/cake Yaldap.sample_app
cake Yaldap.sample_app [arg]
   arg is one of:
   args ............ Show argument list
   ldap_read ....... Model->read()
   ldap_findby ..... Model->findByXXX()
   ldap_findfirst .. Model->find('first')
   ldap_findall .... Model->find('all')
   ldap_query ...... Model->query(sql)
   ldap_auth ....... Model->find('first', password)
   ad_read ....... Model->read()
   ad_findby ..... Model->findByXXX()
   ad_findfirst .. Model->find('first')
   ad_findall .... Model->find('all')
   ad_query ...... Model->query(sql)
   ad_auth ....... Model->find('first', password)

事実上の引数は第二パラメータ以降となります。(第二)パラメータが指定されていない場合は使用法を表示します。それぞれが各 API を使った簡単なテストとなっています。

実行結果(ldap_read)

それでは、ldap_read を実行してみます。

m-hotta@nigeria:~/cake$ ./app/Console/cake Yaldap.sample_app ldap_read
$this->ldap->read()
Array
(
    [ldap] => Array
        (
            [dn] => uid=user1,ou=Users,dc=example,dc=com
            [cn] => user1-name
            [dn] => uid=user1,ou=Users,dc=example,dc=com
        )
)

ldap_read では、モデル(正確にはデータソース)の read() API を呼んでいます。ここの実行に関係するアプリケーション部分のソースを示します:

  1 <?php
  2 //  cake/app/Plugin/Yaldap/Console/Command/SampleAppShell.php
  3 Class   SampleAppShell  extends AppShell    {
  4
  5     const   USERNAME = 'user1';
  6     public  $uses = array('Yaldap.ldap',
  7                           'Yaldap.ad'); //  PluginName.AliasName
  8
  --- cut --
 27     public  function    ldap_read() {
 28         $this->out('$this->ldap->read()');
 29         $this->ldap->useTable = false;
 30         $this->ldap->useDbConfig = 'ldap';
 31         $this->out(print_r($this->ldap->read(
 32             array('sn', 'cn'), self::USERNAME), true));
 33     }

31行目の print_r() の引数として read() を呼んでいます。ここではプライマリーキーとして self::USERNAME を検索し、見つかったエントリの中から sn と cn 属性を取り出しています。属性の指定に関わらず、dn は常に返されます。他のパラメータについてもそれぞれの API を呼ぶサンプルになっていますので、いろいろと実行してみてください。

各 API の引数は cake/lib/Cake/Model/Model.php にある、同名のメソッドのコメントを参照してください。後述の CakePHP DataSource CheatSheet にも少し書いてあります。なお cake/lib 配下は CakePHP 本体なので不用意に変更しないようにしましょう。

実行結果(ldap_auth)

次は少し毛色が違うものとして、認証サンプルの ldap_auth を使ってみます。

m-hotta@nigeria:~/cake$ ./app/Console/cake Yaldap.sample_app ldap_auth
cake Yaldap.sample_app ldap_auth uid password

ldap_auth はさらに ID とパスワードという2つの引数を受けて、内部的に認証処理を行います。サンプルデータの中の user1 ~ user3 のパスワードはすべて 'password' で統一してあります。

m-hotta@nigeria:~/cake$ ./app/Console/cake Yaldap.sample_app ldap_auth user1 password
$this->ldap->find('first', password)
Array
(
    [ldap] => Array
        (
            [sn] => user1
            [cn] => user1-name
            [homedirectory] => /home/user1
            [dn] => uid=user1,ou=Users,dc=example,dc=com
        )
)

パスワードをわざと間違えてみます。

m-hotta@nigeria:~/cake$ ./app/Console/cake Yaldap.sample_app ldap_auth user1 password2
$this->ldap->find('first', password)
Authentication failed

アプリケーション部分のソースはこんな感じです。エラー処理や入力エスケープ処理等は省略しています。

public  function    ldap_auth() {
    $argc = count($this->args);
    if ($argc != 2) {
        this->out("cake Yaldap.sample_app ldap_auth uid password");
        exit;
    }
    $uid = $this->args[0];
    $password = $this->args[1];
    $this->out('$this->ldap->find(\'first\', password)');
    $this->ldap->useTable = false;
    $this->ldap->useDbConfig = 'ldap';
    $query = array(
        'conditions'    =>  array(
            'uid'       =>  $uid,
            'ldap.password'  =>  $password
        ),
        'fields'        =>  array('sn', 'cn', 'homedirectory')
     );
     $user = $this->ldap->find('first', $query);
     if ($user)  {
         $this->out(print_r($user));
     } else  {
         $this->out("Authentication failed");
     }
}

データソースには認証専用の API はないので、find() に渡すパラメータの中でパスワードフィールドが指定された場合のみ、データソースの内部で ldap_bind() を呼ぶことで認証を行うようにしました。このためパスワードは平文で指定します。

OpenLDAP に限れば、LDAP のフィールドとしての userPassword をクライアント側に取り出してハッシュ値を比較するという実装(*1)も考えられますが、ActiveDirectory ではパスワードフィールドを取り出すことができないようなのでこのような実装にしてあります。いずれにしろ、クライアントと LDAP サーバが別ホストの場合は、両者間の通信路を SSL/TLS で暗号化することをお勧めします。

(*1)…認証関連の基底クラス lib/Cake/Controller/Component/Auth/BasicAuthenticate.php における認証チェック方法です。

Web 画面におけるログイン認証

次はWebアプリにLDAP認証機能を組み込んでみます。Webアプリの実行環境としてCakePHPのインストールの環境ができているものとします。なおコンソール環境の ~/cake ではなく Web 環境の ~/cake244 の中に環境を作りますが、設定が同じものは ~/cake からコピーして持って来ることにします。

環境設定

m-hotta@nigeria:~/cake$ cd ~/cake244
m-hotta@nigeria:~/cake244$ cd app/Plugin
m-hotta@nigeria:~/cake244/app/Plugin$ tar xzf /tmp/Yaldap-0.1.tar.gz
m-hotta@nigeria:~/cake244/app/Plugin$ cd ../..
m-hotta@nigeria:~/cake244$ vi app/Config/bootstrap.php(末尾に以下を追加)
CakePlugin::load('Yaldap'); 
m-hotta@nigeria:~/cake244$ cp ~/cake/app/Config/database.php app/Config

モデル

User クラスで使用するモデルを定義します。これがないと database.php の 'default' エントリを探しに行ってしまい、ないとエラーになります。'default' として使うデータベースの定義とその実体(およびアクセス権)がある場合は不要です。

m-hotta@nigeria:~/cake244$ vi app/Model/User.php
m-hotta@nigeria:~/cake244$ cat app/Model/User.php
<?php
Class   User    Extends AppModel    {
    public  $useDbConfig = 'ldap';
}

ビュー

認証画面を定義しています。'Users' ディレクトリはないので新規に作成します。

m-hotta@nigeria:~/cake244$ mkdir app/View/Users
m-hotta@nigeria:~/cake244$ vi app/View/Users/login.ctp
m-hotta@nigeria:~/cake244$ cat login.ctp
<!-- app/View/Users/login.ctp -->
<?php
echo $this->Form->create('User');
echo $this->Form->input('username');
echo $this->Form->input('password');
echo $this->Form->end('Login');

コントローラー

AppController.php - 認証コンポーネントの定義を追加します。ここに追加することにより、アプリケーション全体に対して認証機能が有効になります。

m-hotta@nigeria:~/cake244$ vi app/Controller/AppController.php
m-hotta@nigeria:~/cake244$ tail -6 app/Controller/AppController.php
class AppController extends Controller {
    public  $components = array(
            'Session',
            'Auth' => array('authenticate' => array('Yaldap.Ldap'))
    );
}

UsersController.php - アプリケーション本体です。新規に作成します。

m-hotta@nigeria:~/cake244$ vi app/Controller/UsersController.php
m-hotta@nigeria:~/cake244$ cat app/Controller/UsersController.php
<?php
//  app/Controller/UsersController.php
//
Class   UsersController    Extends     AppController
{
    public  $uses = false;
    public  $name = 'Users';                //  コントローラ名(複数形)

    public  function    index()
    {
        $this->render();    //  デフォルトの画面を表示
    }

    public  function    login()
    {
        if ($this->request->is('post')) {
            if ($this->Auth->login()) {
                return $this->redirect($this->Auth->redirect());
            } else {
                $this->Session->setFlash(
                'Username or Password is incorrect.');
            }
        }
    }

    public  function    logout()
    {
        $this->Auth->logout();
        return  $this->redirect('/');
    }
}

実行結果

ブラウザで http://cake244.example.com にアクセスすると、ログイン処理時のデフォルトとして http://cake244.example.com/users/login に飛ばされて認証画面が表示されます。

cake244-login

正しいパスワードを入力すると、認証が行われてデフォルトの初期画面が表示されます。

cake244-login-success

すでにログイン情報がセッションに保存されていますので、これ以降は http://cake244.example.com にアクセスすると直接初期画面が表示されます。

次は間違ったパスワードを試してみましょう。http://cake244.example.com/users/logout にアクセスするとセッション情報がクリアされ 、最初の http://cake244.example.com/users/login に飛ばされて認証画面が表示されます。

cake244-login-failure

わざと間違ったパスワードを入力すると、認証エラー画面が表示されます。

アーカイブ

以上のソースを固めたものを ldap-auth-example.tar.gz として置いておきます。どうしてもうまくいかない時は以下を参考にして導入してください。

m-hotta@nigeria:~/cake244$ wget http://net-newbie.com/cakephp/Yaldap/ldap-auth-example.tar.gz -P /tmp
m-hotta@nigeria:~/cake244$ tar xzf /tmp/ldap-auth-example.tar.gz

技術資料

Yaldap プラグインの実装状況

本プラグインはまだまだ道半ばです。現在の実装状況は以下の通りです。

実装済みの機能

未実装/未確認の機能

CheatSheets

本データソースと LDAP 認証機能を作成するにあたり、CakePHP 本体を解析した資料を Google Drive 上に置いてあります。

Go back