Working With ControlsControlsLDAPControlsThis chapter demonstrates how to use LDAP controls.For complete examples corresponding to the excerpts shown below, see
Controls.java, one of the OpenDJ LDAP SDK examples.About LDAP ControlsControls 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
request controls, and those sent by servers are termed
response controls.Determining Supported ControlsControlsSupportedLDAPChecking supported featuresFor OpenDJ, the controls supported are listed in the
Administration Guide appendix, LDAP
Controls. You can access the list of OIDs for
supported LDAP controls by reading the supportedControl
attribute of the root DSE.$ 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.9The following excerpt shows couple of methods to check whether the
directory server supports a control.
/**
* 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;
}
Assertion Request ControlControlsAssertionAssertionsThe LDAP assertion control 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.
if (isSupported(AssertionRequestControl.OID)) {
final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
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);
final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
try {
writer.writeEntry(connection.readEntry(dn, "description"));
writer.close();
} catch (final IOException e) {
// The writer could not write to System.out.
}
}
OpenDJ directory server supports the LDAP assertion control:dn: uid=bjensen,ou=People,dc=example,dc=com
description: Created using LDAP assertion controlAuthorization Identity ControlsControlsAuthorization IDAuthorizationsThe LDAP Authorization Identity Controls let you get the
authorization identity established when you bind to the directory server.
The following excerpt shows simple use of the controls.
if (isSupported(AuthorizationIdentityRequestControl.OID)) {
final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
final char[] pwd = "hifalutin".toCharArray();
System.out.println("Binding as " + dn);
final BindRequest request =
Requests.newSimpleBindRequest(dn, pwd)
.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) {
// Failed to decode the response control.
}
}
OpenDJ directory server supports the LDAP Authorization Identity
Controls:Binding as uid=bjensen,ou=People,dc=example,dc=com
Authorization ID returned: dn:uid=bjensen,ou=People,dc=example,dc=comEntry Change Notification Response ControlsControlsEntry change notificationSearchesEntry change notificationChange notificationWhen 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 persistent
searches for background information.
if (isSupported(PersistentSearchRequestControl.OID)) {
final 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));
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) {
// Failed to decode the response control.
} catch (final ErrorResultIOException e) {
// Request failed due to an IO problem.
} catch (final SearchResultReferenceIOException e) {
// Read a reference, rather than an entry.
}
}
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: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: -1In this case, Change number: -1 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.GetEffectiveRights Request ControlControlsGetEffectiveRightsAuthorizationsYour application can attach the GetEffectiveRights request control to
retrieve information about what the directory server permits a user to do.
Use this control during a search to see permissions on the entries returned.
See the Internet-Draft on the Access
Control Model for LDAP for background.
if (isSupported(GetEffectiveRightsRequestControl.OID)) {
final String authDN = "uid=kvaughan,ou=People,dc=example,dc=com";
final SearchRequest request =
Requests.newSearchRequest(
"dc=example,dc=com", SearchScope.WHOLE_SUBTREE,
"(uid=bjensen)", "cn", "aclRights", "aclRightsInfo")
.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) {
// Request failed due to an IO problem.
} catch (final SearchResultReferenceIOException e) {
// Read a reference, rather than an entry.
} catch (final IOException e) {
// The writer could not write to System.out.
}
}
OpenDJ SDK currently implements the request control, but not the
response control. The results are shown as values of the
aclRights and more verbose aclRightsInfo
attributes.
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)
ManageDsaIT Request ControlControlsManageDsaITReferralsThe ManageDsaIT control, described in RFC 3296, Named
Subordinate References in LDAP Directories, 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.
if (isSupported(ManageDsaITRequestControl.OID)) {
final String dn = "dc=ref,dc=com";
final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
try {
System.out.println("Referral without the ManageDsaIT control.");
SearchRequest request = Requests.newSearchRequest(dn,
SearchScope.SUBORDINATES, "(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) {
// Request failed due to an IO problem.
} catch (final SearchResultReferenceIOException e) {
// Read a reference, rather than an entry.
} catch (final IOException e) {
// The writer could not write to System.out.
}
}
OpenDJ directory server supports the ManageDsaIT Request Control. To use
the example entry create a new base DN, dc=ref,dc=com
before you import the data:Referral without the ManageDsaIT control.
Reference: [ldap:///dc=example,dc=com??sub?]
Referral with the ManageDsaIT control.
dn: dc=references,dc=ref,dc=comMatched Values Request ControlControlsMatched valuesGroupsRFC 3876, Returning Matched Values with the
LDAPv3, 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.Barbara Jensen's entry contains two common name values,
Barbara Jensen and Babs Jensen. The
following excerpt retrieves only the latter.
if (isSupported(MatchedValuesRequestControl.OID)) {
final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
final SearchRequest request =
Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
"(objectclass=*)", "cn")
.addControl(MatchedValuesRequestControl.newControl(
true, "(cn=Babs Jensen)"));
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) {
// The writer could not write to System.out.
}
}
OpenDJ directory server supports the matched values request
control.Reading entry with matched values request.
dn: uid=bjensen,ou=People,dc=example,dc=com
cn: Babs Jensen
Password Expired Response ControlControlsPassword expiredLDAPPassword policyA directory server can return the Password Expired Response Control,
described in the Internet-Draft Password Policy for LDAP Directories, 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.
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 (final 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 (final DecodeException de) {
// Failed to decode the response control.
}
}
}
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. See the OpenDJ
Administration Guide procedure explaining how
To Adjust the Default Password Policy for an example
of how to adjust the maximum password age.Password expired for uid=bjensen,ou=People,dc=example,dc=comPassword Expiring Response ControlControlsPassword expiringLDAPPassword policyThe Password Expiring Response Control, described in the Internet-Draft
Password Policy for LDAP
Directories, warns your application during a bind
that the password used will soon expire.
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 (final DecodeException de) {
// Failed to decode the response control.
}
}
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. See the OpenDJ
Administration Guide procedure explaining how
To Adjust the Default Password Policy for an example
of how to adjust the maximum password age. Also set a short
password-expiration-warning-interval value.Password for uid=bjensen,ou=People,dc=example,dc=com
expires in 237 seconds.Password Policy ControlsControlsPassword policyLDAPPassword policyThe Behera Internet-Draft, Password Policy for LDAP Directories, 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.
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 (final 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 (final DecodeException de) {
// Failed to decode the response control.
}
} catch (final DecodeException e) {
// Failed to decode the response control.
}
}
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. See the OpenDJ
Administration Guide procedure explaining how
To Adjust the Default Password Policy for an example
of how to adjust the maximum password age. Also set a short
password-expiration-warning-interval value.For a warning:Password policy warning timeBeforeExpiration, value 237 for
uid=bjensen,ou=People,dc=example,dc=comFor an error:Password policy error passwordExpired for
uid=bjensen,ou=People,dc=example,dc=comPermissive Modify Request ControlControlsPermissive modifyModificationsPermissive modifyMicrosoft defined a Permissive Modify Request Control that relaxes
some constraints when your application performs a modify operation and
tries to add an attribute that already exists, or to
delete an attribute that does not exist.
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 + ".");
}
OpenDJ directory server supports the Permissive Modify Request
Control:Permissive modify did not complain about attempt to add
uid: bjensen to uid=bjensen,ou=People,dc=example,dc=com.Persistent Search Request ControlControlsPersistent searchSearchesPersistent searchChange notificationSee .Post-Read ControlsControlsPost-readSearchesHandling resultsRFC 4527, LDAP Read Entry Controls,
describes the post-read controls that let your application get the content
of an entry immediately after modifications are applied.
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 (final DecodeException e) {
// Failed to decode the response control.
} catch (final IOException e) {
// The writer could not write to System.out.
}
}
OpenDJ directory server supports these controls:dn: uid=bjensen,ou=People,dc=example,dc=com
description: Using the PostReadRequestControlPre-Read ControlsControlsPre-readAssertionsRFC 4527, LDAP Read Entry Controls,
describes the pre-read controls that let your application get the content
of an entry immediately before modifications are applied.
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 (final DecodeException e) {
// Failed to decode the response control.
} catch (final IOException e) {
// The writer could not write to System.out.
}
}
OpenDJ directory server supports these controls:dn: uid=bjensen,ou=People,dc=example,dc=com
mail: bjensen@example.comProxied Authorization Request ControlsControlsProxied authorizationAuthorizationsProxied authorization provides a standard control as defined in
RFC
4370 (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.
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 (final IOException e) {
// The writer could not write to System.out.
}
}OpenDJ supports proxied authorization, and the example works with the
sample data:dn: uid=bjensen,ou=People,dc=example,dc=com
description: Done with proxied authzServer-Side Sort ControlsControlsServer-side sortSearchesServer-side sortBrowsingSortingThe server-side sort controls are described in RFC 2891, LDAP Control Extension for Server Side Sorting of Search
Results. If possible, sort on the client side instead to
reduce load on the server. If not, then you can request a server-side
sort.
static void useServerSideSortRequestControl(Connection connection)
throws ErrorResultException {
if (isSupported(ServerSideSortRequestControl.OID)) {
final SearchRequest request =
Requests.newSearchRequest("ou=People,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.");
} else {
System.out.println("# Entries not necessarily sorted");
}
} catch (final DecodeException e) {
// Failed to decode the response control.
}
} 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 (final IOException e) {
// The writer could not write to System.out.
}
return true;
}
@Override
public boolean handleReference(SearchResultReference reference) {
System.out.println("Got a reference: " + reference.toString());
return false;
}
}
OpenDJ directory server supports server-side sorting: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.Simple Paged Results ControlControlsSimple paged resultsSearchesSimple paged resultsBrowsingRFC 2696, LDAP Control Extension for Simple Paged Results
Manipulation, defines a control for simple paging of
search results that works with a cookie mechanism.
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 (final DecodeException e) {
// Failed to decode the response control.
}
++page;
} while (cookie.length() != 0);
}
OpenDJ directory server supports getting simple paged results:# 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
Subentries Request ControlControlsSubentriesLDAPSubentriesRFC 3672, Subentries in LDAP, describes
subentries and also the subentries request control. When you perform a search
without the control and visibility set to TRUE, subentries
are only visible in searches with
SearchScope.BASE_OBJECT.
if (isSupported(SubentriesRequestControl.OID)) {
final SearchRequest request =
Requests.newSearchRequest("dc=example,dc=com",
SearchScope.WHOLE_SUBTREE,
"cn=*Class of Service", "cn", "subtreeSpecification")
.addControl(SubentriesRequestControl.newControl(
true, true));
final ConnectionEntryReader reader = connection.search(request);
final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
try {
while (reader.hasNext()) {
if (reader.isEntry()) {
final SearchResultEntry entry = reader.readEntry();
writer.writeEntry(entry);
}
}
writer.close();
} catch (final ErrorResultIOException e) {
// Request failed due to an IO problem.
} catch (final SearchResultReferenceIOException e) {
// Read a reference, rather than an entry.
} catch (final IOException e) {
// The writer could not write to System.out.
}
}
OpenDJ directory server supports the control.dn: cn=Bronze Class of Service,dc=example,dc=com
cn: Bronze Class of Service
subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
bronze)" }
dn: cn=Silver Class of Service,dc=example,dc=com
cn: Silver Class of Service
subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
silver)" }
dn: cn=Gold Class of Service,dc=example,dc=com
cn: Gold Class of Service
subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
gold)" }
Subtree Delete Request ControlControlsSubtree deleteDeletesSubtree deleteThe subtree delete request control, described in the Internet-Draft
Tree Delete Control, lets
your application delete an entire branch of entries starting with the entry
you target for deletion.
if (isSupported(SubtreeDeleteRequestControl.OID)) {
final String dn = "ou=Apps,dc=example,dc=com";
final DeleteRequest request =
Requests.newDeleteRequest(dn)
.addControl(SubtreeDeleteRequestControl.newControl(true));
final Result result = connection.delete(request);
if (result.isSuccess()) {
System.out.println("Successfully deleted " + dn
+ " and all entries below.");
} else {
System.out.println("Result: " + result.getDiagnosticMessage());
}
}
OpenDJ directory server supports the subtree delete control:Successfully deleted ou=Apps,dc=example,dc=com and all entries below.Virtual List View ControlsControlsVirtual list viewSearchesVirtual list viewBrowsingSortingThe virtual list view controls are intended to be used by applications
that let users browse lists of directory entries. The Internet-Draft LDAP Extensions for Scrolling View Browsing of
Search Results describes the controls. The virtual list
view request control is used in conjunction with the server-side sort
control such that the subset of entries the directory server returns from
a search are a window into the full sorted list.
if (isSupported(VirtualListViewRequestControl.OID)) {
ByteString contextID = ByteString.empty();
// Add a window of 2 entries on either side of the first sn=Jensen entry.
final 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));
final SearchResultHandler resultHandler = new MySearchResultHandler();
final Result result = connection.search(request, resultHandler);
try {
final ServerSideSortResponseControl sssControl =
result.getControl(ServerSideSortResponseControl.DECODER,
new DecodeOptions());
if (sssControl != null && sssControl.getResult() == ResultCode.SUCCESS){
System.out.println("# Entries are sorted.");
} else {
System.out.println("# Entries not necessarily sorted");
}
final VirtualListViewResponseControl vlvControl =
result.getControl(VirtualListViewResponseControl.DECODER,
new DecodeOptions());
System.out.println("# Position in list: "
+ vlvControl.getTargetPosition() + "/"
+ vlvControl.getContentCount());
} catch (final DecodeException e) {
// Failed to decode the response control.
}
}
OpenDJ directory server supports the virtual list view controls.
In order to set up OpenDJ directory server to produce the following output
with the example code, use OpenDJ Control Panel > Manage Indexes > New
VLV Index... to set up a virtual list view index for people by last name,
using the filter (|(givenName=*)(sn=*)), and sorting first
by surname, sn, in ascending order, then by given name
also in ascending order.dn: uid=skellehe,ou=People,dc=example,dc=com
givenName: Sue
sn: Kelleher
dn: uid=ejohnson,ou=People,dc=example,dc=com
givenName: Emanuel
sn: Johnson
dn: uid=ajensen,ou=People,dc=example,dc=com
givenName: Allison
sn: Jensen
dn: uid=bjense2,ou=People,dc=example,dc=com
givenName: Bjorn
sn: Jensen
dn: uid=bjensen,ou=People,dc=example,dc=com
givenName: Barbara
sn: Jensen
# Entries are sorted.
# Position in list: 92/150Using a Generic ControlControlsGenericOpenDJ LDAP SDK supports many controls, but you might still need to
work with additional controls. If so, then in some cases you can use the
GenericControl class when adding the control to your
request.For example, the Microsoft LDAP Server Notification Control with OID
1.2.840.113556.1.4.528 can be used to register a change
notification request for a search on Microsoft Active Directory. You can use
a GenericControl.newControl() static method to add the
request control to your search.org.forgerock.opendj.examples.GetADChangeNotifications.javaWhen you run the search against Active Directory and then create,
update, and delete a new user, in this example
CN=New User,CN=Users,DC=ad,DC=example,DC=com, Active
Directory notifies you of changes to directory data.# Search result entry: CN=RID Set,CN=WIN2008R2641,OU=Domain Controllers,
DC=ad,DC=example,DC=com
dn: CN=RID Set,CN=WIN2008R2641,OU=Domain Controllers,DC=ad,DC=example,DC=com
objectClass: top
objectClass: rIDSet
objectGUID:: 178zQQic3EOoBOB1j2QVgQ==
uSNChanged: 12446
# Search result entry: CN=New User,CN=Users,DC=ad,DC=example,DC=com
dn: CN=New User,CN=Users,DC=ad,DC=example,DC=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
uSNChanged: 12753
# Search result entry: CN=New User,CN=Users,DC=ad,DC=example,DC=com
dn: CN=New User,CN=Users,DC=ad,DC=example,DC=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
uSNChanged: 12755
# Search result entry: CN=New User,CN=Users,DC=ad,DC=example,DC=com
dn: CN=New User,CN=Users,DC=ad,DC=example,DC=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
uSNChanged: 12757
# Search result entry: CN=New User,CN=Users,DC=ad,DC=example,DC=com
dn: CN=New User,CN=Users,DC=ad,DC=example,DC=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
uSNChanged: 12758
# Search result entry: CN=New User\0ADEL:3a3f71ed-5d82-4a14-807a-0c008b678d94,
# CN=Deleted Objects,DC=ad,DC=example,DC=com
dn: CN=New User\0ADEL:3a3f71ed-5d82-4a14-807a-0c008b678d94,CN=Deleted Objects,
DC=ad,DC=example,DC=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
isDeleted: TRUE
uSNChanged: 12759
The GenericControl class is useful with controls that
do not require you to encode complex request values, or decode complex
response values. If the control you want to you requires complex encoding
or decoding, you might have to implement
org.forgerock.opendj.ldap.controls.Control.