File: Zend_Ldap-Introduction.xml

package info (click to toggle)
zendframework 1.12.9%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 133,584 kB
  • sloc: xml: 1,311,829; php: 570,173; sh: 170; makefile: 125; sql: 121
file content (324 lines) | stat: -rw-r--r-- 15,098 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.ldap.introduction">
    <title>Introduction</title>

    <para>
        <classname>Zend_Ldap</classname> is a class for performing <acronym>LDAP</acronym>
        operations including but not limited to binding, searching and modifying entries
        in an <acronym>LDAP</acronym> directory.
    </para>

    <sect2 id="zend.ldap.introduction.theory-of-operations">
        <title>Theory of operation</title>

        <para>
            This component currently consists of the main <classname>Zend_Ldap</classname> class,
            that conceptually represents a binding to a single <acronym>LDAP</acronym> server
            and allows for executing operations against a <acronym>LDAP</acronym> server such
            as OpenLDAP or ActiveDirectory (AD) servers. The parameters for binding may be
            provided explicitly or in the form of an options array.
            <classname>Zend_Ldap_Node</classname> provides an object-oriented interface
            for single <acronym>LDAP</acronym> nodes and can be used to form a basis for an
            active-record-like interface for a <acronym>LDAP</acronym>-based domain model.
        </para>

        <para>
            The component provides several helper classes to perform operations on
            <acronym>LDAP</acronym> entries (<classname>Zend_Ldap_Attribute</classname>) such as
            setting and retrieving attributes (date values, passwords, boolean values, ...), to
            create and modify <acronym>LDAP</acronym> filter strings
            (<classname>Zend_Ldap_Filter</classname>) and to manipulate <acronym>LDAP</acronym>
            distinguished names (DN) (<classname>Zend_Ldap_Dn</classname>).
        </para>

        <para>
            Additionally the component abstracts <acronym>LDAP</acronym> schema browsing
            for OpenLDAP and ActiveDirectoy servers <classname>Zend_Ldap_Node_Schema</classname>
            and server information retrieval for OpenLDAP-, ActiveDirectory- and Novell
            eDirectory servers (<classname>Zend_Ldap_Node_RootDse</classname>).
        </para>

        <para>
            Using the <classname>Zend_Ldap</classname> class depends on the type of
            <acronym>LDAP</acronym> server and is best summarized with some simple examples.
        </para>

        <para>
            If you are using OpenLDAP, a simple example looks like the following
            (note that the <emphasis>bindRequiresDn</emphasis> option is important if you are
            <emphasis>not</emphasis> using AD):
        </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>
            If you are using Microsoft AD a simple example is:
        </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>
            Note that we use the <methodname>getCanonicalAccountName()</methodname> method
            to retrieve the account DN here only because that is what exercises the
            most of what little code is currently present in this class.
        </para>

        <sect3 id="zend.ldap.introduction.theory-of-operations.automatic-username-canonicalization">
            <title>Automatic Username Canonicalization When Binding</title>

            <para>
                If <methodname>bind()</methodname> is called with a non-DN username but
                <emphasis>bindRequiresDN</emphasis> is <constant>TRUE</constant> and no username in
                DN form was supplied as an option, the bind will fail. However, if a username in DN
                form is supplied in the options array, <classname>Zend_Ldap</classname> will
                first bind with that username, retrieve the account DN for the username
                supplied to <methodname>bind()</methodname> and then re-bind with that DN.
            </para>

            <para>
                This behavior is critical to <link
                    linkend="zend.auth.adapter.ldap"><classname>Zend_Auth_Adapter_Ldap</classname></link>,
                which passes the username supplied by the user directly to
                <methodname>bind()</methodname>.
            </para>

            <para>
                The following example illustrates how the non-DN username
                '<emphasis>abaker</emphasis>' can be used with <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>
                The <methodname>bind()</methodname> call in this example sees that
                the username '<emphasis>abaker</emphasis>' is not in DN form, finds
                <emphasis>bindRequiresDn</emphasis> is <constant>TRUE</constant>, uses
                '<command>CN=user1,DC=foo,DC=net</command>' and '<emphasis>pass1</emphasis>' to
                bind, retrieves the DN for '<emphasis>abaker</emphasis>', unbinds and then rebinds
                with the newly discovered
                '<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>Account Name Canonicalization</title>

            <para>
                The <emphasis>accountDomainName</emphasis> and
                <emphasis>accountDomainNameShort</emphasis>
                options are used for two purposes: (1) they facilitate multi-domain
                authentication and failover capability, and (2) they are also used to
                canonicalize usernames. Specifically, names are canonicalized to the
                form specified by the <emphasis>accountCanonicalForm</emphasis> option.
                This option may one of the following values:
            </para>

            <table id="zend.ldap.using.theory-of-operation.account-name-canonicalization.table">
                <title>Options for accountCanonicalForm</title>

                <tgroup cols="3">
                    <thead>
                        <row>
                            <entry>Name</entry>
                            <entry>Value</entry>
                            <entry>Example</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>
                The default canonicalization depends on what account domain name options
                were supplied. If <emphasis>accountDomainNameShort</emphasis> was supplied, the
                default <emphasis>accountCanonicalForm</emphasis> value is
                <constant>ACCTNAME_FORM_BACKSLASH</constant>. Otherwise, if
                <emphasis>accountDomainName</emphasis> was supplied, the
                default is <constant>ACCTNAME_FORM_PRINCIPAL</constant>.
            </para>

            <para>
                Account name canonicalization ensures that the string used to identify
                an account is consistent regardless of what was supplied to
                <methodname>bind()</methodname>. For example, if the user supplies an account
                name of <filename>abaker@example.com</filename> or just
                <emphasis>abaker</emphasis> and the <emphasis>accountCanonicalForm</emphasis>
                is set to 3, the resulting canonicalized name would be
                <emphasis>EXAMPLE\abaker</emphasis>.
            </para>
        </sect3>

        <sect3 id="zend.ldap.introduction.theory-of-operations.multi-domain-failover">
            <title>Multi-domain Authentication and Failover</title>

            <para>
                The <classname>Zend_Ldap</classname> component by itself makes no attempt
                to authenticate with multiple servers. However, <classname>Zend_Ldap</classname>
                is specifically designed to handle this scenario gracefully. The
                required technique is to simply iterate over an array of arrays of serve
                 options and attempt to bind with each server. As described above
                 <methodname>bind()</methodname> will automatically canonicalize each name, so
                it does not matter if the user passes <filename>abaker@foo.net</filename> or
                <emphasis>W\bcarter</emphasis> or <emphasis>cdavis</emphasis> - the
                <methodname>bind()</methodname> method will only succeed if the credentials were
                successfully used in the bind.
            </para>

            <para>
                Consider the following example that illustrates the technique required to
                implement multi-domain authentication and failover:
            </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>
                If the bind fails for any reason, the next set of server options is tried.
            </para>

            <para>
                The <methodname>getCanonicalAccountName()</methodname> call gets the canonical
                account name that the application would presumably use to associate data with such
                as preferences. The <emphasis>accountCanonicalForm = 4</emphasis> in all server
                options ensures that the canonical form is consistent regardless of which
                server was ultimately used.
            </para>

            <para>
                The special <constant>LDAP_X_DOMAIN_MISMATCH</constant> exception occurs when an
                account name with a domain component was supplied (e.g.,
                <filename>abaker@foo.net</filename> or <emphasis>FOO\abaker</emphasis> and not just
                <emphasis>abaker</emphasis>) but the domain component did not match either domain
                in the currently selected server options. This exception indicates
                that the server is not an authority for the account. In this
                case, the bind will not be performed, thereby eliminating unnecessary
                communication with the server. Note that the <emphasis>continue</emphasis>
                instruction has no effect in this example, but in practice for error handling and
                debugging purposes, you will probably want to check for
                <constant>LDAP_X_DOMAIN_MISMATCH</constant> as well as
                <constant>LDAP_NO_SUCH_OBJECT</constant> and
                <constant>LDAP_INVALID_CREDENTIALS</constant>.
            </para>

            <para>
                The above code is very similar to code used within <link
                    linkend="zend.auth.adapter.ldap"><classname>Zend_Auth_Adapter_Ldap</classname></link>.
                In fact, we recommend that you simply use that authentication adapter for
                multi-domain + failover <acronym>LDAP</acronym> based authentication
                (or copy the code).
            </para>
        </sect3>
    </sect2>
</sect1>