mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
30.25.2013 ad636a8035cc19f5a412b6b97b3f95ba37d0fe9a
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011-2012 ForgeRock AS
 */
 
package org.forgerock.opendj.ldap;
 
import java.util.Collection;
import java.util.Collections;
 
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.schema.CoreSchema;
 
import com.forgerock.opendj.util.Collections2;
import com.forgerock.opendj.util.FutureResultTransformer;
import com.forgerock.opendj.util.Validator;
 
/**
 * The root DSE is a DSA-specific Entry (DSE) and not part of any naming context
 * (or any subtree), and which is uniquely identified by the empty DN.
 * <p>
 * A Directory Server uses the root DSE to provide information about itself
 * using the following set of attributes:
 * <ul>
 * <li>{@code altServer}: alternative Directory Servers
 * <li>{@code namingContexts}: naming contexts
 * <li>{@code supportedControl}: recognized LDAP controls
 * <li>{@code supportedExtension}: recognized LDAP extended operations
 * <li>{@code supportedFeatures}: recognized LDAP features
 * <li>{@code supportedLDAPVersion}: LDAP versions supported
 * <li>{@code supportedSASLMechanisms}: recognized SASL authentication
 * mechanisms
 * <li>{@code supportedAuthPasswordSchemes}: recognized authentication password
 * schemes
 * <li>{@code subschemaSubentry}: the name of the subschema subentry holding the
 * schema controlling the Root DSE
 * <li>{@code vendorName}: the name of the Directory Server implementer
 * <li>{@code vendorVersion}: the version of the Directory Server
 * implementation.
 * </ul>
 * The values provided for these attributes may depend on session- specific and
 * other factors. For example, a server supporting the SASL EXTERNAL mechanism
 * might only list "EXTERNAL" when the client's identity has been established by
 * a lower level.
 * <p>
 * The root DSE may also include a {@code subschemaSubentry} attribute. If it
 * does, the attribute refers to the subschema (sub)entry holding the schema
 * controlling the root DSE. Clients SHOULD NOT assume that this subschema
 * (sub)entry controls other entries held by the server.
 *
 * @see <a href="http://tools.ietf.org/html/rfc4512">RFC 4512 - Lightweight
 *      Directory Access Protocol (LDAP): Directory Information Models </a>
 * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing Vendor
 *      Information in the LDAP Root DSE </a>
 * @see <a href="http://tools.ietf.org/html/rfc3112">RFC 3112 - LDAP
 *      Authentication Password Schema </a>
 */
public final class RootDSE {
    private static final AttributeDescription ATTR_ALT_SERVER = AttributeDescription
            .create(CoreSchema.getAltServerAttributeType());
 
    private static final AttributeDescription ATTR_NAMING_CONTEXTS = AttributeDescription
            .create(CoreSchema.getNamingContextsAttributeType());
 
    private static final AttributeDescription ATTR_SUBSCHEMA_SUBENTRY = AttributeDescription
            .create(CoreSchema.getSubschemaSubentryAttributeType());
 
    private static final AttributeDescription ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES =
            AttributeDescription.create(CoreSchema.getSupportedAuthPasswordSchemesAttributeType());
 
    private static final AttributeDescription ATTR_SUPPORTED_CONTROL = AttributeDescription
            .create(CoreSchema.getSupportedControlAttributeType());
 
    private static final AttributeDescription ATTR_SUPPORTED_EXTENSION = AttributeDescription
            .create(CoreSchema.getSupportedExtensionAttributeType());
 
    private static final AttributeDescription ATTR_SUPPORTED_FEATURE = AttributeDescription
            .create(CoreSchema.getSupportedFeaturesAttributeType());
 
    private static final AttributeDescription ATTR_SUPPORTED_LDAP_VERSION = AttributeDescription
            .create(CoreSchema.getSupportedLDAPVersionAttributeType());
 
    private static final AttributeDescription ATTR_SUPPORTED_SASL_MECHANISMS = AttributeDescription
            .create(CoreSchema.getSupportedSASLMechanismsAttributeType());
 
    private static final AttributeDescription ATTR_VENDOR_NAME = AttributeDescription
            .create(CoreSchema.getVendorNameAttributeType());
 
