<?xml version="1.0" encoding="UTF-8"?>
|
<!--
|
! CCPL HEADER START
|
!
|
! This work is licensed under the Creative Commons
|
! Attribution-NonCommercial-NoDerivs 3.0 Unported License.
|
! To view a copy of this license, visit
|
! http://creativecommons.org/licenses/by-nc-nd/3.0/
|
! or send a letter to Creative Commons, 444 Castro Street,
|
! Suite 900, Mountain View, California, 94041, USA.
|
!
|
! You can also obtain a copy of the license at
|
! trunk/opendj3/legal-notices/CC-BY-NC-ND.txt.
|
! See the License for the specific language governing permissions
|
! and limitations under the License.
|
!
|
! If applicable, add the following below this CCPL HEADER, with the fields
|
! enclosed by brackets "[]" replaced with your own identifying information:
|
! Portions Copyright [yyyy] [name of copyright owner]
|
!
|
! CCPL HEADER END
|
!
|
! Copyright 2011-2012 ForgeRock AS
|
!
|
-->
|
<chapter xml:id='chap-controls'
|
xmlns='http://docbook.org/ns/docbook' version='5.0' xml:lang='en'
|
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
xsi:schemaLocation='http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd'
|
xmlns:xlink='http://www.w3.org/1999/xlink'
|
xmlns:xinclude='http://www.w3.org/2001/XInclude'>
|
<title>Working With Controls</title>
|
|
<para>This chapter demonstrates how to use LDAP controls.</para>
|
|
<section xml:id="about-ldap-controls">
|
<title>About LDAP Controls</title>
|
<para>Controls provide a mechanism whereby the semantics and arguments of
|
existing LDAP operations may be extended. One or more controls may be
|
attached to a single LDAP message. A control only affects the semantics of
|
the message it is attached to. Controls sent by clients are termed
|
<emphasis>request controls</emphasis>, and those sent by servers are termed
|
<emphasis>response controls</emphasis>.</para>
|
</section>
|
|
<section xml:id="get-supported-controls">
|
<title>Determining Supported Controls</title>
|
|
<para>For OpenDJ, the controls supported are listed in the
|
<citetitle>Administration Guide</citetitle> appendix, <link
|
xlink:href="admin-guide#appendix-controls"
|
xlink:role="http://docbook.org/xlink/role/olink"><citetitle>LDAP
|
Controls</citetitle></link>. You can access the list of OIDs for
|
supported LDAP controls by reading the <literal>supportedControl</literal>
|
attribute of the root DSE.</para>
|
|
<screen>$ ldapsearch
|
--baseDN ""
|
--searchScope base
|
--port 1389
|
"(objectclass=*)" supportedControl
|
dn:
|
supportedControl: 1.2.826.0.1.3344810.2.3
|
supportedControl: 1.2.840.113556.1.4.1413
|
supportedControl: 1.2.840.113556.1.4.319
|
supportedControl: 1.2.840.113556.1.4.473
|
supportedControl: 1.2.840.113556.1.4.805
|
supportedControl: 1.3.6.1.1.12
|
supportedControl: 1.3.6.1.1.13.1
|
supportedControl: 1.3.6.1.1.13.2
|
supportedControl: 1.3.6.1.4.1.26027.1.5.2
|
supportedControl: 1.3.6.1.4.1.42.2.27.8.5.1
|
supportedControl: 1.3.6.1.4.1.42.2.27.9.5.2
|
supportedControl: 1.3.6.1.4.1.42.2.27.9.5.8
|
supportedControl: 1.3.6.1.4.1.4203.1.10.1
|
supportedControl: 1.3.6.1.4.1.4203.1.10.2
|
supportedControl: 1.3.6.1.4.1.7628.5.101.1
|
supportedControl: 2.16.840.1.113730.3.4.12
|
supportedControl: 2.16.840.1.113730.3.4.16
|
supportedControl: 2.16.840.1.113730.3.4.17
|
supportedControl: 2.16.840.1.113730.3.4.18
|
supportedControl: 2.16.840.1.113730.3.4.19
|
supportedControl: 2.16.840.1.113730.3.4.2
|
supportedControl: 2.16.840.1.113730.3.4.3
|
supportedControl: 2.16.840.1.113730.3.4.4
|
supportedControl: 2.16.840.1.113730.3.4.5
|
supportedControl: 2.16.840.1.113730.3.4.9</screen>
|
|
<para>The following excerpt shows couple of methods to check whether the
|
directory server supports a control.</para>
|
|
<programlisting language="java">
|
/**
|
* Controls supported by the LDAP server.
|
*/
|
private static Collection<String> controls;
|
|
/**
|
* 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();
|
}
|
|
/**
|
* 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 && !controls.isEmpty()) {
|
return controls.contains(control);
|
}
|
return false;
|
}
|
</programlisting>
|
</section>
|
|
<section xml:id="use-assertion-request-control">
|
<title>Assertion Request Control</title>
|
|
<para>The <link xlink:href="http://tools.ietf.org/html/rfc4528"
|
xlink:show="new" >LDAP assertion control</link> lets you specify a condition
|
that must be true in order for the operation you request to be processed
|
normally. The following excerpt shows, for example, how you might check
|
that no description exists on the entry before adding your description.</para>
|
|
<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");
|
|
connection.modify(request);
|
|
LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
|
try {
|
writer.writeEntry(connection.readEntry(dn, "description"));
|
writer.close();
|
} catch (final IOException e) {
|
e.printStackTrace();
|
}
|
}</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>
|
</section>
|
|
<section xml:id="use-authorization-identity-control">
|
<title>Authorization Identity Controls</title>
|
|
<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>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>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-managedsait-control">
|
<title>ManageDsaIT Request Control</title>
|
|
<para>The ManageDsaIT control, described in <link xlink:show="new"
|
xlink:href="http://tools.ietf.org/html/rfc3296">RFC 3296, <citetitle>Named
|
Subordinate References in LDAP Directories</citetitle></link>, lets your
|
application handle references and other special entries as normal entries.
|
Use it when you want to read from or write to reference or special
|
entry.</para>
|
|
<programlisting language="java">
|
if (isSupported(ManageDsaITRequestControl.OID)) {
|
// This entry is a referral object:
|
final String dn = "dc=references,dc=example,dc=com";
|
|
final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
|
try {
|
System.out.println("Referral without the ManageDsaIT control.");
|
SearchRequest request = Requests.newSearchRequest(dn,
|
SearchScope.BASE_OBJECT, "(objectclass=*)", "");
|
final ConnectionEntryReader reader = connection.search(request);
|
while (reader.hasNext()) {
|
if (reader.isReference()) {
|
final SearchResultReference ref = reader.readReference();
|
System.out.println("Reference: " + ref.getURIs().toString());
|
}
|
}
|
|
System.out.println("Referral with the ManageDsaIT control.");
|
request.addControl(ManageDsaITRequestControl.newControl(true));
|
final SearchResultEntry entry = connection.searchSingleEntry(request);
|
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 directory server supports the ManageDsaIT Request Control.</para>
|
</section>
|
|
<section xml:id="use-matched-values-request-control">
|
<title>Matched Values Request Control</title>
|
|
<para>RFC 3876, <link xlink:href="http://tools.ietf.org/html/rfc3876"
|
xlink:show="new"><citetitle>Returning Matched Values with the
|
LDAPv3</citetitle></link>, describes a control that lets your application
|
pass a filter in a search request getting a multivalued attribute such that
|
the directory server only returns attribute values that match the
|
filter.</para>
|
|
<para>Barbara Jensen's entry contains two common name values,
|
<literal>Barbara Jensen</literal> and <literal>Babs Jensen</literal>. The
|
following excerpt retrieves only the latter.</para>
|
|
<programlisting language="java">
|
if (isSupported(MatchedValuesRequestControl.OID)) {
|
final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
|
SearchRequest request =
|
Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
|
"(objectclass=*)", "cn");
|
final String filter = "cn=Babs Jensen";
|
request.addControl(MatchedValuesRequestControl.newControl(true, filter));
|
|
final SearchResultEntry entry = connection.searchSingleEntry(request);
|
System.out.println("Reading entry with matched values request.");
|
final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
|
try {
|
writer.writeEntry(entry);
|
writer.close();
|
} catch (final IOException e) {
|
e.printStackTrace();
|
}
|
}
|
</programlisting>
|
|
<para>OpenDJ directory server supports the matched values request
|
control.</para>
|
|
<programlisting language="ldif">Reading entry with matched values request.
|
dn: uid=bjensen,ou=People,dc=example,dc=com
|
cn: Babs Jensen
|
</programlisting>
|
</section>
|
|
<section xml:id="use-password-expired-control">
|
<title>Password Expired Response Control</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-password-expiring-control">
|
<title>Password Expiring Response Control</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-password-policy-controls">
|
<title>Password Policy Controls</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-permissive-modify-request-control">
|
<title>Permissive Modify Request Control</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-persistent-search-request-control">
|
<title>Persistent Search Request Control</title>
|
|
<para>See <xref linkend="use-entry-change-notification-control" />.</para>
|
</section>
|
|
<section xml:id="use-post-read-control">
|
<title>Post-Read Controls</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-pre-read-control">
|
<title>Pre-Read Controls</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-proxy-authz-control">
|
<title>Proxied Authorization Request Controls</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-server-side-sort-control">
|
<title>Server-Side Sort Controls</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-simple-paged-results-control">
|
<title>Simple Paged Results Control</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-subentry-request-control">
|
<title>Sub-entries Request Control</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-subtree-delete-control">
|
<title>Subtree Delete Request Control</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="use-vlv-control">
|
<title>Virtual List View Controls</title>
|
<para>TODO</para>
|
</section>
|
|
<section xml:id="custom-control">
|
<title>Custom Controls</title>
|
<para>TODO</para>
|
</section>
|
</chapter>
|