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

Mark Craig
03.44.2012 cf136586c780f03c81e504ec2b8018a0598a9c28
A bit more for the LDAP controls chapter
2 files modified
461 ■■■■■ changed files
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java 188 ●●●●● patch | view | raw | blame | history
opendj3/src/main/docbkx/dev-guide/chap-controls.xml 273 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
@@ -30,14 +30,30 @@
import java.util.Collection;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.DecodeOptions;
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.RootDSE;
import org.forgerock.opendj.ldap.SearchResultReferenceIOException;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.controls.AssertionRequestControl;
import org.forgerock.opendj.ldap.controls.AuthorizationIdentityRequestControl;
import org.forgerock.opendj.ldap.controls.AuthorizationIdentityResponseControl;
import org.forgerock.opendj.ldap.controls.EntryChangeNotificationResponseControl;
import org.forgerock.opendj.ldap.controls.GetEffectiveRightsRequestControl;
import org.forgerock.opendj.ldap.controls.PersistentSearchChangeType;
import org.forgerock.opendj.ldap.controls.PersistentSearchRequestControl;
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.SearchResultEntry;
import org.forgerock.opendj.ldif.ConnectionEntryReader;
import org.forgerock.opendj.ldif.LDIFEntryWriter;
/**
@@ -52,6 +68,7 @@
 * to use the LDAP controls needed.
 */