    private static final AttributeDescription ATTR_VENDOR_VERSION = AttributeDescription
            .create(CoreSchema.getVendorNameAttributeType());
 
    private static final SearchRequest SEARCH_REQUEST = Requests.newSearchRequest(DN.rootDN(),
            SearchScope.BASE_OBJECT, Filter.objectClassPresent(), ATTR_ALT_SERVER.toString(),
            ATTR_NAMING_CONTEXTS.toString(), ATTR_SUPPORTED_CONTROL.toString(),
            ATTR_SUPPORTED_EXTENSION.toString(), ATTR_SUPPORTED_FEATURE.toString(),
            ATTR_SUPPORTED_LDAP_VERSION.toString(), ATTR_SUPPORTED_SASL_MECHANISMS.toString(),
            ATTR_VENDOR_NAME.toString(), ATTR_VENDOR_VERSION.toString(),
            ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES.toString(), ATTR_SUBSCHEMA_SUBENTRY.toString(),
            "*");
 
    /**
     * Asynchronously reads the Root DSE from the Directory Server using the
     * provided connection.
     * <p>
     * If the Root DSE is not returned by the Directory Server then the request
     * will fail with an {@link EntryNotFoundException}. More specifically, the
     * returned future will never return {@code null}.
     *
     * @param connection
     *            A connection to the Directory Server whose Root DSE is to be
     *            read.
     * @param handler
     *            A result handler which can be used to asynchronously process
     *            the operation result when it is received, may be {@code null}.
     * @return A future representing the result of the operation.
     * @throws UnsupportedOperationException
     *             If the connection does not support search operations.
     * @throws IllegalStateException
     *             If the connection has already been closed, i.e. if
     *             {@code isClosed() == true}.
     * @throws NullPointerException
     *             If the {@code connection} was {@code null}.
     */
    public static FutureResult<RootDSE> readRootDSEAsync(final Connection connection,
            final ResultHandler<? super RootDSE> handler) {
        final FutureResultTransformer<SearchResultEntry, RootDSE> future =
                new FutureResultTransformer<SearchResultEntry, RootDSE>(handler) {
 
                    @Override
                    protected RootDSE transformResult(final SearchResultEntry result)
                            throws ErrorResultException {
                        return valueOf(result);
                    }
 
                };
 
        final FutureResult<SearchResultEntry> innerFuture =
                connection.searchSingleEntryAsync(SEARCH_REQUEST, future);
        future.setFutureResult(innerFuture);
        return future;
    }
 
    /**
     * Reads the Root DSE from the Directory Server using the provided
     * connection.
     * <p>
     * If the Root DSE is not returned by the Directory Server then the request
     * will fail with an {@link EntryNotFoundException}. More specifically, this
     * method will never return {@code null}.
     *
     * @param connection
     *            A connection to the Directory Server whose Root DSE is to be
     *            read.
     * @return The Directory Server's Root DSE.
     * @throws ErrorResultException
     *             If the result code indicates that the request failed for some
     *             reason.
     * @throws UnsupportedOperationException
     *             If the connection does not support search operations.
     * @throws IllegalStateException
     *             If the connection has already been closed, i.e. if
     *             {@code isClosed() == true}.
     * @throws NullPointerException
     *             If the {@code connection} was {@code null}.
     */
    public static RootDSE readRootDSE(final Connection connection) throws ErrorResultException {
        final Entry entry = connection.searchSingleEntry(SEARCH_REQUEST);
        return valueOf(entry);
    }
 
    /**
     * Creates a new Root DSE instance backed by the provided entry.
     * Modifications made to {@code entry} will be reflected in the returned
     * Root DSE. The returned Root DSE instance is unmodifiable and attempts to
     * use modify any of the returned collections will result in a
     * {@code UnsupportedOperationException}.
     *
     * @param entry
     *            The Root DSE entry.
     * @return A Root DSE instance backed by the provided entry.
     * @throws NullPointerException
     *             If {@code entry} was {@code null} .
     */
    public static RootDSE valueOf(Entry entry) {
        Validator.ensureNotNull(entry);
        return new RootDSE(entry);
    }
 
