| | |
| | | new LDAPConnectionFactory(host, port, getTrustAllOptions()); |
| | | connection = factory.getConnection(); |
| | | PlainSASLBindRequest request = |
| | | Requests.newPlainSASLBindRequest(authcid, passwd.toCharArray()); |
| | | if (authzid != null) { |
| | | request.setAuthorizationID(authzid); |
| | | } |
| | | Requests.newPlainSASLBindRequest(authcid, passwd.toCharArray()) |
| | | .setAuthorizationID(authzid); |
| | | connection.bind(request); |
| | | System.out.println("Authenticated as " + authcid + "."); |
| | | } catch (final ErrorResultException e) { |
| | |
| | | import java.io.IOException; |
| | | |
| | | import org.forgerock.opendj.ldap.Connection; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entries; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | | import org.forgerock.opendj.ldap.ErrorResultException; |
| | |
| | | char[] adminPwd = "bribery".toCharArray(); |
| | | |
| | | // An entry to add to the directory |
| | | DN entryDN = DN.valueOf("cn=Bob,ou=People,dc=example,dc=com"); |
| | | Entry entry = new LinkedHashMapEntry(entryDN.toString()) |
| | | String entryDN = "cn=Bob,ou=People,dc=example,dc=com"; |
| | | Entry entry = new LinkedHashMapEntry(entryDN) |
| | | .addAttribute("cn", "Bob") |
| | | .addAttribute("objectclass", "top") |
| | | .addAttribute("objectclass", "person") |
| | |
| | | System.out.println("...done."); |
| | | |
| | | System.out.println("Renaming the entry..."); |
| | | DN newDN = DN.valueOf("cn=Ted,ou=People,dc=example,dc=com"); |
| | | entry = entry.setName(newDN); |
| | | String newDN = "cn=Ted,ou=People,dc=example,dc=com"; |
| | | entry =entry.setName(newDN); |
| | | writeToConsole(writer, entry); |
| | | connection.modifyDN(entryDN.toString(), "cn=Ted"); |
| | | connection.modifyDN(entryDN, "cn=Ted"); |
| | | System.out.println("...done."); |
| | | |
| | | System.out.println("Deleting the entry..."); |
| | | writeToConsole(writer, entry); |
| | | connection.delete(newDN.toString()); |
| | | connection.delete(newDN); |
| | | System.out.println("...done."); |
| | | } catch (final ErrorResultException e) { |
| | | System.err.println(e.getMessage()); |
| | |
| | | * {@code attribute} is empty then the entire attribute will be removed if |
| | | * it is present. |
| | | * <p> |
| | | * <b>NOTE:</b> This method implements LDAP Modify replace semantics. |
| | | * <b>NOTE:</b> This method implements LDAP Modify replace semantics as |
| | | * described in <a href="http://tools.ietf.org/html/rfc4511#section-4.6" |
| | | * >RFC 4511 - Section 4.6. Modify Operation</a>. |
| | | * |
| | | * @param attribute |
| | | * The attribute values to be added to this entry, replacing any |
| | |
| | | * Any attribute values which are not instances of {@code ByteString} will |
| | | * be converted using the {@link ByteString#valueOf(Object)} method. |
| | | * <p> |
| | | * <b>NOTE:</b> This method implements LDAP Modify replace semantics. |
| | | * <b>NOTE:</b> This method implements LDAP Modify replace semantics as |
| | | * described in <a href="http://tools.ietf.org/html/rfc4511#section-4.6" |
| | | * >RFC 4511 - Section 4.6. Modify Operation</a>. |
| | | * |
| | | * @param attributeDescription |
| | | * The name of the attribute whose values are to be replaced. |
| | |
| | | * underlying connection factories. |
| | | * <p> |
| | | * This algorithm is typically used for load-balancing <i>between</i> data |
| | | * centers, where there is preference to always always forward connection |
| | | * centers, where there is preference to always forward connection |
| | | * requests to the <i>closest available</i> data center. This algorithm |
| | | * contrasts with the {@link RoundRobinLoadBalancingAlgorithm} which is used for |
| | | * load-balancing <i>within</i> a data center. |
| | |
| | | |
| | | /** |
| | | * Common options for LDAP client connections. |
| | | * <p> |
| | | * For example you set LDAP options when you want to use StartTLS. |
| | | * |
| | | * <pre> |
| | | * LDAPOptions options = new LDAPOptions(); |
| | | * SSLContext sslContext = |
| | | * new SSLContextBuilder().setTrustManager(...).getSSLContext(); |
| | | * options.setSSLContext(sslContext); |
| | | * options.setUseStartTLS(true); |
| | | * |
| | | * String host = ...; |
| | | * int port = ...; |
| | | * LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port, options); |
| | | * Connection connection = factory.getConnection(); |
| | | * // Connection uses StartTLS... |
| | | * </pre> |
| | | */ |
| | | public final class LDAPOptions { |
| | | private SSLContext sslContext; |
| | |
| | | * An implementation of the {@code Entry} interface which uses a |
| | | * {@code LinkedHashMap} for storing attributes. Attributes are returned in the |
| | | * same order that they were added to the entry. All operations are supported by |
| | | * this implementation. |
| | | * <p> |
| | | * this implementation. For example, you can build an entry like this: |
| | | * |
| | | * <pre> |
| | | * Entry entry = new LinkedHashMapEntry("cn=Bob,ou=People,dc=example,dc=com") |
| | | * .addAttribute("cn", "Bob") |
| | | * .addAttribute("objectclass", "top") |
| | | * .addAttribute("objectclass", "person") |
| | | * .addAttribute("objectclass", "organizationalPerson") |
| | | * .addAttribute("objectclass", "inetOrgPerson") |
| | | * .addAttribute("mail", "subgenius@example.com") |
| | | * .addAttribute("sn", "Dobbs"); |
| | | * </pre> |
| | | * |
| | | * A {@code LinkedHashMapEntry} stores references to attributes which have been |
| | | * added using the {@link #addAttribute} methods. Attributes sharing the same |
| | | * attribute description are merged by adding the values of the new attribute to |
| | |
| | | * {@link SSLContext} instances for use when securing connections with SSL or |
| | | * the StartTLS extended operation. The {@link #getSSLContext()} should be |
| | | * called in order to obtain the {@code SSLContext}. |
| | | * <p> |
| | | * For example, use the SSL context builder when setting up LDAP options needed |
| | | * to use StartTLS. {@link org.forgerock.opendj.ldap.TrustManagers |
| | | * TrustManagers} has methods you can use to set the trust manager for the SSL |
| | | * context builder. |
| | | * |
| | | * <pre> |
| | | * LDAPOptions options = new LDAPOptions(); |
| | | * SSLContext sslContext = |
| | | * new SSLContextBuilder().setTrustManager(...).getSSLContext(); |
| | | * options.setSSLContext(sslContext); |
| | | * options.setUseStartTLS(true); |
| | | * |
| | | * String host = ...; |
| | | * int port = ...; |
| | | * LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port, options); |
| | | * Connection connection = factory.getConnection(); |
| | | * // Connection uses StartTLS... |
| | | * </pre> |
| | | */ |
| | | public final class SSLContextBuilder { |
| | | |
| | |
| | | * for storing attributes. Attributes are returned in ascending order of |
| | | * attribute description, with {@code objectClass} first, then all user |
| | | * attributes, and finally any operational attributes. All operations are |
| | | * supported by this implementation. |
| | | * supported by this implementation. For example, you can build an entry like |
| | | * this: |
| | | * |
| | | * <pre> |
| | | * Entry entry = new TreeMapEntry("cn=Bob,ou=People,dc=example,dc=com") |
| | | * .addAttribute("cn", "Bob") |
| | | * .addAttribute("objectclass", "top") |
| | | * .addAttribute("objectclass", "person") |
| | | * .addAttribute("objectclass", "organizationalPerson") |
| | | * .addAttribute("objectclass", "inetOrgPerson") |
| | | * .addAttribute("mail", "subgenius@example.com") |
| | | * .addAttribute("sn", "Dobbs"); |
| | | * </pre> |
| | | * |
| | | * <p> |
| | | * A {@code TreeMapEntry} stores references to attributes which have been added |
| | | * using the {@link #addAttribute} methods. Attributes sharing the same |
| | |
| | | * processed if an assertion applied to the target entry of the operation is |
| | | * true. It can be used to construct "test and set", "test and clear", and other |
| | | * conditional operations. |
| | | * <p> |
| | | * The following excerpt shows how to check that no description exists on an |
| | | * entry before adding a description. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * connection.bind(...); |
| | | * |
| | | * String entryDN = ...; |
| | | * ModifyRequest request = |
| | | * Requests.newModifyRequest(entryDN) |
| | | * .addControl(AssertionRequestControl.newControl( |
| | | * true, Filter.valueOf("!(description=*)"))) |
| | | * .addModification(ModificationType.ADD, "description", |
| | | * "Created using LDAP assertion control"); |
| | | * |
| | | * connection.modify(request); |
| | | * ... |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc4528">RFC 4528 - Lightweight |
| | | * Directory Access Protocol (LDAP) Assertion Control </a> |
| | |
| | | * identity control extends the Lightweight Directory Access Protocol (LDAP) |
| | | * bind operation with a mechanism for requesting and returning the |
| | | * authorization identity it establishes. |
| | | * <p> |
| | | * The following excerpt shows how to get the authorization identity established |
| | | * when binding to the directory server. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String bindDN = ...; |
| | | * String bindPassword = ...; |
| | | * |
| | | * BindRequest request = |
| | | * Requests.newSimpleBindRequest(bindDN, bindPassword.toCharArray()) |
| | | * .addControl(AuthorizationIdentityRequestControl |
| | | * .newControl(true)); |
| | | * |
| | | * BindResult result = connection.bind(request); |
| | | * AuthorizationIdentityResponseControl control = |
| | | * result.getControl(AuthorizationIdentityResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * // Authorization ID returned: control.getAuthorizationID() |
| | | * </pre> |
| | | * |
| | | * @see AuthorizationIdentityResponseControl |
| | | * @see org.forgerock.opendj.ldap.requests.WhoAmIExtendedRequest |
| | |
| | | * <p> |
| | | * The authorization identity is specified using an authorization ID, or |
| | | * {@code authzId}, as defined in RFC 4513 section 5.2.1.8. |
| | | * <p> |
| | | * The following excerpt shows how to get the authorization identity established |
| | | * when binding to the directory server. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String bindDN = ...; |
| | | * String bindPassword = ...; |
| | | * |
| | | * BindRequest request = |
| | | * Requests.newSimpleBindRequest(bindDN, bindPassword.toCharArray()) |
| | | * .addControl(AuthorizationIdentityRequestControl |
| | | * .newControl(true)); |
| | | * |
| | | * BindResult result = connection.bind(request); |
| | | * AuthorizationIdentityResponseControl control = |
| | | * result.getControl(AuthorizationIdentityResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * // Authorization ID returned: control.getAuthorizationID() |
| | | * </pre> |
| | | * |
| | | * @see AuthorizationIdentityRequestControl |
| | | * @see org.forgerock.opendj.ldap.requests.WhoAmIExtendedRequest |
| | |
| | | * single LDAP message. A control only affects the semantics of the message it |
| | | * is attached to. Controls sent by clients are termed 'request controls', and |
| | | * those sent by servers are termed 'response controls'. |
| | | * <p> |
| | | * To determine whether a directory server supports a given control, read the |
| | | * list of supported controls from the root DSE to get a collection of control |
| | | * OIDs, and then check for a match: |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * Collection<String> supported = |
| | | * RootDSE.readRootDSE(connection).getSupportedControls(); |
| | | * |
| | | * Control control = ...; |
| | | * String OID = control.getOID(); |
| | | * if (supported != null && !supported.isEmpty() && supported.contains(OID)) { |
| | | * // The control is supported. Use it here... |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc4511">RFC 4511 - Lightweight |
| | | * Directory Access Protocol (LDAP): The Protocol </a> |
| | |
| | | * about the change the caused a particular entry to be returned as the result |
| | | * of a persistent search. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * |
| | | * SearchRequest request = |
| | | * Requests.newSearchRequest( |
| | | * "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, |
| | | * "(objectclass=inetOrgPerson)", "cn") |
| | | * .addControl(PersistentSearchRequestControl.newControl( |
| | | * true, true, true, // critical,changesOnly,returnECs |
| | | * PersistentSearchChangeType.ADD, |
| | | * PersistentSearchChangeType.DELETE, |
| | | * PersistentSearchChangeType.MODIFY, |
| | | * PersistentSearchChangeType.MODIFY_DN)); |
| | | * |
| | | * ConnectionEntryReader reader = connection.search(request); |
| | | * |
| | | * while (reader.hasNext()) { |
| | | * if (!reader.isReference()) { |
| | | * SearchResultEntry entry = reader.readEntry(); // Entry that changed |
| | | * |
| | | * EntryChangeNotificationResponseControl control = entry.getControl( |
| | | * EntryChangeNotificationResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * |
| | | * PersistentSearchChangeType type = control.getChangeType(); |
| | | * if (type.equals(PersistentSearchChangeType.MODIFY_DN)) { |
| | | * // Previous DN: control.getPreviousName() |
| | | * } |
| | | * // Change number: control.getChangeNumber()); |
| | | * } |
| | | * } |
| | | * |
| | | * </pre> |
| | | * |
| | | * @see PersistentSearchRequestControl |
| | | * @see PersistentSearchChangeType |
| | | * @see <a |
| | |
| | | * } |
| | | * </pre> |
| | | * |
| | | * You can use the control to retrieve effective rights during a search: |
| | | * |
| | | * <pre> |
| | | * String authDN = ...; |
| | | * |
| | | * SearchRequest request = |
| | | * Requests.newSearchRequest( |
| | | * "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, |
| | | * "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo") |
| | | * .addControl(GetEffectiveRightsRequestControl.newControl( |
| | | * true, authDN, "cn")); |
| | | * |
| | | * ConnectionEntryReader reader = connection.search(request); |
| | | * while (reader.hasNext()) { |
| | | * if (!reader.isReference()) { |
| | | * SearchResultEntry entry = reader.readEntry(); |
| | | * // Interpret aclRights and aclRightsInfo |
| | | * } |
| | | * } |
| | | * </pre> |
| | | * |
| | | * The entries returned by the search hold the {@code aclRights} and |
| | | * {@code aclRightsInfo} attributes with the effective rights information. You |
| | | * must parse the attribute options and values to interpret the information. |
| | | * |
| | | * @see <a |
| | | * href="http://tools.ietf.org/html/draft-ietf-ldapext-acl-model">draft-ietf-ldapext-acl-model |
| | | * - Access Control Model for LDAPv3 </a> |
| | |
| | | * objects and instead will treat the referral object as a normal entry. The |
| | | * server, however, is still free to return referrals for other reasons. |
| | | * |
| | | * <pre> |
| | | * // "dc=ref,dc=com" holds a referral to something else. |
| | | * |
| | | * // Referral without the ManageDsaIT control: |
| | | * SearchRequest request = Requests.newSearchRequest( |
| | | * "dc=ref,dc=com", |
| | | * SearchScope.SUBORDINATES, |
| | | * "(objectclass=*)", |
| | | * ""); |
| | | * |
| | | * ConnectionEntryReader reader = connection.search(request); |
| | | * while (reader.hasNext()) { |
| | | * if (reader.isReference()) { |
| | | * SearchResultReference ref = reader.readReference(); |
| | | * // References: ref.getURIs() |
| | | * } |
| | | * } |
| | | * |
| | | * // Referral with the ManageDsaIT control: |
| | | * request.addControl(ManageDsaITRequestControl.newControl(true)); |
| | | * SearchResultEntry entry = connection.searchSingleEntry(request); |
| | | * // ... |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc3296">RFC 3296 - Named |
| | | * Subordinate References in Lightweight Directory Access Protocol (LDAP) |
| | | * Directories </a> |
| | |
| | | * matchValue [3] AssertionValue} |
| | | * </pre> |
| | | * |
| | | * For example Barbara Jensen's entry contains two common name values, Barbara |
| | | * Jensen and Babs Jensen. The following code retrieves only the latter. |
| | | * |
| | | * <pre> |
| | | * String DN = "uid=bjensen,ou=People,dc=example,dc=com"; |
| | | * SearchRequest request = Requests.newSearchRequest(DN, |
| | | * SearchScope.BASE_OBJECT, "(objectclass=*)", "cn") |
| | | * .addControl(MatchedValuesRequestControl |
| | | * .newControl(true, "(cn=Babs Jensen)")); |
| | | * |
| | | * // Get the entry, retrieving cn: Babs Jensen, not cn: Barbara Jensen |
| | | * SearchResultEntry entry = connection.searchSingleEntry(request); |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc3876">RFC 3876 - Returning |
| | | * Matched Values with the Lightweight Directory Access Protocol version 3 |
| | | * (LDAPv3) </a> |
| | |
| | | * password has expired and must be changed. This control always has a value |
| | | * which is the string {@code "0"}. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String DN = ...; |
| | | * char[] password = ...; |
| | | * |
| | | * try { |
| | | * connection.bind(DN, password); |
| | | * } catch (ErrorResultException e) { |
| | | * Result result = e.getResult(); |
| | | * try { |
| | | * PasswordExpiredResponseControl control = |
| | | * result.getControl(PasswordExpiredResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * if (!(control == null) && control.hasValue()) { |
| | | * // Password expired |
| | | * } |
| | | * } catch (DecodeException de) { |
| | | * // Failed to decode the response control. |
| | | * } |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see <a |
| | | * href="http://tools.ietf.org/html/draft-vchu-ldap-pwd-policy">draft-vchu-ldap-pwd-policy |
| | | * - Password Policy for LDAP Directories </a> |
| | |
| | | * control value is a string representation of the number of seconds until |
| | | * expiration. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String DN = ...; |
| | | * char[] password = ...; |
| | | * |
| | | * BindResult result = connection.bind(DN, password); |
| | | * try { |
| | | * PasswordExpiringResponseControl control = |
| | | * result.getControl(PasswordExpiringResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * if (!(control == null) && control.hasValue()) { |
| | | * // Password expires in control.getSecondsUntilExpiration() seconds |
| | | * } |
| | | * } catch (DecodeException de) { |
| | | * // Failed to decode the response control. |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see <a |
| | | * href="http://tools.ietf.org/html/draft-vchu-ldap-pwd-policy">draft-vchu-ldap-pwd-policy |
| | | * - Password Policy for LDAP Directories </a> |
| | |
| | | * control. When a server receives this control, it will return the password |
| | | * policy response control when appropriate and with the proper data. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String DN = ...; |
| | | * char[] password = ...; |
| | | * |
| | | * try { |
| | | * BindRequest request = Requests.newSimpleBindRequest(DN, password) |
| | | * .addControl(PasswordPolicyRequestControl.newControl(true)); |
| | | * |
| | | * BindResult result = connection.bind(request); |
| | | * |
| | | * PasswordPolicyResponseControl control = |
| | | * result.getControl(PasswordPolicyResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * if (!(control == null) && !(control.getWarningType() == null)) { |
| | | * // Password policy warning, use control.getWarningType(), |
| | | * // and control.getWarningValue(). |
| | | * } |
| | | * } catch (ErrorResultException e) { |
| | | * Result result = e.getResult(); |
| | | * try { |
| | | * PasswordPolicyResponseControl control = |
| | | * result.getControl(PasswordPolicyResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * if (!(control == null)) { |
| | | * // Password policy error, use control.getErrorType(). |
| | | * } |
| | | * } catch (DecodeException de) { |
| | | * // Failed to decode the response control. |
| | | * } |
| | | * } catch (DecodeException e) { |
| | | * // Failed to decode the response control. |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see PasswordPolicyResponseControl |
| | | * @see <a href="http://tools.ietf.org/html/draft-behera-ldap-password-policy"> |
| | | * draft-behera-ldap-password-policy - Password Policy for LDAP Directories |
| | |
| | | /** |
| | | * The password policy response control as defined in |
| | | * draft-behera-ldap-password-policy. |
| | | * <p> |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String DN = ...; |
| | | * char[] password = ...; |
| | | * |
| | | * try { |
| | | * BindRequest request = Requests.newSimpleBindRequest(DN, password) |
| | | * .addControl(PasswordPolicyRequestControl.newControl(true)); |
| | | * |
| | | * BindResult result = connection.bind(request); |
| | | * |
| | | * PasswordPolicyResponseControl control = |
| | | * result.getControl(PasswordPolicyResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * if (!(control == null) && !(control.getWarningType() == null)) { |
| | | * // Password policy warning, use control.getWarningType(), |
| | | * // and control.getWarningValue(). |
| | | * } |
| | | * } catch (ErrorResultException e) { |
| | | * Result result = e.getResult(); |
| | | * try { |
| | | * PasswordPolicyResponseControl control = |
| | | * result.getControl(PasswordPolicyResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * if (!(control == null)) { |
| | | * // Password policy error, use control.getErrorType(). |
| | | * } |
| | | * } catch (DecodeException de) { |
| | | * // Failed to decode the response control. |
| | | * } |
| | | * } catch (DecodeException e) { |
| | | * // Failed to decode the response control. |
| | | * } |
| | | * </pre> |
| | | * |
| | | * If the client has sent a passwordPolicyRequest control, the server (when |
| | | * solicited by the inclusion of the request control) sends this control with |
| | | * the following operation responses: bindResponse, modifyResponse, addResponse, |
| | |
| | | * the attribute contains the specified attribute value, and a {@code delete} |
| | | * modification <i>ensures</i> that the attribute does not contain the specified |
| | | * attribute value. |
| | | * |
| | | * <pre> |
| | | * String groupDN = ...; |
| | | * String memberDN = ...; |
| | | * Connection connection = ...; |
| | | * |
| | | * // Add a member to a static group, telling the directory server not to |
| | | * // complain if the member already belongs to the group. |
| | | * ModifyRequest request = Requests.newModifyRequest(groupDN) |
| | | * .addControl(PermissiveModifyRequestControl.newControl(true)) |
| | | * .addModification(ModificationType.ADD, "member", memberDN); |
| | | * connection.modify(request); |
| | | * </pre> |
| | | */ |
| | | public final class PermissiveModifyRequestControl implements Control { |
| | | /** |
| | |
| | | * The persistent search request control as defined in |
| | | * draft-ietf-ldapext-psearch. This control allows a client to receive |
| | | * notification of changes that occur in an LDAP server. |
| | | * <p> |
| | | * You can examine the entry change notification response control to get more |
| | | * information about a change returned by the persistent search. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * |
| | | * SearchRequest request = |
| | | * Requests.newSearchRequest( |
| | | * "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, |
| | | * "(objectclass=inetOrgPerson)", "cn") |
| | | * .addControl(PersistentSearchRequestControl.newControl( |
| | | * true, true, true, // critical,changesOnly,returnECs |
| | | * PersistentSearchChangeType.ADD, |
| | | * PersistentSearchChangeType.DELETE, |
| | | * PersistentSearchChangeType.MODIFY, |
| | | * PersistentSearchChangeType.MODIFY_DN)); |
| | | * |
| | | * ConnectionEntryReader reader = connection.search(request); |
| | | * |
| | | * while (reader.hasNext()) { |
| | | * if (!reader.isReference()) { |
| | | * SearchResultEntry entry = reader.readEntry(); // Entry that changed |
| | | * |
| | | * EntryChangeNotificationResponseControl control = entry.getControl( |
| | | * EntryChangeNotificationResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * |
| | | * PersistentSearchChangeType type = control.getChangeType(); |
| | | * if (type.equals(PersistentSearchChangeType.MODIFY_DN)) { |
| | | * // Previous DN: control.getPreviousName() |
| | | * } |
| | | * // Change number: control.getChangeNumber()); |
| | | * } |
| | | * } |
| | | * |
| | | * </pre> |
| | | * |
| | | * @see EntryChangeNotificationResponseControl |
| | | * @see PersistentSearchChangeType |
| | |
| | | * client to read the target entry of an update operation immediately after the |
| | | * modifications are applied. These reads are done as an atomic part of the |
| | | * update operation. |
| | | * <p> |
| | | * The following example gets a modified entry from the result of a modify |
| | | * operation. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String DN = ...; |
| | | * |
| | | * ModifyRequest request = |
| | | * Requests.newModifyRequest(DN) |
| | | * .addControl(PostReadRequestControl.newControl(true, "description")) |
| | | * .addModification(ModificationType.REPLACE, |
| | | * "description", "Using the PostReadRequestControl"); |
| | | * |
| | | * Result result = connection.modify(request); |
| | | * PostReadResponseControl control = |
| | | * result.getControl(PostReadResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * Entry modifiedEntry = control.getEntry(); |
| | | * </pre> |
| | | * |
| | | * @see PostReadResponseControl |
| | | * @see <a href="http://tools.ietf.org/html/rfc4527">RFC 4527 - Lightweight |
| | |
| | | * included a post-read request control. The control contains a Search Result |
| | | * Entry containing, subject to access controls and other constraints, values of |
| | | * the requested attributes. |
| | | * <p> |
| | | * The following example gets a modified entry from the result of a modify |
| | | * operation. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String DN = ...; |
| | | * |
| | | * ModifyRequest request = |
| | | * Requests.newModifyRequest(DN) |
| | | * .addControl(PostReadRequestControl.newControl(true, "description")) |
| | | * .addModification(ModificationType.REPLACE, |
| | | * "description", "Using the PostReadRequestControl"); |
| | | * |
| | | * Result result = connection.modify(request); |
| | | * PostReadResponseControl control = |
| | | * result.getControl(PostReadResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * Entry modifiedEntry = control.getEntry(); |
| | | * </pre> |
| | | * |
| | | * @see PostReadRequestControl |
| | | * @see <a href="http://tools.ietf.org/html/rfc4527">RFC 4527 - Lightweight |
| | |
| | | * client to read the target entry of an update operation immediately before the |
| | | * modifications are applied. These reads are done as an atomic part of the |
| | | * update operation. |
| | | * <p> |
| | | * The following example gets the entry as it was before the modify operation. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String DN = ...; |
| | | * |
| | | * ModifyRequest request = |
| | | * Requests.newModifyRequest(DN) |
| | | * .addControl(PreReadRequestControl.newControl(true, "mail")) |
| | | * .addModification(ModificationType.REPLACE, |
| | | * "mail", "modified@example.com"); |
| | | * |
| | | * Result result = connection.modify(request); |
| | | * PreReadResponseControl control = |
| | | * result.getControl(PreReadResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * Entry unmodifiedEntry = control.getEntry(); |
| | | * </pre> |
| | | * |
| | | * @see PreReadResponseControl |
| | | * @see <a href="http://tools.ietf.org/html/rfc4527">RFC 4527 - Lightweight |
| | |
| | | * included a pre-read request control. The control contains a Search Result |
| | | * Entry containing, subject to access controls and other constraints, values of |
| | | * the requested attributes. |
| | | * <p> |
| | | * The following example gets the entry as it was before the modify operation. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String DN = ...; |
| | | * |
| | | * ModifyRequest request = |
| | | * Requests.newModifyRequest(DN) |
| | | * .addControl(PreReadRequestControl.newControl(true, "mail")) |
| | | * .addModification(ModificationType.REPLACE, |
| | | * "mail", "modified@example.com"); |
| | | * |
| | | * Result result = connection.modify(request); |
| | | * PreReadResponseControl control = |
| | | * result.getControl(PreReadResponseControl.DECODER, |
| | | * new DecodeOptions()); |
| | | * Entry unmodifiedEntry = control.getEntry(); |
| | | * </pre> |
| | | * |
| | | * @see PreReadRequestControl |
| | | * @see <a href="http://tools.ietf.org/html/rfc4527">RFC 4527 - Lightweight |
| | |
| | | * <p> |
| | | * The target user is specified using an authorization ID, or {@code authzId}, |
| | | * as defined in RFC 4513 section 5.2.1.8. |
| | | * <p> |
| | | * This example shows an application replacing a description on a user entry on |
| | | * behalf of a directory administrator. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String bindDN = "cn=My App,ou=Apps,dc=example,dc=com"; // Client app |
| | | * char[] password = ...; |
| | | * String targetDn = "uid=bjensen,ou=People,dc=example,dc=com"; // Regular user |
| | | * String authzId = "dn:uid=kvaughan,ou=People,dc=example,dc=com"; // Admin user |
| | | * |
| | | * ModifyRequest request = |
| | | * Requests.newModifyRequest(targetDn) |
| | | * .addControl(ProxiedAuthV2RequestControl.newControl(authzId)) |
| | | * .addModification(ModificationType.REPLACE, "description", |
| | | * "Done with proxied authz"); |
| | | * |
| | | * connection.bind(bindDN, password); |
| | | * connection.modify(request); |
| | | * Entry entry = connection.readEntry(targetDn, "description"); |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc4370">RFC 4370 - Lightweight |
| | | * Directory Access Protocol (LDAP) Proxied Authorization Control </a> |
| | |
| | | * This controls may be useful when the client has limited functionality or for |
| | | * some other reason cannot sort the results but still needs them sorted. In |
| | | * cases where the client can sort the results client-side sorting is |
| | | * recommended in order to reduce load on the server. See {@link SortKey} for |
| | | * an example of client-side sorting. |
| | | * recommended in order to reduce load on the server. See {@link SortKey} for an |
| | | * example of client-side sorting. |
| | | * <p> |
| | | * The following example demonstrates how to work with a server-side sort. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * |
| | | * SearchRequest request = Requests.newSearchRequest( |
| | | * "ou=People,dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn") |
| | | * .addControl(ServerSideSortRequestControl.newControl(true, new SortKey("cn"))); |
| | | * |
| | | * SearchResultHandler resultHandler = new MySearchResultHandler(); |
| | | * Result result = connection.search(request, resultHandler); |
| | | * |
| | | * ServerSideSortResponseControl control = result.getControl( |
| | | * ServerSideSortResponseControl.DECODER, new DecodeOptions()); |
| | | * if (control != null && control.getResult() == ResultCode.SUCCESS) { |
| | | * // Entries are sorted. |
| | | * } else { |
| | | * // Entries not sorted. |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see ServerSideSortResponseControl |
| | | * @see SortKey |
| | |
| | | * result code in this control is success. If the server omits this control from |
| | | * the search result, the client SHOULD assume that the sort control was ignored |
| | | * by the server. |
| | | * <p> |
| | | * The following example demonstrates how to work with a server-side sort. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * |
| | | * SearchRequest request = Requests.newSearchRequest( |
| | | * "ou=People,dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn") |
| | | * .addControl(ServerSideSortRequestControl.newControl(true, new SortKey("cn"))); |
| | | * |
| | | * SearchResultHandler resultHandler = new MySearchResultHandler(); |
| | | * Result result = connection.search(request, resultHandler); |
| | | * |
| | | * ServerSideSortResponseControl control = result.getControl( |
| | | * ServerSideSortResponseControl.DECODER, new DecodeOptions()); |
| | | * if (control != null && control.getResult() == ResultCode.SUCCESS) { |
| | | * // Entries are sorted. |
| | | * } else { |
| | | * // Entries not sorted. |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see ServerSideSortRequestControl |
| | | * @see <a href="http://tools.ietf.org/html/rfc2891">RFC 2891 - LDAP Control |
| | |
| | | * cookie OCTET STRING |
| | | * } |
| | | * </pre> |
| | | * <p> |
| | | * The following example demonstrates use of simple paged results to handle |
| | | * three entries at a time. |
| | | * |
| | | * <pre> |
| | | * ByteString cookie = ByteString.empty(); |
| | | * SearchRequest request; |
| | | * SearchResultHandler resultHandler = new MySearchResultHandler(); |
| | | * Result result; |
| | | * |
| | | * int page = 1; |
| | | * do { |
| | | System.out.println("# Simple paged results: Page " + page); |
| | | * |
| | | * request = Requests.newSearchRequest( |
| | | "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn") |
| | | .addControl(SimplePagedResultsControl.newControl(true, 3, cookie)); |
| | | * |
| | | * result = connection.search(request, resultHandler); |
| | | * try { |
| | | * SimplePagedResultsControl control = result.getControl( |
| | | SimplePagedResultsControl.DECODER, new DecodeOptions()); |
| | | cookie = control.getCookie(); |
| | | } catch (final DecodeException e) { |
| | | // Failed to decode the response control. |
| | | } |
| | | * |
| | | * ++page; |
| | | * } while (cookie.length() != 0); |
| | | * </pre> |
| | | * |
| | | * The search result handler in this case displays pages of results as LDIF on |
| | | * standard out. |
| | | * |
| | | * <pre> |
| | | * private static class MySearchResultHandler implements SearchResultHandler { |
| | | * |
| | | * {@literal @}Override |
| | | * public void handleErrorResult(ErrorResultException error) { |
| | | * // Ignore. |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public void handleResult(Result result) { |
| | | * // Ignore. |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public boolean handleEntry(SearchResultEntry entry) { |
| | | * final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); |
| | | * try { |
| | | * writer.writeEntry(entry); |
| | | * writer.flush(); |
| | | * } catch (final IOException e) { |
| | | * // The writer could not write to System.out. |
| | | * } |
| | | * return true; |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public boolean handleReference(SearchResultReference reference) { |
| | | * System.out.println("Got a reference: " + reference.toString()); |
| | | * return false; |
| | | * } |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc2696">RFC 2696 - LDAP Control |
| | | * Extension for Simple Paged Results Manipulation </a> |
| | |
| | | * visible to search operations unless the target/base of the operation is a |
| | | * sub-entry. In the presence of the sub-entry request control, sub-entries are |
| | | * visible if and only if the control's value is {@code TRUE}. |
| | | * <p> |
| | | * Consider "Class of Service" sub-entries such as the following: |
| | | * |
| | | * <pre> |
| | | * dn: cn=Gold Class of Service,dc=example,dc=com |
| | | * objectClass: collectiveAttributeSubentry |
| | | * objectClass: extensibleObject |
| | | * objectClass: subentry |
| | | * objectClass: top |
| | | * cn: Gold Class of Service |
| | | * diskQuota;collective: 100 GB |
| | | * mailQuota;collective: 10 GB |
| | | * subtreeSpecification: { base "ou=People", specificationFilter "(classOfService= |
| | | * gold)" } |
| | | * </pre> |
| | | * |
| | | * To access the sub-entries in your search, use the control with value |
| | | * {@code TRUE}. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * |
| | | * SearchRequest request = Requests.newSearchRequest("dc=example,dc=com", |
| | | * SearchScope.WHOLE_SUBTREE, "cn=*Class of Service", "cn", "subtreeSpecification") |
| | | * .addControl(SubentriesRequestControl.newControl(true, true)); |
| | | * Â |
| | | * ConnectionEntryReader reader = connection.search(request); |
| | | * while (reader.hasNext()) { |
| | | * if (reader.isEntry()) { |
| | | * SearchResultEntry entry = reader.readEntry(); |
| | | * // ... |
| | | * } |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc3672">RFC 3672 - Subentries in |
| | | * the Lightweight Directory Access Protocol </a> |
| | |
| | | * This control allows a client to delete an entire subtree of a container entry |
| | | * in a single delete operation. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String baseDN = ...; |
| | | * |
| | | * DeleteRequest request = |
| | | * Requests.newDeleteRequest(baseDN) |
| | | * .addControl(SubtreeDeleteRequestControl.newControl(true)); |
| | | * connection.delete(request); |
| | | * </pre> |
| | | * |
| | | * @see <a |
| | | * href="http://tools.ietf.org/html/draft-armijo-ldap-treedelete">draft-armijo-ldap-treedelete |
| | | * - Tree Delete Control </a> |
| | |
| | | * <p> |
| | | * This control is similar to the simple paged results request control, except |
| | | * that it allows the client to move backwards and forwards in the result set. |
| | | * <p> |
| | | * The following example demonstrates use of the virtual list view controls. |
| | | * |
| | | * <pre> |
| | | * ByteString contextID = ByteString.empty(); |
| | | * |
| | | * // Add a window of 2 entries on either side of the first sn=Jensen entry. |
| | | * SearchRequest request = Requests.newSearchRequest("ou=People,dc=example,dc=com", |
| | | * SearchScope.WHOLE_SUBTREE, "(sn=*)", "sn", "givenName") |
| | | * .addControl(ServerSideSortRequestControl.newControl(true, new SortKey("sn"))) |
| | | * .addControl(VirtualListViewRequestControl.newAssertionControl( |
| | | * true, ByteString.valueOf("Jensen"), 2, 2, contextID)); |
| | | * |
| | | * SearchResultHandler resultHandler = new MySearchResultHandler(); |
| | | * Result result = connection.search(request, resultHandler); |
| | | * |
| | | * ServerSideSortResponseControl sssControl = |
| | | * result.getControl(ServerSideSortResponseControl.DECODER, new DecodeOptions()); |
| | | * if (sssControl != null && sssControl.getResult() == ResultCode.SUCCESS) { |
| | | * // Entries are sorted. |
| | | * } else { |
| | | * // Entries not necessarily sorted |
| | | * } |
| | | * |
| | | * VirtualListViewResponseControl vlvControl = |
| | | * result.getControl(VirtualListViewResponseControl.DECODER, new DecodeOptions()); |
| | | * // Position in list: vlvControl.getTargetPosition()/vlvControl.getContentCount() |
| | | * </pre> |
| | | * |
| | | * The search result handler in this case displays pages of results as LDIF on |
| | | * standard out. |
| | | * |
| | | * <pre> |
| | | * private static class MySearchResultHandler implements SearchResultHandler { |
| | | * |
| | | * {@literal @}Override |
| | | * public void handleErrorResult(ErrorResultException error) { |
| | | * // Ignore. |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public void handleResult(Result result) { |
| | | * // Ignore. |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public boolean handleEntry(SearchResultEntry entry) { |
| | | * final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); |
| | | * try { |
| | | * writer.writeEntry(entry); |
| | | * writer.flush(); |
| | | * } catch (final IOException e) { |
| | | * // The writer could not write to System.out. |
| | | * } |
| | | * return true; |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public boolean handleReference(SearchResultReference reference) { |
| | | * System.out.println("Got a reference: " + reference.toString()); |
| | | * return false; |
| | | * } |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see VirtualListViewResponseControl |
| | | * @see ServerSideSortRequestControl |
| | |
| | | * <p> |
| | | * The content count and context ID should be used in a subsequent virtual list |
| | | * view requests. |
| | | * <p> |
| | | * The following example demonstrates use of the virtual list view controls. |
| | | * |
| | | * <pre> |
| | | * ByteString contextID = ByteString.empty(); |
| | | * |
| | | * // Add a window of 2 entries on either side of the first sn=Jensen entry. |
| | | * SearchRequest request = Requests.newSearchRequest("ou=People,dc=example,dc=com", |
| | | * SearchScope.WHOLE_SUBTREE, "(sn=*)", "sn", "givenName") |
| | | * .addControl(ServerSideSortRequestControl.newControl(true, new SortKey("sn"))) |
| | | * .addControl(VirtualListViewRequestControl.newAssertionControl( |
| | | * true, ByteString.valueOf("Jensen"), 2, 2, contextID)); |
| | | * |
| | | * SearchResultHandler resultHandler = new MySearchResultHandler(); |
| | | * Result result = connection.search(request, resultHandler); |
| | | * |
| | | * ServerSideSortResponseControl sssControl = |
| | | * result.getControl(ServerSideSortResponseControl.DECODER, new DecodeOptions()); |
| | | * if (sssControl != null && sssControl.getResult() == ResultCode.SUCCESS) { |
| | | * // Entries are sorted. |
| | | * } else { |
| | | * // Entries not necessarily sorted |
| | | * } |
| | | * |
| | | * VirtualListViewResponseControl vlvControl = |
| | | * result.getControl(VirtualListViewResponseControl.DECODER, new DecodeOptions()); |
| | | * // Position in list: vlvControl.getTargetPosition()/vlvControl.getContentCount() |
| | | * </pre> |
| | | * |
| | | * The search result handler in this case displays pages of results as LDIF on |
| | | * standard out. |
| | | * |
| | | * <pre> |
| | | * private static class MySearchResultHandler implements SearchResultHandler { |
| | | * |
| | | * {@literal @}Override |
| | | * public void handleErrorResult(ErrorResultException error) { |
| | | * // Ignore. |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public void handleResult(Result result) { |
| | | * // Ignore. |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public boolean handleEntry(SearchResultEntry entry) { |
| | | * final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); |
| | | * try { |
| | | * writer.writeEntry(entry); |
| | | * writer.flush(); |
| | | * } catch (final IOException e) { |
| | | * // The writer could not write to System.out. |
| | | * } |
| | | * return true; |
| | | * } |
| | | * |
| | | * {@literal @}Override |
| | | * public boolean handleReference(SearchResultReference reference) { |
| | | * System.out.println("Got a reference: " + reference.toString()); |
| | | * return false; |
| | | * } |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see VirtualListViewRequestControl |
| | | * @see <a href="http://tools.ietf.org/html/draft-ietf-ldapext-ldapv3-vlv"> |
| | |
| | | * Note that some directory systems may establish access controls that permit |
| | | * the values of certain attributes (such as {@code userPassword} ) to be |
| | | * compared but not interrogated by other means. |
| | | * <p> |
| | | * The following excerpt shows how to use the Compare operation to check whether |
| | | * a member belongs to a (possibly large) static group. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String groupDN = ...; |
| | | * String memberDN = ...; |
| | | * |
| | | * CompareRequest request = |
| | | * Requests.newCompareRequest(groupDN, "member", memberDN); |
| | | * CompareResult result = connection.compare(request); |
| | | * if (result.matched()) { |
| | | * // The member belongs to the group. |
| | | * } |
| | | * </pre> |
| | | */ |
| | | public interface CompareRequest extends Request { |
| | | /** |
| | |
| | | * Only leaf entries (those with no subordinate entries) can be deleted with |
| | | * this operation. However, addition of the {@code SubtreeDeleteControl} permits |
| | | * whole sub-trees to be deleted using a single Delete request. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String baseDN = ...; |
| | | * |
| | | * DeleteRequest request = |
| | | * Requests.newDeleteRequest(baseDN) |
| | | * .addControl(SubtreeDeleteRequestControl.newControl(true)); |
| | | * connection.delete(request); |
| | | * </pre> |
| | | */ |
| | | public interface DeleteRequest extends Request, ChangeRecord { |
| | | /** |
| | |
| | | * operation which installs transport layer security (see |
| | | * {@link StartTLSExtendedRequest}). |
| | | * |
| | | * <p> |
| | | * To determine whether a directory server supports a given extension, read the |
| | | * list of supported extensions from the root DSE to get a collection of |
| | | * extension OIDs, and then check for a match. For example: |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * Collection<String> supported = |
| | | * RootDSE.readRootDSE(connection).getSupportedExtendedOperations(); |
| | | * |
| | | * ExtendedRequest extension = ...; |
| | | * String OID = extension.getOID(); |
| | | * if (supported != null && !supported.isEmpty() && supported.contains(OID)) { |
| | | * // The extension is supported. Use it here... |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @param <S> |
| | | * The type of result. |
| | | */ |
| | |
| | | /** |
| | | * The Modify operation allows a client to request that a modification of an |
| | | * entry be performed on its behalf by a server. |
| | | * <p> |
| | | * The following example adds a member to a static group entry. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String groupDN = ...; |
| | | * String memberDN = ...; |
| | | * |
| | | * ModifyRequest addMember = Requests.newModifyRequest(groupDN) |
| | | * .addModification(ModificationType.ADD, "member", memberDN); |
| | | * connection.modify(addMember); |
| | | * </pre> |
| | | */ |
| | | public interface ModifyRequest extends Request, ChangeRecord { |
| | | /** |
| | |
| | | * addition, it includes support for requiring the user's current password as |
| | | * well as for generating a new password if none was provided. |
| | | * |
| | | * <pre> |
| | | * String userIdentity = ...; // For example, u:<uid> or dn:<DN> |
| | | * char[] oldPassword = ...; |
| | | * char[] newPassword = ...; |
| | | * Connection connection = ...; |
| | | * |
| | | * PasswordModifyExtendedRequest request = |
| | | * Requests.newPasswordModifyExtendedRequest() |
| | | * .setUserIdentity(userIdentity) |
| | | * .setOldPassword(oldPassword) |
| | | * .setNewPassword(newPassword); |
| | | * |
| | | * PasswordModifyExtendedResult result = connection.extendedRequest(request); |
| | | * if (result.isSuccess()) { |
| | | * // Changed password |
| | | * } else { |
| | | * // Use result to diagnose error. |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see PasswordModifyExtendedResult |
| | | * @see <a href="http://tools.ietf.org/html/rfc3062">RFC 3062 - LDAP Password |
| | | * Modify Extended Operation </a> |
| | |
| | | * The authentication and optional authorization identity is specified using an |
| | | * authorization ID, or {@code authzId}, as defined in RFC 4513 section 5.2.1.8. |
| | | * |
| | | * <pre> |
| | | * String authcid = ...; // Authentication ID, e.g. dn:<dn>, u:<uid> |
| | | * String authzid = ...; // Authorization ID, e.g. dn:<dn>, u:<uid> |
| | | * char[] password = ...; |
| | | * Connection connection = ...; // Use StartTLS to protect the request |
| | | * |
| | | * PlainSASLBindRequest request = |
| | | * Requests.newPlainSASLBindRequest(authcid, password) |
| | | * .setAuthorizationID(authzid); |
| | | * |
| | | * connection.bind(request); |
| | | * // Authenticated if the connection succeeds |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc4616">RFC 4616 - The PLAIN Simple |
| | | * Authentication and Security Layer (SASL) Mechanism </a> |
| | | * @see <a href="http://tools.ietf.org/html/rfc4513#section-5.2.1.8">RFC 4513 - |
| | |
| | | * 4513. |
| | | * <p> |
| | | * <TODO>finish doc. |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc4513#section-5.2.1.8">RFC 4513 - |
| | | * SASL Authorization Identities (authzId) </a> |
| | | */ |
| | | public interface SASLBindRequest extends BindRequest { |
| | | /** |
| | |
| | | * criterion. This can be used to read attributes from a single entry, from |
| | | * entries immediately subordinate to a particular entry, or from a whole |
| | | * subtree of entries. |
| | | * <p> |
| | | * Use {@link Requests#newSearchRequest(DN, SearchScope, Filter, String...)} or |
| | | * {@link Requests#newSearchRequest(String, SearchScope, String, String...)} to |
| | | * create a new search request. |
| | | * |
| | | * <pre> |
| | | * SearchRequest request = Requests.newSearchRequest( |
| | | * "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn"); |
| | | * </pre> |
| | | * |
| | | * Alternatively, use the |
| | | * {@link org.forgerock.opendj.ldap.Connection#search(String, SearchScope, String, String...) |
| | | * Connection.search()} method to specify the arguments directly. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * ConnectionEntryReader reader = connection.search( |
| | | * "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn"); |
| | | * </pre> |
| | | */ |
| | | public interface SearchRequest extends Request { |
| | | /** |
| | |
| | | * <li>A name/password authentication mechanism using credentials consisting of |
| | | * a name and a password. |
| | | * </ul> |
| | | * {@link Requests} has methods to create a {@code SimpleBindRequest}. |
| | | * |
| | | * <pre> |
| | | * String bindDN = ...; |
| | | * char[] bindPassword = ...; |
| | | * |
| | | * SimpleBindRequest sbr = Requests.newSimpleBindRequest(bindDN, bindPassword); |
| | | * </pre> |
| | | * |
| | | * Alternatively, use |
| | | * {@link org.forgerock.opendj.ldap.Connection#bind(String, char[]) |
| | | * Connection.bind}. |
| | | * |
| | | * <pre> |
| | | * Connection connection; |
| | | * String bindDN = ...; |
| | | * char[] bindPassword = ...; |
| | | * |
| | | * connection.bind(bindDN, bindPassword); |
| | | * </pre> |
| | | */ |
| | | public interface SimpleBindRequest extends BindRequest { |
| | | /** |
| | |
| | | * The start TLS extended request as defined in RFC 4511. The Start Transport |
| | | * Layer Security (StartTLS) operation's purpose is to initiate installation of |
| | | * a TLS layer. |
| | | * <p> |
| | | * Use an {@link org.forgerock.opendj.ldap.SSLContextBuilder SSLContextBuilder} |
| | | * when setting up LDAP options needed to use StartTLS. |
| | | * {@link org.forgerock.opendj.ldap.TrustManagers TrustManagers} has methods you |
| | | * can use to set the trust manager for the SSL context builder. |
| | | * |
| | | * <pre> |
| | | * LDAPOptions options = new LDAPOptions(); |
| | | * SSLContext sslContext = |
| | | * new SSLContextBuilder().setTrustManager(...).getSSLContext(); |
| | | * options.setSSLContext(sslContext); |
| | | * options.setUseStartTLS(true); |
| | | * |
| | | * String host = ...; |
| | | * int port = ...; |
| | | * LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port, options); |
| | | * Connection connection = factory.getConnection(); |
| | | * // Connection uses StartTLS... |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc4511">RFC 4511 - Lightweight |
| | | * Directory Access Protocol (LDAP): The Protocol </a> |
| | |
| | | * clients to obtain the primary authorization identity, in its primary form, |
| | | * that the server has associated with the user or application entity. |
| | | * <p> |
| | | * The following example demonstrates use of the Who Am I? request and response. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String name = ...; |
| | | * char[] password = ...; |
| | | * |
| | | * Result result = connection.bind(name, password); |
| | | * if (result.isSuccess()) { |
| | | * WhoAmIExtendedRequest request = Requests.newWhoAmIExtendedRequest(); |
| | | * WhoAmIExtendedResult extResult = connection.extendedRequest(request); |
| | | * |
| | | * if (extResult.isSuccess()) { |
| | | * // Authz ID: " + extResult.getAuthorizationID()); |
| | | * } |
| | | * } |
| | | * </pre> |
| | | * |
| | | * This operation may preferable to the Authorization Identity Controls |
| | | * mechanism defined in RFC 3829, which uses Bind request and response controls |
| | | * to request and return the authorization identity. Bind controls are not |
| | |
| | | * the attribute or sub-type according to the attribute's equality matching rule |
| | | * then the result code is set to {@link ResultCode#COMPARE_TRUE} and can be |
| | | * determined by invoking the {@link #matched} method. |
| | | * <p> |
| | | * The following excerpt shows how to use the Compare operation to check whether |
| | | * a member belongs to a (possibly large) static group. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String groupDN = ...; |
| | | * String memberDN = ...; |
| | | * |
| | | * CompareRequest request = |
| | | * Requests.newCompareRequest(groupDN, "member", memberDN); |
| | | * CompareResult result = connection.compare(request); |
| | | * if (result.matched()) { |
| | | * // The member belongs to the group. |
| | | * } |
| | | * </pre> |
| | | */ |
| | | public interface CompareResult extends Result { |
| | | /** |
| | |
| | | * An Intermediate response may convey an optional response name and value. |
| | | * These can be retrieved using the {@link #getOID} and {@link #getValue} |
| | | * methods respectively. |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc3771">RFC 3771 - The Lightweight |
| | | * Directory Access Protocol (LDAP) Intermediate Response Message</a> |
| | | */ |
| | | public interface IntermediateResponse extends Response { |
| | | /** |
| | |
| | | * <p> |
| | | * The authorization identity is specified using an authorization ID, or |
| | | * {@code authzId}, as defined in RFC 4513 section 5.2.1.8. |
| | | * <p> |
| | | * The following example demonstrates use of the Who Am I? request and response. |
| | | * |
| | | * <pre> |
| | | * Connection connection = ...; |
| | | * String name = ...; |
| | | * char[] password = ...; |
| | | * |
| | | * Result result = connection.bind(name, password); |
| | | * if (result.isSuccess()) { |
| | | * WhoAmIExtendedRequest request = Requests.newWhoAmIExtendedRequest(); |
| | | * WhoAmIExtendedResult extResult = connection.extendedRequest(request); |
| | | * |
| | | * if (extResult.isSuccess()) { |
| | | * // Authz ID: " + extResult.getAuthorizationID()); |
| | | * } |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see org.forgerock.opendj.ldap.requests.WhoAmIExtendedRequest |
| | | * @see org.forgerock.opendj.ldap.controls.AuthorizationIdentityRequestControl |
| | |
| | | /** |
| | | * This enumeration defines the set of possible attribute usage values that may |
| | | * apply to an attribute type, as defined in RFC 2252. |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc2252">RFC 2252 - Lightweight |
| | | * Directory Access Protocol (v3): Attribute Syntax Definitions</a> |
| | | */ |
| | | public enum AttributeUsage { |
| | | /** |
| | |
| | | /** |
| | | * This enumeration defines the set of possible objectclass types that may be |
| | | * used, as defined in RFC 2252. |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc2252">RFC 2252 - Lightweight |
| | | * Directory Access Protocol (v3): Attribute Syntax Definitions</a> |
| | | */ |
| | | public enum ObjectClassType { |
| | | /** |
| | |
| | | * An LDIF change record reader reads change records using the LDAP Data |
| | | * Interchange Format (LDIF) from a user defined source. |
| | | * |
| | | * <p> |
| | | * The following example reads changes from LDIF, and writes the changes to the |
| | | * directory server. |
| | | * |
| | | * <pre> |
| | | * InputStream ldif = ...; |
| | | * LDIFChangeRecordReader reader = new LDIFChangeRecordReader(ldif); |
| | | * |
| | | * Connection connection = ...; |
| | | * connection.bind(...); |
| | | * |
| | | * ConnectionChangeRecordWriter writer = |
| | | * new ConnectionChangeRecordWriter(connection); |
| | | * while (reader.hasNext()) { |
| | | * ChangeRecord changeRecord = reader.readChangeRecord(); |
| | | * writer.writeChangeRecord(changeRecord); |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc2849">RFC 2849 - The LDAP Data |
| | | * Interchange Format (LDIF) - Technical Specification </a> |
| | | */ |
| | |
| | | /** |
| | | * An LDIF change record writer writes change records using the LDAP Data |
| | | * Interchange Format (LDIF) to a user defined destination. |
| | | * <p> |
| | | * The following example reads changes from LDIF, and writes the changes to the |
| | | * directory server. |
| | | * |
| | | * <pre> |
| | | * InputStream ldif = ...; |
| | | * LDIFChangeRecordReader reader = new LDIFChangeRecordReader(ldif); |
| | | * |
| | | * Connection connection = ...; |
| | | * connection.bind(...); |
| | | * |
| | | * ConnectionChangeRecordWriter writer = |
| | | * new ConnectionChangeRecordWriter(connection); |
| | | * while (reader.hasNext()) { |
| | | * ChangeRecord changeRecord = reader.readChangeRecord(); |
| | | * writer.writeChangeRecord(changeRecord); |
| | | * } |
| | | * </pre> |
| | | * |
| | | * @see <a href="http://tools.ietf.org/html/rfc2849">RFC 2849 - The LDAP Data |
| | | * Interchange Format (LDIF) - Technical Specification </a> |
| | |
| | | LDAP Directory Services as defined in <a |
| | | href="http://tools.ietf.org/html/rfc4510">RFC 4510</a>. |
| | | <br> |
| | | <h1>Getting started</h1> |
| | | For an introduction to LDAP, read the <em>OpenDJ SDK Developer's Guide</em> |
| | | chapter on <a |
| | | href="http://opendj.forgerock.org/doc/dev-guide/index.html#chap-understanding-ldap" |
| | | >Understanding LDAP</a>. Also see the chapter on <a |
| | | href="http://opendj.forgerock.org/doc/dev-guide/index.html#chap-best-practices" |
| | | >Best Practices For LDAP Application Developers</a>. |
| | | <br> |
| | | <h1>Getting Started</h1> |
| | | The following example shows how the OpenDJ SDK may be used to |
| | | connect to a Directory Server, authenticate, and then perform a |
| | | connect to a directory server, authenticate, and then perform a |
| | | search. The search results are output as LDIF to the standard |
| | | output: |
| | | <br> |
| | |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | <br><!-- It seems the .zip is not packaged with the SDK. --> |
| | | Additional examples can be found online at the <a |
| | | href="http://opendj.forgerock.org/opendj-ldap-sdk-examples/" |
| | | >OpenDJ LDAP SDK Examples</a> site. |
| | | <br> |
| | | Additional examples can be found in the file examples.zip which is |
| | | included with this SDK. |
| | | <br> |
| | | <h1>Creating connections</h1> |
| | | <h1>Creating Connections</h1> |
| | | The following classes can be used to create and manage connections to |
| | | LDAP Directory Servers: |
| | | LDAP directory servers: |
| | | <ul> |
| | | <li>{@link org.forgerock.opendj.ldap.LDAPConnectionFactory}</li> |
| | | <li>{@link org.forgerock.opendj.ldap.Connection}</li> |
| | | <li>{@link org.forgerock.opendj.ldap.Connections}</li> |
| | | </ul> |
| | | <br> |
| | | <h1>Creating requests</h1> |
| | | <h1>Creating Requests</h1> |
| | | The following classes can be used to create LDAP requests: |
| | | <ul> |
| | | <li>{@link org.forgerock.opendj.ldap.requests.Requests}</li> |
| | | <li>{@link org.forgerock.opendj.ldap.requests.Request}</li> |
| | | </ul> |
| | | <br> |
| | | <h1>Using controls</h1> |
| | | <h1>Using Controls</h1> |
| | | Common LDAP control implementations can be found in |
| | | {@link org.forgerock.opendj.ldap.controls}. |
| | | <br> |
| | | <br> |
| | | <h1>Core types</h1> |
| | | <h1>Core Types</h1> |
| | | The following classes and interfaces represent core types: |
| | | <ul> |
| | | <li>{@link org.forgerock.opendj.ldap.AttributeDescription}</li> |
| | |
| | | final LDAPConnectionFactory factory = |
| | | new LDAPConnectionFactory(host, port, getTrustAllOptions()); |
| | | connection = factory.getConnection(); |
| | | PlainSASLBindRequest request = Requests.newPlainSASLBindRequest( |
| | | authcid, passwd.toCharArray()); |
| | | if (authzid != null) request.setAuthorizationID(authzid); |
| | | PlainSASLBindRequest request = |
| | | Requests.newPlainSASLBindRequest(authcid, passwd.toCharArray()) |
| | | .setAuthorizationID(authzid); |
| | | connection.bind(request); |
| | | System.out.println("Authenticated as " + authcid + "."); |
| | | }</programlisting> |
| | |
| | | <literal>ou=People,dc=example,dc=com</literal>.</para> |
| | | |
| | | <programlisting language="java">// An entry to add to the directory |
| | | DN entryDN = DN.valueOf("cn=Bob,ou=People,dc=example,dc=com"); |
| | | Entry entry = new LinkedHashMapEntry(entryDN.toString()) |
| | | Entry entry = new LinkedHashMapEntry("cn=Bob,ou=People,dc=example,dc=com") |
| | | .addAttribute("cn", "Bob") |
| | | .addAttribute("objectclass", "top") |
| | | .addAttribute("objectclass", "person") |
| | |
| | | |
| | | // Here, entryDN contains cn=Bob,ou=People,dc=example,dc=com. |
| | | // The second argument is the new relative distinguished name. |
| | | connection.modifyDN(entryDN.toString(), "cn=Ted"); |
| | | connection.modifyDN(entryDN, "cn=Ted"); |
| | | |
| | | } catch (final ErrorResultException e) { |
| | | System.err.println(e.getMessage()); |