001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2009-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2014 ForgeRock AS 026 */ 027package org.forgerock.opendj.ldap; 028 029import java.util.Collection; 030import java.util.Collections; 031 032import org.forgerock.opendj.ldap.requests.Requests; 033import org.forgerock.opendj.ldap.requests.SearchRequest; 034import org.forgerock.opendj.ldap.responses.SearchResultEntry; 035import org.forgerock.opendj.ldap.schema.CoreSchema; 036import org.forgerock.util.Reject; 037import org.forgerock.util.Function; 038import org.forgerock.util.promise.NeverThrowsException; 039 040import com.forgerock.opendj.util.Collections2; 041 042/** 043 * The root DSE is a DSA-specific Entry (DSE) and not part of any naming context 044 * (or any subtree), and which is uniquely identified by the empty DN. 045 * <p> 046 * A Directory Server uses the root DSE to provide information about itself 047 * using the following set of attributes: 048 * <ul> 049 * <li>{@code altServer}: alternative Directory Servers 050 * <li>{@code namingContexts}: naming contexts 051 * <li>{@code supportedControl}: recognized LDAP controls 052 * <li>{@code supportedExtension}: recognized LDAP extended operations 053 * <li>{@code supportedFeatures}: recognized LDAP features 054 * <li>{@code supportedLDAPVersion}: LDAP versions supported 055 * <li>{@code supportedSASLMechanisms}: recognized SASL authentication 056 * mechanisms 057 * <li>{@code supportedAuthPasswordSchemes}: recognized authentication password 058 * schemes 059 * <li>{@code subschemaSubentry}: the name of the subschema subentry holding the 060 * schema controlling the Root DSE 061 * <li>{@code vendorName}: the name of the Directory Server implementer 062 * <li>{@code vendorVersion}: the version of the Directory Server 063 * implementation. 064 * </ul> 065 * The values provided for these attributes may depend on session- specific and 066 * other factors. For example, a server supporting the SASL EXTERNAL mechanism 067 * might only list "EXTERNAL" when the client's identity has been established by 068 * a lower level. 069 * <p> 070 * The root DSE may also include a {@code subschemaSubentry} attribute. If it 071 * does, the attribute refers to the subschema (sub)entry holding the schema 072 * controlling the root DSE. Clients SHOULD NOT assume that this subschema 073 * (sub)entry controls other entries held by the server. 074 * 075 * @see <a href="http://tools.ietf.org/html/rfc4512">RFC 4512 - Lightweight 076 * Directory Access Protocol (LDAP): Directory Information Models </a> 077 * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing Vendor 078 * Information in the LDAP Root DSE </a> 079 * @see <a href="http://tools.ietf.org/html/rfc3112">RFC 3112 - LDAP 080 * Authentication Password Schema </a> 081 */ 082public final class RootDSE { 083 private static final AttributeDescription ATTR_ALT_SERVER = AttributeDescription 084 .create(CoreSchema.getAltServerAttributeType()); 085 086 private static final AttributeDescription ATTR_NAMING_CONTEXTS = AttributeDescription 087 .create(CoreSchema.getNamingContextsAttributeType()); 088 089 private static final AttributeDescription ATTR_SUBSCHEMA_SUBENTRY = AttributeDescription 090 .create(CoreSchema.getSubschemaSubentryAttributeType()); 091 092 private static final AttributeDescription ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES = 093 AttributeDescription.create(CoreSchema.getSupportedAuthPasswordSchemesAttributeType()); 094 095 private static final AttributeDescription ATTR_SUPPORTED_CONTROL = AttributeDescription 096 .create(CoreSchema.getSupportedControlAttributeType()); 097 098 private static final AttributeDescription ATTR_SUPPORTED_EXTENSION = AttributeDescription 099 .create(CoreSchema.getSupportedExtensionAttributeType()); 100 101 private static final AttributeDescription ATTR_SUPPORTED_FEATURE = AttributeDescription 102 .create(CoreSchema.getSupportedFeaturesAttributeType()); 103 104 private static final AttributeDescription ATTR_SUPPORTED_LDAP_VERSION = AttributeDescription 105 .create(CoreSchema.getSupportedLDAPVersionAttributeType()); 106 107 private static final AttributeDescription ATTR_SUPPORTED_SASL_MECHANISMS = AttributeDescription 108 .create(CoreSchema.getSupportedSASLMechanismsAttributeType()); 109 110 private static final AttributeDescription ATTR_VENDOR_NAME = AttributeDescription 111 .create(CoreSchema.getVendorNameAttributeType()); 112 113 private static final AttributeDescription ATTR_VENDOR_VERSION = AttributeDescription 114 .create(CoreSchema.getVendorNameAttributeType()); 115 116 private static final AttributeDescription ATTR_FULL_VENDOR_VERSION = AttributeDescription 117 .create(CoreSchema.getFullVendorVersionAttributeType()); 118 119 private static final SearchRequest SEARCH_REQUEST = Requests.newSearchRequest(DN.rootDN(), 120 SearchScope.BASE_OBJECT, Filter.objectClassPresent(), ATTR_ALT_SERVER.toString(), 121 ATTR_NAMING_CONTEXTS.toString(), ATTR_SUPPORTED_CONTROL.toString(), 122 ATTR_SUPPORTED_EXTENSION.toString(), ATTR_SUPPORTED_FEATURE.toString(), 123 ATTR_SUPPORTED_LDAP_VERSION.toString(), ATTR_SUPPORTED_SASL_MECHANISMS.toString(), 124 ATTR_FULL_VENDOR_VERSION.toString(), 125 ATTR_VENDOR_NAME.toString(), ATTR_VENDOR_VERSION.toString(), 126 ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES.toString(), ATTR_SUBSCHEMA_SUBENTRY.toString(), 127 "*"); 128 129 /** 130 * Asynchronously reads the Root DSE from the Directory Server using the 131 * provided connection. 132 * <p> 133 * If the Root DSE is not returned by the Directory Server then the request 134 * will fail with an {@link EntryNotFoundException}. More specifically, the 135 * returned promise will never return {@code null}. 136 * 137 * @param connection 138 * A connection to the Directory Server whose Root DSE is to be 139 * read. 140 * @param handler 141 * A result handler which can be used to asynchronously process 142 * the operation result when it is received, may be {@code null}. 143 * @return A promise representing the result of the operation. 144 * @throws UnsupportedOperationException 145 * If the connection does not support search operations. 146 * @throws IllegalStateException 147 * If the connection has already been closed, i.e. if 148 * {@code isClosed() == true}. 149 * @throws NullPointerException 150 * If the {@code connection} was {@code null}. 151 */ 152 public static LdapPromise<RootDSE> readRootDSEAsync(final Connection connection, 153 final LdapResultHandler<? super RootDSE> handler) { 154 return connection.searchSingleEntryAsync(SEARCH_REQUEST).then( 155 new Function<SearchResultEntry, RootDSE, LdapException>() { 156 @Override 157 public RootDSE apply(SearchResultEntry result) { 158 return valueOf(result); 159 } 160 }); 161 } 162 163 /** 164 * Reads the Root DSE from the Directory Server using the provided 165 * connection. 166 * <p> 167 * If the Root DSE is not returned by the Directory Server then the request 168 * will fail with an {@link EntryNotFoundException}. More specifically, this 169 * method will never return {@code null}. 170 * 171 * @param connection 172 * A connection to the Directory Server whose Root DSE is to be 173 * read. 174 * @return The Directory Server's Root DSE. 175 * @throws LdapException 176 * If the result code indicates that the request failed for some 177 * reason. 178 * @throws UnsupportedOperationException 179 * If the connection does not support search operations. 180 * @throws IllegalStateException 181 * If the connection has already been closed, i.e. if 182 * {@code isClosed() == true}. 183 * @throws NullPointerException 184 * If the {@code connection} was {@code null}. 185 */ 186 public static RootDSE readRootDSE(final Connection connection) throws LdapException { 187 final Entry entry = connection.searchSingleEntry(SEARCH_REQUEST); 188 return valueOf(entry); 189 } 190 191 /** 192 * Creates a new Root DSE instance backed by the provided entry. 193 * Modifications made to {@code entry} will be reflected in the returned 194 * Root DSE. The returned Root DSE instance is unmodifiable and attempts to 195 * use modify any of the returned collections will result in a 196 * {@code UnsupportedOperationException}. 197 * 198 * @param entry 199 * The Root DSE entry. 200 * @return A Root DSE instance backed by the provided entry. 201 * @throws NullPointerException 202 * If {@code entry} was {@code null} . 203 */ 204 public static RootDSE valueOf(Entry entry) { 205 Reject.ifNull(entry); 206 return new RootDSE(entry); 207 } 208 209 private final Entry entry; 210 211 /** Prevent direct instantiation. */ 212 private RootDSE(final Entry entry) { 213 this.entry = entry; 214 } 215 216 /** 217 * Returns an unmodifiable list of URIs referring to alternative Directory 218 * Servers that may be contacted when the Directory Server becomes 219 * unavailable. 220 * <p> 221 * URIs for Directory Servers implementing the LDAP protocol are written 222 * according to RFC 4516. Other kinds of URIs may be provided. 223 * <p> 224 * If the Directory Server does not know of any other Directory Servers that 225 * could be used, the returned list will be empty. 226 * 227 * @return An unmodifiable list of URIs referring to alternative Directory 228 * Servers, which may be empty. 229 * @see <a href="http://tools.ietf.org/html/rfc4516">RFC 4516 - Lightweight 230 * Directory Access Protocol (LDAP): Uniform Resource Locator </a> 231 */ 232 public Collection<String> getAlternativeServers() { 233 return getMultiValuedAttribute(ATTR_ALT_SERVER, Functions.byteStringToString()); 234 } 235 236 /** 237 * Returns the entry which backs this Root DSE instance. Modifications made 238 * to the returned entry will be reflected in this Root DSE. 239 * 240 * @return The underlying Root DSE entry. 241 */ 242 public Entry getEntry() { 243 return entry; 244 } 245 246 /** 247 * Returns an unmodifiable list of DNs identifying the context prefixes of 248 * the naming contexts that the Directory Server masters or shadows (in part 249 * or in whole). 250 * <p> 251 * If the Directory Server does not master or shadow any naming contexts, 252 * the returned list will be empty. 253 * 254 * @return An unmodifiable list of DNs identifying the context prefixes of 255 * the naming contexts, which may be empty. 256 */ 257 public Collection<DN> getNamingContexts() { 258 return getMultiValuedAttribute(ATTR_NAMING_CONTEXTS, Functions.byteStringToDN()); 259 } 260 261 /** 262 * Returns a string which represents the DN of the subschema subentry 263 * holding the schema controlling the Root DSE. 264 * <p> 265 * Clients SHOULD NOT assume that this subschema (sub)entry controls other 266 * entries held by the Directory Server. 267 * 268 * @return The DN of the subschema subentry holding the schema controlling 269 * the Root DSE, or {@code null} if the DN is not provided. 270 */ 271 public DN getSubschemaSubentry() { 272 return getSingleValuedAttribute(ATTR_SUBSCHEMA_SUBENTRY, Functions.byteStringToDN()); 273 } 274 275 /** 276 * Returns an unmodifiable list of supported authentication password schemes 277 * which the Directory Server supports. 278 * <p> 279 * If the Directory Server does not support any authentication password 280 * schemes, the returned list will be empty. 281 * 282 * @return An unmodifiable list of supported authentication password 283 * schemes, which may be empty. 284 * @see <a href="http://tools.ietf.org/html/rfc3112">RFC 3112 - LDAP 285 * Authentication Password Schema </a> 286 */ 287 public Collection<String> getSupportedAuthenticationPasswordSchemes() { 288 return getMultiValuedAttribute(ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES, Functions 289 .byteStringToString()); 290 } 291 292 /** 293 * Returns an unmodifiable list of object identifiers identifying the 294 * request controls that the Directory Server supports. 295 * <p> 296 * If the Directory Server does not support any request controls, the 297 * returned list will be empty. Object identifiers identifying response 298 * controls may not be listed. 299 * 300 * @return An unmodifiable list of object identifiers identifying the 301 * request controls, which may be empty. 302 */ 303 public Collection<String> getSupportedControls() { 304 return getMultiValuedAttribute(ATTR_SUPPORTED_CONTROL, Functions.byteStringToString()); 305 } 306 307 /** 308 * Returns an unmodifiable list of object identifiers identifying the 309 * extended operations that the Directory Server supports. 310 * <p> 311 * If the Directory Server does not support any extended operations, the 312 * returned list will be empty. 313 * <p> 314 * An extended operation generally consists of an extended request and an 315 * extended response but may also include other protocol data units (such as 316 * intermediate responses). The object identifier assigned to the extended 317 * request is used to identify the extended operation. Other object 318 * identifiers used in the extended operation may not be listed as values of 319 * this attribute. 320 * 321 * @return An unmodifiable list of object identifiers identifying the 322 * extended operations, which may be empty. 323 */ 324 public Collection<String> getSupportedExtendedOperations() { 325 return getMultiValuedAttribute(ATTR_SUPPORTED_EXTENSION, Functions.byteStringToString()); 326 } 327 328 /** 329 * Returns an unmodifiable list of object identifiers identifying elective 330 * features that the Directory Server supports. 331 * <p> 332 * If the server does not support any discoverable elective features, the 333 * returned list will be empty. 334 * 335 * @return An unmodifiable list of object identifiers identifying the 336 * elective features, which may be empty. 337 */ 338 public Collection<String> getSupportedFeatures() { 339 return getMultiValuedAttribute(ATTR_SUPPORTED_FEATURE, Functions.byteStringToString()); 340 } 341 342 /** 343 * Returns an unmodifiable list of the versions of LDAP that the Directory 344 * Server supports. 345 * 346 * @return An unmodifiable list of the versions. 347 */ 348 public Collection<Integer> getSupportedLDAPVersions() { 349 return getMultiValuedAttribute(ATTR_SUPPORTED_LDAP_VERSION, Functions.byteStringToInteger()); 350 } 351 352 /** 353 * Returns an unmodifiable list of the SASL mechanisms that the Directory 354 * Server recognizes and/or supports. 355 * <p> 356 * The contents of the returned list may depend on the current session state 357 * and may be empty if the Directory Server does not support any SASL 358 * mechanisms. 359 * 360 * @return An unmodifiable list of the SASL mechanisms, which may be empty. 361 * @see <a href="http://tools.ietf.org/html/rfc4513">RFC 4513 - Lightweight 362 * Directory Access Protocol (LDAP): Authentication Methods and 363 * Security Mechanisms </a> 364 * @see <a href="http://tools.ietf.org/html/rfc4422">RFC 4422 - Simple 365 * Authentication and Security Layer (SASL) </a> 366 */ 367 public Collection<String> getSupportedSASLMechanisms() { 368 return getMultiValuedAttribute(ATTR_SUPPORTED_SASL_MECHANISMS, Functions 369 .byteStringToString()); 370 } 371 372 /** 373 * Returns a string which represents the name of the Directory Server 374 * implementer. 375 * 376 * @return The name of the Directory Server implementer, or {@code null} if 377 * the vendor name is not provided. 378 * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing 379 * Vendor Information in the LDAP Root DSE </a> 380 */ 381 public String getVendorName() { 382 return getSingleValuedAttribute(ATTR_VENDOR_NAME, Functions.byteStringToString()); 383 } 384 385 /** 386 * Returns a string which represents the version of the Directory Server 387 * implementation. 388 * <p> 389 * Note that this value is typically a release value comprised of a string 390 * and/or a string of numbers used by the developer of the LDAP server 391 * product. The returned string will be unique between two versions of the 392 * Directory Server, but there are no other syntactic restrictions on the 393 * value or the way it is formatted. 394 * 395 * @return The version of the Directory Server implementation, or 396 * {@code null} if the vendor version is not provided. 397 * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing 398 * Vendor Information in the LDAP Root DSE </a> 399 */ 400 public String getVendorVersion() { 401 return getSingleValuedAttribute(ATTR_VENDOR_VERSION, Functions.byteStringToString()); 402 } 403 404 /** 405 * Returns a string which represents the full version of the Directory Server 406 * implementation. 407 * 408 * @return The full version of the Directory Server implementation, or 409 * {@code null} if the vendor version is not provided. 410 */ 411 public String getFullVendorVersion() { 412 return getSingleValuedAttribute(ATTR_FULL_VENDOR_VERSION, Functions.byteStringToString()); 413 } 414 415 private <N> Collection<N> getMultiValuedAttribute( 416 final AttributeDescription attributeDescription, 417 final Function<ByteString, N, NeverThrowsException> function) { 418 // The returned collection is unmodifiable because we may need to 419 // return an empty collection if the attribute does not exist in the 420 // underlying entry. If a value is then added to the returned empty 421 // collection it would require that an attribute is created in the 422 // underlying entry in order to maintain consistency. 423 final Attribute attr = entry.getAttribute(attributeDescription); 424 if (attr != null) { 425 return Collections.unmodifiableCollection(Collections2.transformedCollection(attr, 426 function, Functions.objectToByteString())); 427 } else { 428 return Collections.emptySet(); 429 } 430 } 431 432 private <N> N getSingleValuedAttribute(final AttributeDescription attributeDescription, 433 final Function<ByteString, N, NeverThrowsException> function) { 434 final Attribute attr = entry.getAttribute(attributeDescription); 435 if (attr == null || attr.isEmpty()) { 436 return null; 437 } else { 438 return function.apply(attr.firstValue()); 439 } 440 } 441 442}