public final class Controls {
    /**
     * Connect to the server, and then try to use some LDAP controls.
     *
@@ -64,8 +81,8 @@
            System.err.println("For example: localhost 1389");
            System.exit(1);
        }
        String host = args[0];
        int port = Integer.parseInt(args[1]);
        final String host = args[0];
        final int port = Integer.parseInt(args[1]);
        final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port);
        Connection connection = null;
@@ -73,11 +90,18 @@
            connection = factory.getConnection();
            checkSupportedControls(connection);
            String user = "cn=Directory Manager";
            char[] password = "password".toCharArray();
            final String user = "cn=Directory Manager";
            final char[] password = "password".toCharArray();
            connection.bind(user, password);
            useAssertionControl(connection);
            // Uncomment one of the methods:
            //useAssertionControl(connection);
            useAuthorizationIdentityRequestControl(connection);
            // For the EntryChangeNotificationResponseControl see
            // usePersistentSearchRequestControl()
            //useGetEffectiveRightsRequestControl(connection);
            //usePersistentSearchRequestControl(connection);
            // TODO: The rest of the supported controls
        } catch (final ErrorResultException e) {
@@ -98,16 +122,18 @@
     *            Active connection to LDAP server containing <a
     *            href="http://opendj.forgerock.org/Example.ldif"
     *            >Example.ldif</a> content.
     * @throws ErrorResultException
     *             Operation failed.
     */
    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.
            String dn = "uid=bjensen,ou=People,dc=example,dc=com";
            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.addControl(AssertionRequestControl.newControl(true, Filter
                    .valueOf("!(description=*)")));
            request.addModification(ModificationType.ADD, "description",
                    "Created with the help of the LDAP assertion control");
@@ -118,7 +144,145 @@
                writer.writeEntry(connection.readEntry(dn, "description"));
                writer.close();
            } catch (final IOException e) {
                // Ignore.
                e.printStackTrace();
            }
        }
    }
    /**
     * Use the LDAP Authorization Identity Controls to get the authorization ID.
     *
     * @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 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();
            System.out.println("Binding as " + name);
            BindRequest request = Requests.newSimpleBindRequest(name, password);
            request.addControl(AuthorizationIdentityRequestControl.newControl(true));
            final BindResult result = connection.bind(request);
            try {
                final AuthorizationIdentityResponseControl control =
                        result.getControl(AuthorizationIdentityResponseControl.DECODER,
                                new DecodeOptions());
                System.out.println("Authorization ID returned: "
                                + control.getAuthorizationID());
            } catch (final DecodeException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * Use the GetEffectiveRights Request Control to determine what sort of
     * access a user has to particular attributes on an entry.
     *
     * @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 useGetEffectiveRightsRequestControl(Connection connection)
            throws ErrorResultException {
        if (isSupported(GetEffectiveRightsRequestControl.OID)) {
            final String authDN = "uid=kvaughan,ou=People,dc=example,dc=com";
            SearchRequest request =
                    Requests.newSearchRequest(
                            "dc=example,dc=com", SearchScope.WHOLE_SUBTREE,
                            "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo");
            request.addControl(
                    GetEffectiveRightsRequestControl.newControl(true, authDN, "cn"));
            final ConnectionEntryReader reader = connection.search(request);
            final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
            try {
                while (reader.hasNext()) {
                    if (!reader.isReference()) {
                        final SearchResultEntry entry = reader.readEntry();
                        writer.writeEntry(entry);
                    }
                }
                writer.close();
            } catch (final ErrorResultIOException e) {
                e.printStackTrace();
            } catch (final SearchResultReferenceIOException e) {
                e.printStackTrace();
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * Use the LDAP PersistentSearchRequestControl to set up a persistent
     * search. Also use the Entry Change Notification Response Control to get
     * details about why an entry was returned for a persistent search.
     *
     * After you set this up, use another application to make changes to user
     * entries under dc=example,dc=com.
     *
     * @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 usePersistentSearchRequestControl(Connection connection) throws ErrorResultException {
        if (isSupported(PersistentSearchRequestControl.OID)) {
            SearchRequest request =
                    Requests.newSearchRequest(
                            "dc=example,dc=com",
                            SearchScope.WHOLE_SUBTREE,
                            "(objectclass=inetOrgPerson)",
                            "cn");
            request.addControl(PersistentSearchRequestControl.newControl(
                    true, true, true, // isCritical, changesOnly, returnECs
                    PersistentSearchChangeType.ADD,
                    PersistentSearchChangeType.DELETE,
                    PersistentSearchChangeType.MODIFY,
                    PersistentSearchChangeType.MODIFY_DN));
            final ConnectionEntryReader reader = connection.search(request);
            try {
                while (reader.hasNext()) {
                    if (!reader.isReference()) {
                        final SearchResultEntry entry = reader.readEntry();
                        System.out.println("Entry changed: " + entry.getName().toString());
                        final EntryChangeNotificationResponseControl control =
                                entry.getControl(
                                        EntryChangeNotificationResponseControl.DECODER,
                                        new DecodeOptions());
                        final PersistentSearchChangeType type = control.getChangeType();
                        System.out.println("Change type: " + type.toString());
                        if (type.equals(PersistentSearchChangeType.MODIFY_DN)) {
                            System.out.println("Previous DN: "
                                    + control.getPreviousName().toString());
                        }
                        System.out.println("Change number: " + control.getChangeNumber());
                        System.out.println(); // Add a blank line.
                    }
                }
            } catch (final DecodeException e) {
                e.printStackTrace();
            } catch (final ErrorResultIOException e) {
                e.printStackTrace();
            } catch (final SearchResultReferenceIOException e) {
                e.printStackTrace();
            }
        }
    }
@@ -134,10 +298,10 @@
     * @param connection
     *            Active connection to the LDAP server.
     * @throws ErrorResultException
     *             Failed to get list of controls.
     */
    static void checkSupportedControls(Connection connection) throws ErrorResultException {
        RootDSE dse = RootDSE.readRootDSE(connection);
        controls = dse.getSupportedControls();
        controls = RootDSE.readRootDSE(connection).getSupportedControls();
    }
    /**
@@ -148,7 +312,7 @@
     *            Check support for this control, provided by OID.
     * @return True if the control is supported.
     */
    static boolean isSupported(String control) {
    static boolean isSupported(final String control) {
        if (controls != null && !controls.isEmpty()) {
            return controls.contains(control);
        }
opendj3/src/main/docbkx/dev-guide/chap-controls.xml
@@ -86,30 +86,43 @@
supportedControl: 2.16.840.1.113730.3.4.5
supportedControl: 2.16.840.1.113730.3.4.9</screen>
  <para>The following excerpt shows the Java equivalent of the preceding
  command.</para>
  <para>The following excerpt shows couple of methods to check whether the
  directory server supports a control.</para>
  <programlisting language="java">
final LDAPConnectionFactory factory = new LDAPConnectionFactory(
  host, port);
Connection connection = null;
/**
 * Controls supported by the LDAP server.
 */
private static Collection&lt;String> controls;
try
{
  connection = factory.getConnection();
/**
 * Populate the list of supported LDAP control OIDs.
 *
 * @param connection
 *            Active connection to the LDAP server.
 * @throws ErrorResultException
 *             Failed to get list of controls.
 */
static void checkSupportedControls(Connection connection)
        throws ErrorResultException {
    controls = RootDSE.readRootDSE(connection).getSupportedControls();
}
  // Perform an anonymous search on the root DSE.
  final SearchResultEntry entry = connection.searchSingleEntry(
      "",                         // DN is "" for root DSE.
      SearchScope.BASE_OBJECT,    // Read only the root DSE.
      "objectclass=*",            // Every object matches this filter.
      "supportedControl");        // Check supported controls.
  final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
  writer.writeComment("Supported controls for server " + host + ":" + port);
  if (entry != null) writer.writeEntry(entry);
  writer.flush();
}</programlisting>
/**
 * Check whether a control is supported. Call {@code checkSupportedControls}
 * first.
 *
 * @param control
 *            Check support for this control, provided by OID.
 * @return True if the control is supported.
 */
static boolean isSupported(final String control) {
    if (controls != null &amp;&amp; !controls.isEmpty()) {
        return controls.contains(control);
    }
    return false;
}
</programlisting>
 </section>
 
 <section xml:id="use-assertion-request-control">
@@ -125,11 +138,11 @@
if (isSupported(AssertionRequestControl.OID)) {
    // Modify Babs Jensen's description if her entry does not have
    // a description, yet.
    String dn = "uid=bjensen,ou=People,dc=example,dc=com";
    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.addControl(AssertionRequestControl.newControl(true, Filter
            .valueOf("!(description=*)")));
    request.addModification(ModificationType.ADD, "description",
            "Created with the help of the LDAP assertion control");
@@ -140,26 +153,223 @@
        writer.writeEntry(connection.readEntry(dn, "description"));
        writer.close();
    } catch (final IOException e) {
        // Ignore.
        e.printStackTrace();
    }
}</programlisting>
  <para>OpenDJ directory server supports the LDAP assertion control.</para>
  <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>
 </section>
 <section xml:id="use-authorization-identity-control">
  <title>Authorization Identity Controls</title>
  <para>TODO</para>
  <para>The <link xlink:href="http://tools.ietf.org/html/rfc3829"
  xlink:show="new">LDAP Authorization Identity Controls</link> let you get the
  authorization identity established when you bind to the directory server.
  The following excerpt shows simple use of the controls.</para>
  <programlisting language="java">
if (isSupported(AuthorizationIdentityRequestControl.OID)) {
    final String name = "uid=bjensen,ou=People,dc=example,dc=com";
    final char[] password = "hifalutin".toCharArray();
    System.out.println("Binding as " + name);
    BindRequest request = Requests.newSimpleBindRequest(name, password);
    request.addControl(AuthorizationIdentityRequestControl.newControl(true));
    final BindResult result = connection.bind(request);
    try {
        final AuthorizationIdentityResponseControl control =
                result.getControl(AuthorizationIdentityResponseControl.DECODER,
                        new DecodeOptions());
        System.out.println("Authorization ID returned: "
                        + control.getAuthorizationID());
    } catch (final DecodeException e) {
        e.printStackTrace();
    }
}</programlisting>
  <para>OpenDJ directory server supports the LDAP Authorization Identity
  Controls:</para>
  <programlisting>Binding as uid=bjensen,ou=People,dc=example,dc=com
Authorization ID returned: dn:uid=bjensen,ou=People,dc=example,dc=com</programlisting>
 </section>
 
 <section xml:id="use-entry-change-notification-control">
  <title>Entry Change Notification Response Controls</title>
  <para>TODO</para>
  <para>When performing a persistent search, your application can retrieve
  information using this response control about why the directory server
  returned the entry. See the Internet-Draft on <link xlink:show="new"
  xlink:href="tools.ietf.org/html/draft-ietf-ldapext-psearch">persistent
  searches</link> for background information.</para>
  <programlisting language="java">
if (isSupported(PersistentSearchRequestControl.OID)) {
    SearchRequest request =
            Requests.newSearchRequest(
                    "dc=example,dc=com",
                    SearchScope.WHOLE_SUBTREE,
                    "(objectclass=inetOrgPerson)",
                    "cn");
    request.addControl(PersistentSearchRequestControl.newControl(
            true, true, true, // isCritical, changesOnly, returnECs
            PersistentSearchChangeType.ADD,
            PersistentSearchChangeType.DELETE,
            PersistentSearchChangeType.MODIFY,
            PersistentSearchChangeType.MODIFY_DN));
    final ConnectionEntryReader reader = connection.search(request);
    try {
        while (reader.hasNext()) {
            if (!reader.isReference()) {
                final SearchResultEntry entry = reader.readEntry();
                System.out.println("Entry changed: "
                        + entry.getName().toString());
                EntryChangeNotificationResponseControl control =
                        entry.getControl(
                                EntryChangeNotificationResponseControl.DECODER,
                                new DecodeOptions());
                PersistentSearchChangeType type = control.getChangeType();
                System.out.println("Change type: " + type.toString());
                if (type.equals(PersistentSearchChangeType.MODIFY_DN)) {
                    System.out.println("Previous DN: "
                            + control.getPreviousName().toString());
                }
                System.out.println("Change number: "
                        + control.getChangeNumber());
                System.out.println(); // Add a blank line.
           }
        }
    } catch (final DecodeException e) {
        e.printStackTrace();
    } catch (final ErrorResultIOException e) {
        e.printStackTrace();
    } catch (final SearchResultReferenceIOException e) {
        e.printStackTrace();
    }
}
</programlisting>
  <para>OpenDJ directory server supports persistent searches and the entry
  change notification response control. When another application renames
  Anne-Louise Barnes's entry, the sample code picks up information from the
  entry change notification response control:</para>
  <programlisting>Entry changed: uid=bdobbs,ou=People,dc=example,dc=com
Change type: modifyDN
Previous DN: uid=abarnes,ou=People,dc=example,dc=com
Change number: -1</programlisting>
 </section>
 
 <section xml:id="use-get-effective-rights-control">
  <title>Get Effective Rights Request Control</title>
  <para>TODO</para>
  <title>GetEffectiveRights Request Control</title>
  <para>Your application can attach the GetEffectiveRights request control to
  a search in order to determine what access a user has to perform operations
  on entries found. See the Internet-Draft on the <link xlink:show="new"
  xlink:href="http://tools.ietf.org/html/draft-ietf-ldapext-acl-model">Access
  Control Model for LDAP</link> for background.</para>
  <programlisting language="java">
if (isSupported(GetEffectiveRightsRequestControl.OID)) {
    final String authDN = "uid=kvaughan,ou=People,dc=example,dc=com";
    SearchRequest request =
            Requests.newSearchRequest(
                    "dc=example,dc=com", SearchScope.WHOLE_SUBTREE,
                    "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo");
    request.addControl(
            GetEffectiveRightsRequestControl.newControl(true, authDN, "cn"));
    final ConnectionEntryReader reader = connection.search(request);
    final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
    try {
        while (reader.hasNext()) {
            if (!reader.isReference()) {
                final SearchResultEntry entry = reader.readEntry();
                writer.writeEntry(entry);
            }
        }
        writer.close();
    } catch (final ErrorResultIOException e) {
        e.printStackTrace();
    } catch (final SearchResultReferenceIOException e) {
        e.printStackTrace();
    } catch (final IOException e) {
        e.printStackTrace();
    }
}
</programlisting>
  <para>OpenDJ SDK currently implements the request control, but not the
  response control. The results are shown as values of the
  <literal>aclRights</literal> and more verbose <literal>aclRightsInfo</literal>
  attributes.</para>
  <programlisting language="ldif">
dn: uid=bjensen,ou=People,dc=example,dc=com
aclRightsInfo;logs;attributeLevel;selfwrite_delete;cn: acl_summary(main)
 : access allowed(write) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com
 , distinguishedName) to (uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: evaluated allow , deciding_aci: allow all Admin group)
