<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<!-- EN-Revision: 24249 -->
<sect1 id="zend.ldap.introduction">
    <title>導入</title>

    <para>
        <classname>Zend_Ldap</classname> は <acronym>LDAP</acronym>操作を行うクラスです。
        バインドだけが可能で、
        <acronym>LDAP</acronym> ディレクトリ内のエントリの検索や変更には対応していません。
    </para>

    <sect2 id="zend.ldap.introduction.theory-of-operations">
        <title>動作原理</title>

        <para>
            現在このコンポーネントは、OpenLDAPまたはActiveDirectory（AD）サーバのような
            単一の<acronym>LDAP</acronym>サーバへのバインディングを概念的に表現して、
            <acronym>LDAP</acronym>サーバに対する活動を実行できる
            主要な<classname>Zend_Ldap</classname>クラスから成ります。
            バインディングのパラメータは、明示的に、または、オプション配列の形で提供されるかもしれません。
            <classname>Zend_Ldap_Node</classname>は、
            単一の<acronym>LDAP</acronym>ノードのためにオブジェクト指向インタフェースを提供します。
            そして、<acronym>LDAP</acronym>ベースのドメイン・モデルのために、
            アクティブ・レコードのようなインターフェースの基盤を作ることに使うことができます。
        </para>

        <para>
            属性の設定や取得 (日付値、パスワード、ブール値など)のような
            <acronym>LDAP</acronym>項目上での活動の実行や
            (<classname>Zend_Ldap_Attribute</classname>)、
            <acronym>LDAP</acronym>フィルタ文字列の作成や修正
            (<classname>Zend_Ldap_Filter</classname>)、
            <acronym>LDAP</acronym>識別名 (DN)の操作
            (<classname>Zend_Ldap_Dn</classname>)
            をおこなうためのいくつかのヘルパー・クラスをコンポーネントで提供します。
        </para>

        <para>
            その上、
            OpenLDAPとActiveDirectoyサーバの<classname>Zend_Ldap_Node_Schema</classname>のために
            ブラウズする<acronym>LDAP</acronym>スキーマ、
            そして
            OpenLDAPサーバやActiveDirectoryサーバ、
            Novell eDirectoryサーバのためのサーバ情報取得
            (<classname>Zend_Ldap_Node_RootDse</classname>)をコンポーネントで抽象します。
        </para>

        <para>
            <classname>Zend_Ldap</classname> クラスの使用法は <acronym>LDAP</acronym> サーバの形式によって異なり、
            以下のいずれかのパターンとなります。
        </para>

        <para>
            OpenLDAP を使用している場合は、以下の例のようになります (AD を使って
            <emphasis>いない</emphasis> 場合は <emphasis>bindRequiresDn</emphasis>
            オプションが重要となることに注意しましょう):
        </para>

        <programlisting language="php"><![CDATA[
$options = array(
    'host'              => 's0.foo.net',
    'username'          => 'CN=user1,DC=foo,DC=net',
    'password'          => 'pass1',
    'bindRequiresDn'    => true,
    'accountDomainName' => 'foo.net',
    'baseDn'            => 'OU=Sales,DC=foo,DC=net',
);
$ldap = new Zend_Ldap($options);
$acctname = $ldap->getCanonicalAccountName('abaker',
                                           Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";
]]></programlisting>

        <para>
            Microsoft AD を使う場合の簡単な例です:
        </para>

        <programlisting language="php"><![CDATA[
$options = array(
    'host'                   => 'dc1.w.net',
    'useStartTls'            => true,
    'username'               => 'user1@w.net',
    'password'               => 'pass1',
    'accountDomainName'      => 'w.net',
    'accountDomainNameShort' => 'W',
    'baseDn'                 => 'CN=Users,DC=w,DC=net',
);
$ldap = new Zend_Ldap($options);
$acctname = $ldap->getCanonicalAccountName('bcarter',
                                           Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";
]]></programlisting>

        <para>
            ここでは、<methodname>getCanonicalAccountName()</methodname> メソッドで、
            アカウントの DN を取得していることに注意しましょう。
            これはただ単に、このクラスに現在存在するコードの例をできるだけ多く見せたいからです。
        </para>

        <sect3 id="zend.ldap.introduction.theory-of-operations.automatic-username-canonicalization">
            <title>バインド時のユーザ名自動正規化</title>

            <para>
                <emphasis>bindRequiresDN</emphasis> が <constant>TRUE</constant>で、
                かつ DN 形式のユーザ名がオプションで設定されていない場合、
                <methodname>bind()</methodname> を DN でないユーザ名でコールするとバインドに失敗します。
                しかし、DN 形式のユーザ名がオプションで設定されていれば、
                <classname>Zend_Ldap</classname>はまずそのユーザ名でバインドを行い、
                <methodname>bind()</methodname>で指定したユーザ名に対応するアカウントの DN を取得した上で
                改めてその DN でバインドしなおします。
            </para>

            <para>
                この振る舞いは<link
                    linkend="zend.auth.adapter.ldap"><classname>Zend_Auth_Adapter_Ldap</classname></link>
                にとっては重要です。
                これは、ユーザが指定したユーザ名を直接 <methodname>bind()</methodname> に渡します。
            </para>

            <para>
                次の例では、DN でないユーザ名 '<emphasis>abaker</emphasis>'
                を <methodname>bind()</methodname> で使用する方法を示します:
            </para>

            <programlisting language="php"><![CDATA[
$options = array(
        'host'              => 's0.foo.net',
        'username'          => 'CN=user1,DC=foo,DC=net',
        'password'          => 'pass1',
        'bindRequiresDn'    => true,
        'accountDomainName' => 'foo.net',
        'baseDn'            => 'OU=Sales,DC=foo,DC=net',
);
$ldap = new Zend_Ldap($options);
$ldap->bind('abaker', 'moonbike55');
$acctname = $ldap->getCanonicalAccountName('abaker',
                                           Zend_Ldap::ACCTNAME_FORM_DN);
echo "$acctname\n";
]]></programlisting>

            <para>
                この例において <methodname>bind()</methodname> をコールすると、
                ユーザ名 '<emphasis>abaker</emphasis>' が DN 形式でないことと
                <emphasis>bindRequiresDn</emphasis> が <constant>TRUE</constant> であることから、まず
                '<command>CN=user1,DC=foo,DC=net</command>' と '<emphasis>pass1</emphasis>'
                を用いてバインドします。それから '<emphasis>abaker</emphasis>' の DN を取得し、
                いったんバインドを解除したうえであらためて
                '<command>CN=Alice Baker,OU=Sales,DC=foo,DC=net</command>'
                でバインドしなおします。
            </para>
        </sect3>

        <sect3 id="zend.ldap.introduction.theory-of-operations.account-name-canonicalization">
            <title>アカウント名の正規化</title>

            <para>
                <emphasis>accountDomainName</emphasis>および
                <emphasis>accountDomainNameShort</emphasis>オプションは、
                次のふたつの目的で使用します。
                (1) 複数ドメインによる認証 (どちらか一方が使えないときの代替機能) を実現する。
                (2) ユーザ名を正規化する。
                特に、名前の正規化の際には
                <emphasis>accountCanonicalForm</emphasis>オプションで指定した形式を使用します。
                このオプションの値は、次のいずれかとなります:
            </para>

            <table id="zend.ldap.using.theory-of-operation.account-name-canonicalization.table">
                <title>accountCanonicalFormのオプション</title>
                <tgroup cols="3">
                    <thead>
                        <row>
                            <entry>名前</entry>
                            <entry>値</entry>
                            <entry>例</entry>
                        </row>
                    </thead>
                    <tbody>
                        <row>
                            <entry>
                                <constant>ACCTNAME_FORM_DN</constant>
                            </entry>
                            <entry>1</entry>
                            <entry>CN=Alice Baker,CN=Users,DC=example,DC=com</entry>
                        </row>
                        <row>
                            <entry>
                                <constant>ACCTNAME_FORM_USERNAME</constant>
                            </entry>
                            <entry>2</entry>
                            <entry>abaker</entry>
                        </row>
                        <row>
                            <entry>
                                <constant>ACCTNAME_FORM_BACKSLASH</constant>
                            </entry>
                            <entry>3</entry>
                            <entry>EXAMPLE\abaker</entry>
                        </row>
                        <row>
                            <entry>
                                <constant>ACCTNAME_FORM_PRINCIPAL</constant>
                            </entry>
                            <entry>4</entry>
                            <entry><filename>abaker@example.com</filename></entry>
                        </row>
                    </tbody>
                </tgroup>
            </table>

            <para>
                デフォルトの正規化は、アカウントのドメイン名のオプションが
                どのように設定されているかによって変わります。
                <emphasis>accountDomainNameShort</emphasis> が指定されている場合は、デフォルトの
                <emphasis>accountCanonicalForm</emphasis> の値は
                <constant>ACCTNAME_FORM_BACKSLASH</constant> となります。
                それ以外の場合は、もし <emphasis>accountDomainName</emphasis>
                が設定されていればデフォルトは
                <constant>ACCTNAME_FORM_PRINCIPAL</constant> となります。
            </para>

            <para>
                アカウント名の正規化をすることで、<methodname>bind()</methodname>
                に何が渡されたのかにかかわらずアカウントの識別に用いる文字列が一貫性のあるものになります。
                たとえば、ユーザがアカウント名として
                <filename>abaker@example.com</filename> あるいは単に <emphasis>abaker</emphasis>
                だけを指定したとしても、<emphasis>accountCanonicalForm</emphasis>
                が 3 に設定されていれば正規化後の名前は
                <emphasis>EXAMPLE\abaker</emphasis> となります。
            </para>
        </sect3>

        <sect3 id="zend.ldap.introduction.theory-of-operations.multi-domain-failover">
            <title>複数ドメインの認証とフェイルオーバー</title>

            <para>
                <classname>Zend_Ldap</classname> コンポーネント自身は、
                複数サーバでの認証を試みません。
                しかし、<classname>Zend_Ldap</classname> はこのような場合に対応するようにも設計されています。
                サーバのオプションを指定した配列の配列を順にたどり、
                個々のサーバへのバインドを試みるのです。上で説明したように、
                <methodname>bind()</methodname> は自動的に名前を正規化します。したがって、ユーザが
                <filename>abaker@foo.net</filename> を指定したか、あるいは <emphasis>W\bcarter</emphasis>
                や <emphasis>cdavis</emphasis> と指定したかにはかかわらず、
                <methodname>bind()</methodname> メソッドが成功するかどうかは
                バインド時に認証情報が正しく指定されたかどうかによって決まります。
            </para>

            <para>
                次の例では、複数ドメインでの認証と
                フェイルオーバー機能を実装するために必要な技術を説明します:
            </para>

            <programlisting language="php"><![CDATA[
$acctname = 'W\\user2';
$password = 'pass2';

$multiOptions = array(
    'server1' => array(
        'host'                   => 's0.foo.net',
        'username'               => 'CN=user1,DC=foo,DC=net',
        'password'               => 'pass1',
        'bindRequiresDn'         => true,
        'accountDomainName'      => 'foo.net',
        'accountDomainNameShort' => 'FOO',
        'accountCanonicalForm'   => 4, // ACCT_FORM_PRINCIPAL
        'baseDn'                 => 'OU=Sales,DC=foo,DC=net',
    ),
    'server2' => array(
        'host'                   => 'dc1.w.net',
        'useSsl'                 => true,
        'username'               => 'user1@w.net',
        'password'               => 'pass1',
        'accountDomainName'      => 'w.net',
        'accountDomainNameShort' => 'W',
        'accountCanonicalForm'   => 4, // ACCT_FORM_PRINCIPAL
        'baseDn'                 => 'CN=Users,DC=w,DC=net',
    ),
);

$ldap = new Zend_Ldap();

foreach ($multiOptions as $name => $options) {

    echo "Trying to bind using server options for '$name'\n";

    $ldap->setOptions($options);
    try {
        $ldap->bind($acctname, $password);
        $acctname = $ldap->getCanonicalAccountName($acctname);
        echo "SUCCESS: authenticated $acctname\n";
        return;
    } catch (Zend_Ldap_Exception $zle) {
        echo '  ' . $zle->getMessage() . "\n";
        if ($zle->getCode() === Zend_Ldap_Exception::LDAP_X_DOMAIN_MISMATCH) {
            continue;
        }
    }
}
]]></programlisting>

            <para>
                何らかの理由でバインドに失敗すると、その次のセットのサーバオプションでバインドを試みます。
            </para>

            <para>
                <methodname>getCanonicalAccountName()</methodname> をコールすると、
                正規化したアカウント名を取得できます。
                これを使用して、アプリケーションから関連データを取得できるようになります。
                <emphasis>accountCanonicalForm = 4</emphasis> をすべてのサーバのオプションに設定することで、
                どのサーバを使用する場合にも一貫した正規化が行えるようになっています。
            </para>

            <para>
                ドメイン部つきのアカウント名 (単なる <emphasis>abaker</emphasis>
                ではなく <filename>abaker@foo.net</filename> や <emphasis>FOO\abaker</emphasis> など)
                を指定した場合は、そのドメインが設定済みのオプションのどれとも一致しなければ
                特別な例外 <constant>LDAP_X_DOMAIN_MISMATCH</constant> が発生します。
                この例外は、そのアカウントがサーバに見つからないことを表します。
                この場合はバインドは行われず、
                サーバとの余計な通信は発生しません。
                この例では <emphasis>continue</emphasis> という指示は無意味であることに注意しましょう。
                しかし、実際には、エラー処理やデバッグなどのために
                <constant>LDAP_NO_SUCH_OBJECT</constant> と <constant>LDAP_INVALID_CREDENTIALS</constant>
                だけではなく <constant>LDAP_X_DOMAIN_MISMATCH</constant> もチェックすることになるでしょう。
            </para>

            <para>
                上のコードは、<link
                    linkend="zend.auth.adapter.ldap"><classname>Zend_Auth_Adapter_Ldap</classname></link>
                の中で使用するコードと非常によく似ています。実際のところ、
                複数ドメインとフェイルオーバー機能をもつ <acronym>LDAP</acronym> 基本認証を行うのなら、
                この認証アダプタを使用する (あるいはコードをコピーする) ことをおすすめします。
            </para>
        </sect3>
    </sect2>
</sect1>