    private final Entry entry;
 
    // Prevent direct instantiation.
    private RootDSE(final Entry entry) {
        this.entry = entry;
    }
 
    /**
     * Returns an unmodifiable list of URIs referring to alternative Directory
     * Servers that may be contacted when the Directory Server becomes
     * unavailable.
     * <p>
     * URIs for Directory Servers implementing the LDAP protocol are written
     * according to RFC 4516. Other kinds of URIs may be provided.
     * <p>
     * If the Directory Server does not know of any other Directory Servers that
     * could be used, the returned list will be empty.
     *
     * @return An unmodifiable list of URIs referring to alternative Directory
     *         Servers, which may be empty.
     * @see <a href="http://tools.ietf.org/html/rfc4516">RFC 4516 - Lightweight
     *      Directory Access Protocol (LDAP): Uniform Resource Locator </a>
     */
    public Collection<String> getAlternativeServers() {
        return getMultiValuedAttribute(ATTR_ALT_SERVER, Functions.byteStringToString());
    }
 
    /**
     * Returns the entry which backs this Root DSE instance. Modifications made
     * to the returned entry will be reflected in this Root DSE.
     *
     * @return The underlying Root DSE entry.
     */
    public Entry getEntry() {
        return entry;
    }
 
    /**
     * Returns an unmodifiable list of DNs identifying the context prefixes of
     * the naming contexts that the Directory Server masters or shadows (in part
     * or in whole).
     * <p>
     * If the Directory Server does not master or shadow any naming contexts,
     * the returned list will be empty.
     *
     * @return An unmodifiable list of DNs identifying the context prefixes of
     *         the naming contexts, which may be empty.
     */
    public Collection<DN> getNamingContexts() {
        return getMultiValuedAttribute(ATTR_NAMING_CONTEXTS, Functions.byteStringToDN());
    }
 
    /**
     * Returns a string which represents the DN of the subschema subentry
     * holding the schema controlling the Root DSE.
     * <p>
     * Clients SHOULD NOT assume that this subschema (sub)entry controls other
     * entries held by the Directory Server.
     *
     * @return The DN of the subschema subentry holding the schema controlling
     *         the Root DSE, or {@code null} if the DN is not provided.
     */
    public DN getSubschemaSubentry() {
        return getSingleValuedAttribute(ATTR_SUBSCHEMA_SUBENTRY, Functions.byteStringToDN());
    }
 
    /**
     * Returns an unmodifiable list of supported authentication password schemes
     * which the Directory Server supports.
     * <p>
     * If the Directory Server does not support any authentication password
     * schemes, the returned list will be empty.
     *
     * @return An unmodifiable list of supported authentication password
     *         schemes, which may be empty.
     * @see <a href="http://tools.ietf.org/html/rfc3112">RFC 3112 - LDAP
     *      Authentication Password Schema </a>
     */
    public Collection<String> getSupportedAuthenticationPasswordSchemes() {
        return getMultiValuedAttribute(ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES, Functions
                .byteStringToString());
    }
 
    /**
     * Returns an unmodifiable list of object identifiers identifying the
     * request controls that the Directory Server supports.
     * <p>
     * If the Directory Server does not support any request controls, the
     * returned list will be empty. Object identifiers identifying response
     * controls may not be listed.
     *
     * @return An unmodifiable list of object identifiers identifying the
     *         request controls, which may be empty.
     */
    public Collection<String> getSupportedControls() {
        return getMultiValuedAttribute(ATTR_SUPPORTED_CONTROL, Functions.byteStringToString());
    }
 
    /**
     * Returns an unmodifiable list of object identifiers identifying the
     * extended operations that the Directory Server supports.
     * <p>
     * If the Directory Server does not support any extended operations, the
     * returned list will be empty.
     * <p>
     * An extended operation generally consists of an extended request and an
     * extended response but may also include other protocol data units (such as
     * intermediate responses). The object identifier assigned to the extended
     * request is used to identify the extended operation. Other object
     * identifiers used in the extended operation may not be listed as values of
     * this attribute.
     *
     * @return An unmodifiable list of object identifiers identifying the
     *         extended operations, which may be empty.
     */
    public Collection<String> getSupportedExtendedOperations() {
        return getMultiValuedAttribute(ATTR_SUPPORTED_EXTENSION, Functions.byteStringToString());
    }
 