aclRightsInfo;logs;entryLevel;read: acl_summary(main): access allowed(read
 ) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, objectClass) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied) ( reason
 : evaluated allow , deciding_aci: Anonymous read-search access)
aclRightsInfo;logs;attributeLevel;proxy;cn: acl_summary(main)
 : access not allowed(proxy) on entry/attr(uid=bjensen,ou=People,dc=example,
 dc=com, cn) to (uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) (reason: no acis matched the subject )
aclRights;attributeLevel;cn: search:1,read:1,compare:1,write:1,selfwrite_add:1,
 selfwrite_delete:1,proxy:0
aclRightsInfo;logs;attributeLevel;write;cn: acl_summary(main): access allowed
 (write) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, cn) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: evaluated allow , deciding_aci: allow all Admin group)
aclRights;entryLevel: add:1,delete:1,read:1,write:1,proxy:0
aclRightsInfo;logs;attributeLevel;search;cn: acl_summary(main): access allowed(
 search) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, cn) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: evaluated allow , deciding_aci: Anonymous read-search access)
aclRightsInfo;logs;entryLevel;write: acl_summary(main): access allowed(write
 ) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, NULL) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: evaluated allow , deciding_aci: allow all Admin group)
aclRightsInfo;logs;attributeLevel;selfwrite_add;cn: acl_summary(main
 ): access allowed(write) on entry/attr(uid=bjensen,ou=People,dc=example,
 dc=com, distinguishedName) to (uid=kvaughan,ou=People,dc=example,dc=com) (
 not proxied) ( reason: evaluated allow , deciding_aci: allow all Admin group)
