opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
@@ -29,17 +29,22 @@ import java.io.IOException; import java.util.Collection; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.Connection; import org.forgerock.opendj.ldap.DecodeException; import org.forgerock.opendj.ldap.DecodeOptions; import org.forgerock.opendj.ldap.Entry; import org.forgerock.opendj.ldap.ErrorResultException; import org.forgerock.opendj.ldap.ErrorResultIOException; import org.forgerock.opendj.ldap.Filter; import org.forgerock.opendj.ldap.LDAPConnectionFactory; import org.forgerock.opendj.ldap.ModificationType; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.RootDSE; import org.forgerock.opendj.ldap.SearchResultHandler; import org.forgerock.opendj.ldap.SearchResultReferenceIOException; import org.forgerock.opendj.ldap.SearchScope; import org.forgerock.opendj.ldap.SortKey; import org.forgerock.opendj.ldap.controls.AssertionRequestControl; import org.forgerock.opendj.ldap.controls.AuthorizationIdentityRequestControl; import org.forgerock.opendj.ldap.controls.AuthorizationIdentityResponseControl; @@ -47,13 +52,27 @@ import org.forgerock.opendj.ldap.controls.GetEffectiveRightsRequestControl; import org.forgerock.opendj.ldap.controls.ManageDsaITRequestControl; import org.forgerock.opendj.ldap.controls.MatchedValuesRequestControl; import org.forgerock.opendj.ldap.controls.PasswordExpiredResponseControl; import org.forgerock.opendj.ldap.controls.PasswordExpiringResponseControl; import org.forgerock.opendj.ldap.controls.PasswordPolicyRequestControl; import org.forgerock.opendj.ldap.controls.PasswordPolicyResponseControl; import org.forgerock.opendj.ldap.controls.PermissiveModifyRequestControl; import org.forgerock.opendj.ldap.controls.PersistentSearchChangeType; import org.forgerock.opendj.ldap.controls.PersistentSearchRequestControl; import org.forgerock.opendj.ldap.controls.PostReadRequestControl; import org.forgerock.opendj.ldap.controls.PostReadResponseControl; import org.forgerock.opendj.ldap.controls.PreReadRequestControl; import org.forgerock.opendj.ldap.controls.PreReadResponseControl; import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl; import org.forgerock.opendj.ldap.controls.ServerSideSortRequestControl; import org.forgerock.opendj.ldap.controls.ServerSideSortResponseControl; import org.forgerock.opendj.ldap.controls.SimplePagedResultsControl; import org.forgerock.opendj.ldap.requests.BindRequest; import org.forgerock.opendj.ldap.requests.ModifyRequest; import org.forgerock.opendj.ldap.requests.Requests; import org.forgerock.opendj.ldap.requests.SearchRequest; import org.forgerock.opendj.ldap.responses.BindResult; import org.forgerock.opendj.ldap.responses.Result; import org.forgerock.opendj.ldap.responses.SearchResultEntry; import org.forgerock.opendj.ldap.responses.SearchResultReference; import org.forgerock.opendj.ldif.ConnectionEntryReader; @@ -104,9 +123,18 @@ // For the EntryChangeNotificationResponseControl see // usePersistentSearchRequestControl() //useGetEffectiveRightsRequestControl(connection); //usePersistentSearchRequestControl(connection); //useManageDsaITRequestControl(connection); useMatchedValuesRequestControl(connection); //useMatchedValuesRequestControl(connection); //usePasswordExpiredResponseControl(connection); //usePasswordExpiringResponseControl(connection); //usePasswordPolicyRequestControl(connection); //usePermissiveModifyRequestControl(connection); //usePersistentSearchRequestControl(connection); //usePostReadRequestControl(connection); //usePreReadRequestControl(connection); //useProxiedAuthV2RequestControl(connection); //useServerSideSortRequestControl(connection); useSimplePagedResultsControl(connection); // TODO: The rest of the supported controls } catch (final ErrorResultException e) { @@ -121,7 +149,8 @@ } /** * Use the LDAP assertion control to perform a trivial modification. * Use the LDAP assertion control to modify Babs Jensen's description if * her entry does not have a description, yet. * * @param connection * Active connection to LDAP server containing <a @@ -132,25 +161,26 @@ */ static void useAssertionControl(Connection connection) throws ErrorResultException { if (isSupported(AssertionRequestControl.OID)) { // Modify Babs Jensen's description if her entry does not have // a description, yet. final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; ModifyRequest request = Requests.newModifyRequest(dn); request.addControl(AssertionRequestControl.newControl(true, Filter .valueOf("!(description=*)"))); request.addModification(ModificationType.ADD, "description", "Created with the help of the LDAP assertion control"); final ModifyRequest request = Requests.newModifyRequest(dn) .addControl(AssertionRequestControl.newControl( true, Filter.valueOf("!(description=*)"))) .addModification(ModificationType.ADD, "description", "Created using LDAP assertion control"); connection.modify(request); LDIFEntryWriter writer = new LDIFEntryWriter(System.out); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); try { writer.writeEntry(connection.readEntry(dn, "description")); writer.close(); } catch (final IOException e) { e.printStackTrace(); } } else { System.out.println("AssertionRequestControl not supported."); } } @@ -166,12 +196,13 @@ */ static void useAuthorizationIdentityRequestControl(Connection connection) throws ErrorResultException { if (isSupported(AuthorizationIdentityRequestControl.OID)) { final String name = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] password = "hifalutin".toCharArray(); final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] pwd = "hifalutin".toCharArray(); System.out.println("Binding as " + name); BindRequest request = Requests.newSimpleBindRequest(name, password); request.addControl(AuthorizationIdentityRequestControl.newControl(true)); System.out.println("Binding as " + dn); final BindRequest request = Requests.newSimpleBindRequest(dn, pwd) .addControl(AuthorizationIdentityRequestControl.newControl(true)); final BindResult result = connection.bind(request); try { @@ -183,6 +214,8 @@ } catch (final DecodeException e) { e.printStackTrace(); } } else { System.out.println("AuthorizationIdentityRequestControl not supported."); } } @@ -202,12 +235,12 @@ if (isSupported(GetEffectiveRightsRequestControl.OID)) { final String authDN = "uid=kvaughan,ou=People,dc=example,dc=com"; SearchRequest request = final SearchRequest request = Requests.newSearchRequest( "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo"); request.addControl( GetEffectiveRightsRequestControl.newControl(true, authDN, "cn")); "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo") .addControl(GetEffectiveRightsRequestControl.newControl( true, authDN, "cn")); final ConnectionEntryReader reader = connection.search(request); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); @@ -226,6 +259,8 @@ } catch (final IOException e) { e.printStackTrace(); } } else { System.out.println("GetEffectiveRightsRequestControl not supported."); } } @@ -270,6 +305,8 @@ } catch (final IOException e) { e.printStackTrace(); } } else { System.out.println("ManageDsaITRequestControl not supported."); } } @@ -287,11 +324,11 @@ static void useMatchedValuesRequestControl(Connection connection) throws ErrorResultException { if (isSupported(MatchedValuesRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; SearchRequest request = final SearchRequest request = Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT, "(objectclass=*)", "cn"); final String filter = "cn=Babs Jensen"; request.addControl(MatchedValuesRequestControl.newControl(true, filter)); "(objectclass=*)", "cn") .addControl(MatchedValuesRequestControl.newControl( true, "(cn=Babs Jensen)")); final SearchResultEntry entry = connection.searchSingleEntry(request); System.out.println("Reading entry with matched values request."); @@ -302,6 +339,159 @@ } catch (final IOException e) { e.printStackTrace(); } } else { System.out.println("MatchedValuesRequestControl not supported."); } } /** * Check the Password Expired Response Control. To get this code to output * something, you must first set up an appropriate password policy and wait * for Barbara Jensen's password to expire. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. */ static void usePasswordExpiredResponseControl(Connection connection) { if (isSupported(PasswordExpiredResponseControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] pwd = "hifalutin".toCharArray(); try { connection.bind(dn, pwd); } catch (ErrorResultException e) { final Result result = e.getResult(); try { final PasswordExpiredResponseControl control = result.getControl(PasswordExpiredResponseControl.DECODER, new DecodeOptions()); if (!(control == null) && control.hasValue()) { System.out.println("Password expired for " + dn); } } catch (DecodeException de) { de.printStackTrace(); } } } else { System.out.println("PasswordExpiredResponseControl not supported."); } } /** * Check the Password Expiring Response Control. To get this code to output * something, you must first set up an appropriate password policy and wait * for Barbara Jensen's password to get old enough that the server starts * warning about expiration. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. * @throws ErrorResultException * Operation failed. */ static void usePasswordExpiringResponseControl(Connection connection) throws ErrorResultException { if (isSupported(PasswordExpiringResponseControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] pwd = "hifalutin".toCharArray(); final BindResult result = connection.bind(dn, pwd); try { final PasswordExpiringResponseControl control = result.getControl(PasswordExpiringResponseControl.DECODER, new DecodeOptions()); if (!(control == null) && control.hasValue()) { System.out.println("Password for " + dn + " expires in " + control.getSecondsUntilExpiration() + " seconds."); } } catch (DecodeException de) { de.printStackTrace(); } } else { System.out.println("PasswordExpiringResponseControl not supported"); } } /** * Use the Password Policy Request and Response Controls. To get this code * to output something, you must first set up an appropriate password policy * and wait for Barbara Jensen's password to get old enough that the server * starts warning about expiration, or for the password to expire. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. */ static void usePasswordPolicyRequestControl(Connection connection) { if (isSupported(PasswordPolicyRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] pwd = "hifalutin".toCharArray(); try { final BindRequest request = Requests.newSimpleBindRequest(dn, pwd) .addControl(PasswordPolicyRequestControl.newControl(true)); final BindResult result = connection.bind(request); final PasswordPolicyResponseControl control = result.getControl(PasswordPolicyResponseControl.DECODER, new DecodeOptions()); if (!(control == null) && !(control.getWarningType() == null)) { System.out.println("Password policy warning " + control.getWarningType().toString() + ", value " + control.getWarningValue() + " for " + dn); } } catch (ErrorResultException e) { final Result result = e.getResult(); try { final PasswordPolicyResponseControl control = result.getControl(PasswordPolicyResponseControl.DECODER, new DecodeOptions()); if (!(control == null)) { System.out.println("Password policy error " + control.getErrorType().toString() + " for " + dn); } } catch (DecodeException de) { de.printStackTrace(); } } catch (DecodeException e) { e.printStackTrace(); } } else { System.out.println("PasswordPolicyRequestControl not supported"); } } /** * Use Permissive Modify Request Control to try to add an attribute that * already exists. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. * @throws ErrorResultException * Operation failed. */ static void usePermissiveModifyRequestControl(Connection connection) throws ErrorResultException { if (isSupported(PermissiveModifyRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final ModifyRequest request = Requests.newModifyRequest(dn) .addControl(PermissiveModifyRequestControl.newControl(true)) .addModification(ModificationType.ADD, "uid", "bjensen"); connection.modify(request); System.out.println("Permissive modify did not complain about " + "attempt to add uid: bjensen to " + dn + "."); } else { System.out.println("PermissiveModifyRequestControl not supported"); } } @@ -322,13 +512,11 @@ */ static void usePersistentSearchRequestControl(Connection connection) throws ErrorResultException { if (isSupported(PersistentSearchRequestControl.OID)) { SearchRequest request = final SearchRequest request = Requests.newSearchRequest( "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(objectclass=inetOrgPerson)", "cn"); request.addControl(PersistentSearchRequestControl.newControl( "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(objectclass=inetOrgPerson)", "cn") .addControl(PersistentSearchRequestControl.newControl( true, true, true, // isCritical, changesOnly, returnECs PersistentSearchChangeType.ADD, PersistentSearchChangeType.DELETE, @@ -365,6 +553,241 @@ } catch (final SearchResultReferenceIOException e) { e.printStackTrace(); } } else { System.out.println("PersistentSearchRequestControl not supported."); } } /** * Use Post Read Controls to get entry content after a modification. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. * @throws ErrorResultException * Operation failed. */ static void usePostReadRequestControl(Connection connection) throws ErrorResultException { if (isSupported(PostReadRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final ModifyRequest request = Requests.newModifyRequest(dn) .addControl(PostReadRequestControl.newControl(true, "description")) .addModification(ModificationType.REPLACE, "description", "Using the PostReadRequestControl"); final Result result = connection.modify(request); try { final PostReadResponseControl control = result.getControl(PostReadResponseControl.DECODER, new DecodeOptions()); final Entry entry = control.getEntry(); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); writer.writeEntry(entry); writer.close(); } catch (DecodeException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { System.out.println("PostReadRequestControl not supported"); } } /** * Use Pre Read Controls to get entry content before a modification. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. * @throws ErrorResultException * Operation failed. */ static void usePreReadRequestControl(Connection connection) throws ErrorResultException { if (isSupported(PreReadRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final ModifyRequest request = Requests.newModifyRequest(dn) .addControl(PreReadRequestControl.newControl(true, "mail")) .addModification( ModificationType.REPLACE, "mail", "modified@example.com"); final Result result = connection.modify(request); try { final PreReadResponseControl control = result.getControl(PreReadResponseControl.DECODER, new DecodeOptions()); final Entry entry = control.getEntry(); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); writer.writeEntry(entry); writer.close(); } catch (DecodeException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { System.out.println("PreReadRequestControl not supported"); } } /** * Use proxied authorization to modify an identity as another user. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. * @throws ErrorResultException * Operation failed. */ static void useProxiedAuthV2RequestControl(Connection connection) throws ErrorResultException { if (isSupported(ProxiedAuthV2RequestControl.OID)) { final String bindDN = "cn=My App,ou=Apps,dc=example,dc=com"; final String targetDn = "uid=bjensen,ou=People,dc=example,dc=com"; final String authzId = "dn:uid=kvaughan,ou=People,dc=example,dc=com"; final ModifyRequest request = Requests.newModifyRequest(targetDn) .addControl(ProxiedAuthV2RequestControl.newControl(authzId)) .addModification(ModificationType.REPLACE, "description", "Done with proxied authz"); connection.bind(bindDN, "password".toCharArray()); connection.modify(request); final Entry entry = connection.readEntry(targetDn, "description"); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); try { writer.writeEntry(entry); writer.close(); } catch (IOException e) { e.printStackTrace(); } } else { System.out.println("ProxiedAuthV2RequestControl not supported"); } } /** * Use the server-side sort controls. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. * @throws ErrorResultException * Operation failed. */ static void useServerSideSortRequestControl(Connection connection) throws ErrorResultException { if (isSupported(ServerSideSortRequestControl.OID)) { final SearchRequest request = Requests.newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn") .addControl(ServerSideSortRequestControl.newControl( true, new SortKey("cn"))); final SearchResultHandler resultHandler = new MySearchResultHandler(); final Result result = connection.search(request, resultHandler); try { final ServerSideSortResponseControl control = result.getControl(ServerSideSortResponseControl.DECODER, new DecodeOptions()); if (control != null && control.getResult() == ResultCode.SUCCESS) { System.out.println("# Entries are sorted."); // FIXME: But the order is backwards! } else { System.out.println("# Entries not necessarily sorted"); } } catch (DecodeException e) { e.printStackTrace(); } } else { System.out.println("ServerSideSortRequestControl not supported"); } } private static class MySearchResultHandler implements SearchResultHandler { @Override public void handleErrorResult(ErrorResultException error) { // Ignore. } @Override public void handleResult(Result result) { // Ignore. } @Override public boolean handleEntry(SearchResultEntry entry) { final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); try { writer.writeEntry(entry); writer.flush(); } catch (IOException e) { e.printStackTrace(); } return true; } @Override public boolean handleReference(SearchResultReference reference) { System.out.println("Got a reference: " + reference.toString()); return false; } } /** * Use the simple paged results mechanism. * * @param connection * Active connection to LDAP server containing <a * href="http://opendj.forgerock.org/Example.ldif" * >Example.ldif</a> content. * @throws ErrorResultException * Operation failed. */ static void useSimplePagedResultsControl(Connection connection) throws ErrorResultException { if (isSupported(SimplePagedResultsControl.OID)) { ByteString cookie = ByteString.empty(); SearchRequest request; final 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 (DecodeException e) { e.printStackTrace(); } ++page; } while (cookie.length() != 0); } else { System.out.println("SimplePagedResultsControl not supported"); } } opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-controls.xml
@@ -136,31 +136,31 @@ <programlisting language="java"> if (isSupported(AssertionRequestControl.OID)) { // Modify Babs Jensen's description if her entry does not have // a description, yet. final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; ModifyRequest request = Requests.newModifyRequest(dn); request.addControl(AssertionRequestControl.newControl(true, Filter .valueOf("!(description=*)"))); request.addModification(ModificationType.ADD, "description", "Created with the help of the LDAP assertion control"); final ModifyRequest request = Requests.newModifyRequest(dn) .addControl(AssertionRequestControl.newControl( true, Filter.valueOf("!(description=*)"))) .addModification(ModificationType.ADD, "description", "Created using LDAP assertion control"); connection.modify(request); LDIFEntryWriter writer = new LDIFEntryWriter(System.out); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); try { writer.writeEntry(connection.readEntry(dn, "description")); writer.close(); } catch (final IOException e) { e.printStackTrace(); } }</programlisting> } </programlisting> <para>OpenDJ directory server supports the LDAP assertion control:</para> <programlisting language="ldif">dn: uid=bjensen,ou=People,dc=example,dc=com description: Created with the help of the LDAP assertion control</programlisting> description: Created using LDAP assertion control</programlisting> </section> <section xml:id="use-authorization-identity-control"> @@ -173,12 +173,13 @@ <programlisting language="java"> if (isSupported(AuthorizationIdentityRequestControl.OID)) { final String name = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] password = "hifalutin".toCharArray(); final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] pwd = "hifalutin".toCharArray(); System.out.println("Binding as " + name); BindRequest request = Requests.newSimpleBindRequest(name, password); request.addControl(AuthorizationIdentityRequestControl.newControl(true)); System.out.println("Binding as " + dn); final BindRequest request = Requests.newSimpleBindRequest(dn, pwd) .addControl(AuthorizationIdentityRequestControl.newControl(true)); final BindResult result = connection.bind(request); try { @@ -190,7 +191,8 @@ } catch (final DecodeException e) { e.printStackTrace(); } }</programlisting> } </programlisting> <para>OpenDJ directory server supports the LDAP Authorization Identity Controls:</para> @@ -210,14 +212,12 @@ <programlisting language="java"> if (isSupported(PersistentSearchRequestControl.OID)) { SearchRequest request = final SearchRequest request = Requests.newSearchRequest( "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(objectclass=inetOrgPerson)", "cn"); request.addControl(PersistentSearchRequestControl.newControl( true, true, true, // isCritical, changesOnly, returnECs "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, @@ -267,6 +267,12 @@ Change type: modifyDN Previous DN: uid=abarnes,ou=People,dc=example,dc=com Change number: -1</programlisting> <para>In this case, <literal>Change number: -1</literal> because the server did not set a change number value. OpenDJ directory server does not set the change number value in the response control. If you need to track the order of changes with OpenDJ directory server, read the external change log instead of using the entry change notification response control.</para> </section> <section xml:id="use-get-effective-rights-control"> @@ -282,12 +288,12 @@ if (isSupported(GetEffectiveRightsRequestControl.OID)) { final String authDN = "uid=kvaughan,ou=People,dc=example,dc=com"; SearchRequest request = final SearchRequest request = Requests.newSearchRequest( "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo"); request.addControl( GetEffectiveRightsRequestControl.newControl(true, authDN, "cn")); "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo") .addControl(GetEffectiveRightsRequestControl.newControl( true, authDN, "cn")); final ConnectionEntryReader reader = connection.search(request); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); @@ -435,11 +441,11 @@ <programlisting language="java"> if (isSupported(MatchedValuesRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; SearchRequest request = final SearchRequest request = Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT, "(objectclass=*)", "cn"); final String filter = "cn=Babs Jensen"; request.addControl(MatchedValuesRequestControl.newControl(true, filter)); "(objectclass=*)", "cn") .addControl(MatchedValuesRequestControl.newControl( true, "(cn=Babs Jensen)")); final SearchResultEntry entry = connection.searchSingleEntry(request); System.out.println("Reading entry with matched values request."); @@ -464,22 +470,177 @@ <section xml:id="use-password-expired-control"> <title>Password Expired Response Control</title> <para>TODO</para> <para>A directory server can return the Password Expired Response Control, described in the Internet-Draft <link xlink:show="new" xlink:href="http://tools.ietf.org/html/draft-vchu-ldap-pwd-policy"><citetitle >Password Policy for LDAP Directories</citetitle></link>, when a bind fails because the password has expired. In order to see this, you must configure the directory to expire Barbara Jensen's password.</para> <programlisting language="java"> if (isSupported(PasswordExpiredResponseControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] pwd = "hifalutin".toCharArray(); try { connection.bind(dn, pwd); } catch (ErrorResultException e) { final Result result = e.getResult(); try { final PasswordExpiredResponseControl control = result.getControl(PasswordExpiredResponseControl.DECODER, new DecodeOptions()); if (!(control == null) && control.hasValue()) { System.out.println("Password expired for " + dn); } } catch (DecodeException de) { de.printStackTrace(); } } } </programlisting> <para>OpenDJ directory server supports the Password Expired Response Control. To obtain the following output from the excerpt, you can change the default password policy configuration to set a short maximum password age, change Barbara Jensen's password, and wait for it to expire.</para> <programlisting >Password expired for uid=bjensen,ou=People,dc=example,dc=com</programlisting> </section> <section xml:id="use-password-expiring-control"> <title>Password Expiring Response Control</title> <para>TODO</para> <para>The Password Expiring Response Control, described in the Internet-Draft <link xlink:href="http://tools.ietf.org/html/draft-vchu-ldap-pwd-policy" xlink:show="new" ><citetitle>Password Policy for LDAP Directories</citetitle></link>, warns your application during a bind that the password used will soon expire.</para> <programlisting language="java"> if (isSupported(PasswordExpiringResponseControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] pwd = "hifalutin".toCharArray(); final BindResult result = connection.bind(dn, pwd); try { final PasswordExpiringResponseControl control = result.getControl(PasswordExpiringResponseControl.DECODER, new DecodeOptions()); if (!(control == null) && control.hasValue()) { System.out.println("Password for " + dn + " expires in " + control.getSecondsUntilExpiration() + " seconds."); } } catch (DecodeException de) { de.printStackTrace(); } } </programlisting> <para>OpenDJ directory server supports the Password Expiring Response Control. To obtain the following output from the excerpt, you can change the default password policy configuration to set a maximum password age and a warning interval, change Barbara Jensen's password, and wait until you enter the warning interval before password expiration.</para> <programlisting>Password for uid=bjensen,ou=People,dc=example,dc=com expires in 237 seconds.</programlisting> </section> <section xml:id="use-password-policy-controls"> <title>Password Policy Controls</title> <para>TODO</para> <para>The Behera Internet-Draft, <link xlink:show="new" xlink:href="http://tools.ietf.org/html/draft-behera-ldap-password-policy" ><citetitle>Password Policy for LDAP Directories</citetitle></link>, describes Password Policy Request and Response Controls. You send the request control with a request to let the directory server know that your application can handle the response control. The directory server sends the response control on applicable operations to communicate warnings and errors.</para> <programlisting language="java"> if (isSupported(PasswordPolicyRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final char[] pwd = "hifalutin".toCharArray(); try { final BindRequest request = Requests.newSimpleBindRequest(dn, pwd) .addControl(PasswordPolicyRequestControl.newControl(true)); final BindResult result = connection.bind(request); final PasswordPolicyResponseControl control = result.getControl(PasswordPolicyResponseControl.DECODER, new DecodeOptions()); if (!(control == null) && !(control.getWarningType() == null)) { System.out.println("Password policy warning " + control.getWarningType().toString() + ", value " + control.getWarningValue() + " for " + dn); } } catch (ErrorResultException e) { final Result result = e.getResult(); try { final PasswordPolicyResponseControl control = result.getControl(PasswordPolicyResponseControl.DECODER, new DecodeOptions()); if (!(control == null)) { System.out.println("Password policy error " + control.getErrorType().toString() + " for " + dn); } } catch (DecodeException de) { de.printStackTrace(); } } catch (DecodeException e) { e.printStackTrace(); } } </programlisting> <para>OpenDJ directory server supports the Password Policy Controls. To obtain the output from the excerpt, you can change the default password policy configuration to set a maximum password age and a warning interval, change Barbara Jensen's password, and then run the example during the warning interval and after the password has expired.</para> <para>For a warning:</para> <programlisting>Password policy warning timeBeforeExpiration, value 237 for uid=bjensen,ou=People,dc=example,dc=com</programlisting> <para>For an error:</para> <programlisting>Password policy error passwordExpired for uid=bjensen,ou=People,dc=example,dc=com</programlisting> </section> <section xml:id="use-permissive-modify-request-control"> <title>Permissive Modify Request Control</title> <para>TODO</para> <para>Microsoft defined a Permissive Modify Request Control that relaxes some constraints when your application performs a modify operation and tries to <literal>add</literal> an attribute that already exists, or to <literal>delete</literal> an attribute that does not exist.</para> <programlisting language="java"> if (isSupported(PermissiveModifyRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final ModifyRequest request = Requests.newModifyRequest(dn) .addControl(PermissiveModifyRequestControl.newControl(true)) .addModification(ModificationType.ADD, "uid", "bjensen"); connection.modify(request); System.out.println("Permissive modify did not complain about " + "attempt to add uid: bjensen to " + dn + "."); } </programlisting> <para>OpenDJ directory server supports the Permissive Modify Request Control:</para> <programlisting>Permissive modify did not complain about attempt to add uid: bjensen to uid=bjensen,ou=People,dc=example,dc=com.</programlisting> </section> <section xml:id="use-persistent-search-request-control"> @@ -490,27 +651,310 @@ <section xml:id="use-post-read-control"> <title>Post-Read Controls</title> <para>TODO</para> <para>RFC 4527, <link xlink:href="http://tools.ietf.org/html/rfc4527" xlink:show="new"><citetitle>LDAP Read Entry Controls</citetitle></link>, describes the post-read controls that let your application get the content of an entry immediately after modifications are applied.</para> <programlisting language="java"> if (isSupported(PostReadRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final ModifyRequest request = Requests.newModifyRequest(dn) .addControl(PostReadRequestControl.newControl(true, "description")) .addModification(ModificationType.REPLACE, "description", "Using the PostReadRequestControl"); final Result result = connection.modify(request); try { final PostReadResponseControl control = result.getControl(PostReadResponseControl.DECODER, new DecodeOptions()); final Entry entry = control.getEntry(); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); writer.writeEntry(entry); writer.close(); } catch (DecodeException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } </programlisting> <para>OpenDJ directory server supports these controls:</para> <programlisting language="ldif">dn: uid=bjensen,ou=People,dc=example,dc=com description: Using the PostReadRequestControl</programlisting> </section> <section xml:id="use-pre-read-control"> <title>Pre-Read Controls</title> <para>TODO</para> <para>RFC 4527, <link xlink:href="http://tools.ietf.org/html/rfc4527" xlink:show="new"><citetitle>LDAP Read Entry Controls</citetitle></link>, describes the pre-read controls that let your application get the content of an entry immediately before modifications are applied.</para> <programlisting language="java"> if (isSupported(PreReadRequestControl.OID)) { final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; final ModifyRequest request = Requests.newModifyRequest(dn) .addControl(PreReadRequestControl.newControl(true, "mail")) .addModification( ModificationType.REPLACE, "mail", "modified@example.com"); final Result result = connection.modify(request); try { final PreReadResponseControl control = result.getControl(PreReadResponseControl.DECODER, new DecodeOptions()); final Entry entry = control.getEntry(); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); writer.writeEntry(entry); writer.close(); } catch (DecodeException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } </programlisting> <para>OpenDJ directory server supports these controls:</para> <programlisting language="ldif">dn: uid=bjensen,ou=People,dc=example,dc=com mail: bjensen@example.com</programlisting> </section> <section xml:id="use-proxy-authz-control"> <title>Proxied Authorization Request Controls</title> <para>TODO</para> <para>Proxied authorization provides a standard control as defined in <link xlink:href="http://tools.ietf.org/html/rfc4370" xlink:show="new">RFC 4370</link> (and an earlier Internet-Draft) for binding with the user credentials of a proxy, who carries out LDAP operations on behalf of other users. You might use proxied authorization, for example, to have your application bind with its credentials, and then carry out operations as the users who login to the application.</para> <programlisting language="java"> if (isSupported(ProxiedAuthV2RequestControl.OID)) { final String bindDN = "cn=My App,ou=Apps,dc=example,dc=com"; final String targetDn = "uid=bjensen,ou=People,dc=example,dc=com"; final String authzId = "dn:uid=kvaughan,ou=People,dc=example,dc=com"; final ModifyRequest request = Requests.newModifyRequest(targetDn) .addControl(ProxiedAuthV2RequestControl.newControl(authzId)) .addModification(ModificationType.REPLACE, "description", "Done with proxied authz"); connection.bind(bindDN, "password".toCharArray()); connection.modify(request); final Entry entry = connection.readEntry(targetDn, "description"); final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); try { writer.writeEntry(entry); writer.close(); } catch (IOException e) { e.printStackTrace(); } }</programlisting> <para>OpenDJ supports proxied authorization, and the example works with the sample data:</para> <programlisting language="ldif">dn: uid=bjensen,ou=People,dc=example,dc=com description: Done with proxied authz</programlisting> </section> <section xml:id="use-server-side-sort-control"> <title>Server-Side Sort Controls</title> <para>TODO</para> <para>The server-side sort controls are described in RFC 2891, <link xlink:show="new" xlink:href="http://tools.ietf.org/html/rfc2891"><citetitle >LDAP Control Extension for Server Side Sorting of Search Results</citetitle></link>. If possible, sort on the client side instead to reduce load on the server. If not, then you can request a server-side sort.</para> <programlisting language="java"> static void useServerSideSortRequestControl(Connection connection) throws ErrorResultException { if (isSupported(ServerSideSortRequestControl.OID)) { final SearchRequest request = Requests.newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn") .addControl(ServerSideSortRequestControl.newControl( true, new SortKey("cn"))); final SearchResultHandler resultHandler = new MySearchResultHandler(); final Result result = connection.search(request, resultHandler); try { final ServerSideSortResponseControl control = result.getControl(ServerSideSortResponseControl.DECODER, new DecodeOptions()); if (control != null && control.getResult() == ResultCode.SUCCESS) { System.out.println("# Entries are sorted."); // FIXME: But the order is backwards! } else { System.out.println("# Entries not necessarily sorted"); } } catch (DecodeException e) { e.printStackTrace(); } } else { System.out.println("ServerSideSortRequestControl not supported"); } } private static class MySearchResultHandler implements SearchResultHandler { @Override public void handleErrorResult(ErrorResultException error) { // Ignore. } @Override public void handleResult(Result result) { // Ignore. } @Override public boolean handleEntry(SearchResultEntry entry) { final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); try { writer.writeEntry(entry); writer.flush(); } catch (IOException e) { e.printStackTrace(); } return true; } @Override public boolean handleReference(SearchResultReference reference) { System.out.println("Got a reference: " + reference.toString()); return false; } } </programlisting> <para>OpenDJ directory server supports server-side sorting:</para> <programlisting language="ldif">dn: uid=ajensen,ou=People,dc=example,dc=com cn: Allison Jensen dn: uid=bjensen,ou=People,dc=example,dc=com cn: Barbara Jensen cn: Babs Jensen dn: uid=bjense2,ou=People,dc=example,dc=com cn: Bjorn Jensen dn: uid=gjensen,ou=People,dc=example,dc=com cn: Gern Jensen dn: uid=jjensen,ou=People,dc=example,dc=com cn: Jody Jensen dn: uid=kjensen,ou=People,dc=example,dc=com cn: Kurt Jensen dn: uid=rjense2,ou=People,dc=example,dc=com cn: Randy Jensen dn: uid=rjensen,ou=People,dc=example,dc=com cn: Richard Jensen dn: uid=tjensen,ou=People,dc=example,dc=com cn: Ted Jensen # Entries are sorted.</programlisting> </section> <section xml:id="use-simple-paged-results-control"> <title>Simple Paged Results Control</title> <para>TODO</para> <para>RFC 2696, <link xlink:href="http://tools.ietf.org/html/rfc2696" xlink:show="new"><citetitle>LDAP Control Extension for Simple Paged Results Manipulation</citetitle></link>, defines a control for simple paging of search results that works with a cookie mechanism.</para> <programlisting language="java"> if (isSupported(SimplePagedResultsControl.OID)) { ByteString cookie = ByteString.empty(); SearchRequest request; final 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 (DecodeException e) { e.printStackTrace(); } ++page; } while (cookie.length() != 0); } </programlisting> <para>OpenDJ directory server supports getting simple paged results:</para> <programlisting language="ldif"># Simple paged results: Page 1 dn: uid=ajensen,ou=People,dc=example,dc=com cn: Allison Jensen dn: uid=bjense2,ou=People,dc=example,dc=com cn: Bjorn Jensen dn: uid=bjensen,ou=People,dc=example,dc=com cn: Barbara Jensen cn: Babs Jensen # Simple paged results: Page 2 dn: uid=gjensen,ou=People,dc=example,dc=com cn: Gern Jensen dn: uid=jjensen,ou=People,dc=example,dc=com cn: Jody Jensen dn: uid=kjensen,ou=People,dc=example,dc=com cn: Kurt Jensen # Simple paged results: Page 3 dn: uid=rjense2,ou=People,dc=example,dc=com cn: Randy Jensen dn: uid=rjensen,ou=People,dc=example,dc=com cn: Richard Jensen dn: uid=tjensen,ou=People,dc=example,dc=com cn: Ted Jensen </programlisting> </section> <section xml:id="use-subentry-request-control"> opendj-sdk/opendj3/src/site/resources/Example.ldif
@@ -61,6 +61,9 @@ aci: (target="ldap:///dc=example,dc=com") (targetattr = "*")(version 3.0; acl "allow all Admin group"; allow(all) groupdn = "ldap:///cn=Directory Administrators,ou=Groups,dc=example,dc=com";) aci: (target="ldap:///dc=example,dc=com") (targetattr ="* ")(version 3.0; acl "Allow apps proxied auth"; allow(all, proxy )(userdn = "ldap:///cn=*,ou=Apps,dc=example,dc=com");) dn: ou=Company Servers,dc=example,dc=com objectClass: organizationalUnit @@ -3756,3 +3759,18 @@ objectClass: top ref: ldap:///ou=People,dc=example,dc=com dn: ou=Apps,dc=example,dc=com objectClass: organizationalUnit objectClass: top ou: Apps dn: cn=My App,ou=Apps,dc=example,dc=com cn: My App objectClass: person objectClass: inetOrgPerson objectClass: organizationalPerson objectClass: top sn: App userPassword: password ds-privilege-name: proxied-auth