    /**
     * Returns an unmodifiable list of object identifiers identifying elective
     * features that the Directory Server supports.
     * <p>
     * If the server does not support any discoverable elective features, the
     * returned list will be empty.
     *
     * @return An unmodifiable list of object identifiers identifying the
     *         elective features, which may be empty.
     */
    public Collection<String> getSupportedFeatures() {
        return getMultiValuedAttribute(ATTR_SUPPORTED_FEATURE, Functions.byteStringToString());
    }
 
    /**
     * Returns an unmodifiable list of the versions of LDAP that the Directory
     * Server supports.
     *
     * @return An unmodifiable list of the versions.
     */
    public Collection<Integer> getSupportedLDAPVersions() {
        return getMultiValuedAttribute(ATTR_SUPPORTED_LDAP_VERSION, Functions.byteStringToInteger());
    }
 
    /**
     * Returns an unmodifiable list of the SASL mechanisms that the Directory
     * Server recognizes and/or supports.
     * <p>
     * The contents of the returned list may depend on the current session state
     * and may be empty if the Directory Server does not support any SASL
     * mechanisms.
     *
     * @return An unmodifiable list of the SASL mechanisms, which may be empty.
     * @see <a href="http://tools.ietf.org/html/rfc4513">RFC 4513 - Lightweight
     *      Directory Access Protocol (LDAP): Authentication Methods and
     *      Security Mechanisms </a>
     * @see <a href="http://tools.ietf.org/html/rfc4422">RFC 4422 - Simple
     *      Authentication and Security Layer (SASL) </a>
     */
    public Collection<String> getSupportedSASLMechanisms() {
        return getMultiValuedAttribute(ATTR_SUPPORTED_SASL_MECHANISMS, Functions
                .byteStringToString());
    }
 
    /**
     * Returns a string which represents the name of the Directory Server
     * implementer.
     *
     * @return The name of the Directory Server implementer, or {@code null} if
     *         the vendor name is not provided.
     * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing
     *      Vendor Information in the LDAP Root DSE </a>
     */
    public String getVendorName() {
        return getSingleValuedAttribute(ATTR_VENDOR_NAME, Functions.byteStringToString());
    }
 
    /**
     * Returns a string which represents the version of the Directory Server
     * implementation.
     * <p>
     * Note that this value is typically a release value comprised of a string
     * and/or a string of numbers used by the developer of the LDAP server
     * product. The returned string will be unique between two versions of the
     * Directory Server, but there are no other syntactic restrictions on the
     * value or the way it is formatted.
     *
     * @return The version of the Directory Server implementation, or
     *         {@code null} if the vendor version is not provided.
     * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing
     *      Vendor Information in the LDAP Root DSE </a>
     */
    public String getVendorVersion() {
        return getSingleValuedAttribute(ATTR_VENDOR_VERSION, Functions.byteStringToString());
    }
 
    private <N> Collection<N> getMultiValuedAttribute(
            final AttributeDescription attributeDescription,
            final Function<ByteString, N, Void> function) {
        // The returned collection is unmodifiable because we may need to
        // return an empty collection if the attribute does not exist in the
        // underlying entry. If a value is then added to the returned empty
        // collection it would require that an attribute is created in the
        // underlying entry in order to maintain consistency.
        final Attribute attr = entry.getAttribute(attributeDescription);
        if (attr != null) {
            return Collections.unmodifiableCollection(Collections2.transformedCollection(attr,
                    function, Functions.objectToByteString()));
        } else {
            return Collections.emptySet();
        }
    }
 
    private <N> N getSingleValuedAttribute(final AttributeDescription attributeDescription,
            final Function<ByteString, N, Void> function) {
        final Attribute attr = entry.getAttribute(attributeDescription);
        if (attr == null || attr.isEmpty()) {
            return null;
        } else {
            return function.apply(attr.firstValue(), null);
        }
    }
 
}