aclRightsInfo;logs;entryLevel;add: acl_summary(main): access allowed(add
 ) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, NULL) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: evaluated allow , deciding_aci: allow all Admin group)
aclRightsInfo;logs;attributeLevel;read;cn: acl_summary(main): access allowed(
 read) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, cn) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: evaluated allow , deciding_aci: Anonymous read-search access)
cn: Barbara Jensen
cn: Babs Jensen
aclRightsInfo;logs;entryLevel;proxy: acl_summary(main): access not allowed(
 proxy) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, NULL) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: no acis matched the subject )
aclRightsInfo;logs;attributeLevel;compare;cn: acl_summary(main): access allowed
 (compare) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, cn) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: evaluated allow , deciding_aci: Anonymous read-search access)
aclRightsInfo;logs;entryLevel;delete: acl_summary(main): access allowed(
 delete) on entry/attr(uid=bjensen,ou=People,dc=example,dc=com, NULL) to (
 uid=kvaughan,ou=People,dc=example,dc=com) (not proxied
 ) ( reason: evaluated allow , deciding_aci: allow all Admin group)
</programlisting>
 </section>
 
 <section xml:id="use-manage-dsait-control-control">
@@ -194,7 +404,8 @@
 
 <section xml:id="use-persistent-search-request-control">
  <title>Persistent Search Request Control</title>
  <para>TODO</para>
  <para>See <xref linkend="use-entry-change-notification-control" />.</para>
 </section>
 
 <section xml:id="use-post-read-control">