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