Webアプリを作る上では避けられないのがユーザー認証です。アプリケーションを利用できる人を制限したり,人によって利用できる機能を制限したりするためにはユーザー認証は必須になります。CakePHPのリファレンスガイドの付録Bには
現在のところ私たちは,ユーザ認証システムはアプリケーションごとに仕様が異なると考えています。あるものはハッシュでパスワードを暗号化,別のものは LDAP 認証などという具合,そしてほぼすべてのアプリケーションの User モデルについて,少しずつ違いがある,といったことがあります。それで今のところは,あなた次第,ということにしておきます。
とあります。そういろいろな仕様があると思います。僕の開発するアプリケーションで必要となる認証方法は,DBにユーザー情報を持っておいて,それでログインするというものと,LDAPサーバー(Windows Server 2003 のActive Directory)の認証とを組み合わせたものです。
- ユーザー情報はMySQLのテーブルに持つ
- DB認証のパスワードはハッシュで暗号化して保存する
- DB認証のパスワードが空の場合はLDAPで認証する
- DBにユーザー情報がない場合LDAPで認証できたらDBにユーザー情報を自動作成できる
このような仕様のユーザー認証を作成するのを目標にしたいと思います。
XAMPPでLDAPを利用できるようにする
最初に開発環境でLDAPが利用できるようにする必要があります。xamppでLDAPを利用できるようにするためには,php.iniの変更が必要です。コマンドラインのためには,c:\xampp\php\php.iniを,Apacheのためにはc:\xampp\apache\bin\php.iniを編集します。
;extension=php_ldap.dll
エディタで上記の行を検索して,この行のコメントを外して保存します。Apacheを再起動します。
LDAP Models in CakePHP
http://bakery.cakephp.org/articles/view/ldap-models-in-cakephp にpsychicことJohn David Anderson氏が書いている記事が大変参考になりましたのでその概要を日本語で書いてみます。
序
多くの組織では今日クライアントや従業員の情報を保存するのにLDAPを利用しています。このチュートリアルは,アプリケーションからCakePHPのModelとしてそのデータを利用する方法を紹介します。
L is for Lightweight
それほどLDAPに詳しくない場合でも,LDAPへの接続・問い合わせ・データの取得にはどうしたらようかを理解する助けになるいくつかのポイントがあります。LDAPは本当にかなり軽量です,そしてその動作の基本を理解することは,大いにためになるでしょう。
LDAPはツリー状の構造に保存されます。ツリーの中でノードを特定するには親ノードを特定する構文を使います。LDAPツリーのルートは,通常,,「cn=cakephp,cn=org」のように収容されるドメイン名にちなんで名付けられてます。次のような表現は
ou=People,cn=example,cn=com
LDAPツリーのルートにある,"People"というOU(organizatinal unit)をあらわします。
LDAPには多くのタイプのエントリーがありますが,LDAPツリーの最も一般的な用途の一つは個人情報を保存することです。個人レコードをLDAPサーバに問い合わせたとき,そのレコードはツリー形式で返ります。PHPでは,ネストした配列で返ります。
D is for Down and Dirty
はじめにテーブルを使用しないCakePHPモデルを作成します。そのためにはModelクラスの$useTableプロパティを使います。また,LDAPの接続設定を定義するいくつかのクラス変数を宣言します。
<?php class LdapUser extends AppModel { var $name = 'LdapUser'; var $useTable = false; var $host = 'ldap.example.com'; var $port = 389; var $baseDn = 'dc=example,dc=com'; var $user = 'cn=admin,dc=example,dc=com'; var $pass = 'password'; var $ds; } ?>
LDAPに詳しくない人のために,これらのセッティングについて説明させてください。hostとportは,そのまんまです。baseDnは,LDAPツリーのルートがどこにあるかを指定します。baseDNを一般公開していないサーバーの場合は,管理者に確かめる必要があります。
$userと$passは,ログインに必要な認証情報です。$ds変数は,接続したLDAPサーバーへのリンクを保存します。
次に,ModelがCakePHPによってロードされるときにLDAPサーバーに接続するようにコンストラクタを修正する必要があります。
<?php //ldap_user.php (partial) function __construct() { parent::__construct(); $this->ds = ldap_connect($this->host, $this->port); ldap_set_option($this->ds, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_bind($this->ds, $this->user, $this->pass); } function __destruct() { ldap_close($this->ds); } ?>
A is for Application
セットアップが完了した今,Modelに2,3のアプリケーションに役立つ関数を作成することができます。最初の業務予定は,多分,一定の条件によってデータを取ってくる関数を作成することでしょう。私が作成した最初のものは,ある属性の値に基づくレコードのセットを返すfindAll()関数でした。
<?php function findAll($attribute = 'uid', $value = '*', $baseDn = 'ou=People,dc=example,dc=com') { $r = ldap_search($this->ds, $baseDn, $attribute . '=' . $value); if ($r) { //if the result contains entries with surnames, //sort by surname: ldap_sort($this->ds, $r, "sn"); return ldap_get_entries($this->ds, $r); } } ?>
この関数はデフォルトでは与えられたユーザ名と値に基づくレコードセット("People"というOU下のLDAPツリー上にあるものすべて)をとって来ます。またレコードの姓(sn)によってソートされて返されます。
コントローラでは,多くのやり方で利用できます,:
<?php //Get all users: $this->LdapUser->findAll('uid', '*'); //Get users from a specific group inside the 'People' group: $this->LdapUser->findAll('uid', '*', 'ou=Client Company,ou=People,cn=example,cn=com'); //Get a specific user: $this->LdapUser->findAll('uid', 'jsmith'); ?>
もう一つの使い勝手の良い関数は auth()関数です。
これは特にイントラネットアプリケーションで便利です。ユーザーはたぶん既にLDAPをメールなどのサービスの認証に使用しています。新しいCakePHPによるイントラネットアプリケーションを使うのにそれらと同じユーザ名とパスワードとを使わない手はありません。
この関数はコンポーネントかコントローラにあるべきかもしれませんが,ModelにはLDAP接続情報が既にありますので,私は認証関数をこのModelクラスに置くことにしました。
注:認証をする際には,セキュアな(SSL)LDAP接続をするために異なるポートで接続させたいかもしれません。
<?php function auth($uid, $password) { $result = $this->findAll('uid', $uid); if($result[0]) { if (ldap_bind($this->ds, $result[0]['dn'], $password)) { return true; } else { return false; } } else { return false; } } ?>
簡単ですね。ユーザーを見つけるのにfindAll()関数を使います。このユーザ情報はLDAPサーバがユーザを特定するのに使用するDN(またはdistinguished name)を含んでいます。このステップを取る理由は,Bobのログイン情報を'dn=Bob Smith,ou=MyCompany,dc=example,dc=com'と持つより'bobsmith'と持った方が簡単だからです。
コントローラでこれを利用するのは次のように簡単です。
$this->LdapUser->auth($u, $p);
Active Directoryの利用
psychicが提供してくれた情報で,CakePHPの中にうまくLDAPを取り込めそうだと言うことがわかりました。しかし僕のようにActive DirectoryをLDAPサーバーとして利用する場合には,このままではうまく動かない部分があります。その点については次回で研究します。