blob: 4fe8e3d89d2dba61322157de5d3a2003b89ab029 [file] [log] [blame]
Title: 6.7 - Control
NavPrev: 6.6-csn.html
NavPrevText: 6.6 - Csn
NavUp: 6-ldap-data-structures.html
NavUpText: 6 - LDAP data structures
NavNext: 6.8-cursor.html
NavNextText: 6.8 - Cursor
Notice: Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
# 6.7 - Control (...)
A *LDAP* *Control* is an extension to an operation. It tells the server to do something aside the standard operation, or it let the server send back some information to the client. A *Control* contains three different parts :
* An identifier, the control *OID*
* A flag telling the server what to do if it does not know about the control or if it results in an error (either return an error or ignore the control)
* A value which is generally *BER* encoded
There are many controls available, some being standardized, other being server specific.
One or more control can be added to any operation.
Note that either the client or the server might not know about the controls being used, and if the _criticality_ flag is set to *FALSE*, the server will ignore the control in this case.
## Managed controls
Here is the list of *control*s the LDAP API currently know about. The following short names are used to name the LDAP Servers that support each control.
* AP : ApacheDS
* OL : OpenLDAP
* AD : Active Directory
* OS : OpenDS
* OJ : OpenDJ
* UI : UnboundID
* ID : IMB DS
* All : all the servers
The C/S column indicate if the control is sent to the server (S), or back to the client (C) or can be used both ways (C/S).
| Name | OID | Description | C/S | RFC | LDAP Servers |
|---|---|---|---|---|---|
| [AdDirSync](#addirsync) | 1.2.840.113556.1.4.841 | Microsoft LDAP Control for Directory Synchronization | C/S | [DIRSync draft](http://tools.ietf.org/html/draft-armijo-ldap-dirsync-01) | AD |
| [AdPolicyHints](#adpolicyhints) | 1.2.840.113556.1.4.223 | Enforces the password history length constraint during password set | S | [LDAP_SERVER_POLICY_HINTS_OID](https://msdn.microsoft.com/en-us/library/hh128228.aspx) | AD |
| [AdShowDeleted](#adshowdeleted) | 1.2.840.113556.1.4.417 | DirSync search returns deleted entries | C | [LDAP_SERVER_SHOW_DELETED_OID](https://msdn.microsoft.com/en-us/library/cc223326.aspx) | AD |
| [Cascade](#cascade) | 1.3.6.1.4.1.18060.0.0.1 | Used to ask the server to delete an entry and all its descendants | S | None | AP |
| [ChangeNotifications](#changenotifications) | 1.2.840.113556.1.4.528 | Registers the client to be notified when changes are made to an object in Active Directory. | S | [LDAP_SERVER_NOTIFICATION_OID](https://msdn.microsoft.com/en-us/library/cc223353.aspx) | AD |
| [EntryChange](#entrychange) | 2.16.840.1.113730.3.4.7 | Provides a simple mechanism by which an LDAP client can receive notification of changes that occur in an LDAP server | C | [Persistent Search: A Simple LDAP Change Notification Mechanism](https://www.ietf.org/proceedings/51/I-D/draft-ietf-ldapext-psearch-03.txt) | AP |
| [ManageDSAIT](#managedsait) | 2.16.840.1.113730.3.4.2 | Allows access to management objects as standard objects | S | [RFC 3296](https://www.ietf.org/rfc/rfc3296.txt) | AP/OL |
| [PagedResults](#pagedresults) | 1.2.840.113556.1.4.319 | A request/response control used to implement a simple paging of search results | C/S | [RFC 2696](http://www.faqs.org/rfcs/rfc2696.html) | All |
| [PasswordPolicy](#passwordpolicy) | 1.3.6.1.4.1.42.2.27.8.5.1 | The password policy control | C/S | [PasswordPolicy draft](https://tools.ietf.org/html/draft-behera-ldap-password-policy-10) | AP/OL |
| [PermissiveModify](#permissivemodify) | 1.2.840.113556.1.4.1413 | Allows an LDAP modify to work under less restrictive conditions | S | [LDAP_SERVER_PERMISSIVE_MODIFY_OID](https://msdn.microsoft.com/en-us/library/cc223352.aspx) | AD |
| [PersistentSearch](#persistentsearch) | 2.16.840.1.113730.3.4.3 | Provides a simple mechanism by which an LDAP client can receive notification of changes that occur in an LDAP server | S | [Persistent Search](https://tools.ietf.org/html/draft-ietf-ldapext-psearch-03) | AP/OL |
| [ProxiedAuthz](#proxiedauthz) | 2.16.840.1.113730.3.4.18 | Defines the Proxy Authorization request | S | [RFC 4370](ttps://tools.ietf.org/html/rfc4370) | AP/OL |
| [SortRequest](#sortrequest) | 1.2.840.113556.1.4.473 | Server Side Sort request control | S | [RFC 2891](http://tools.ietf.org/html/rfc2891) | AP/OL |
| [SortResponse](#sortresponse) | 1.2.840.113556.1.4.474 | Server Side Sort request control | C | [RFC 2891](http://tools.ietf.org/html/rfc2891) | AP/OL |
| [Subentries](#subentries) | 1.3.6.1.4.1.4203.1.10.1 | Controls the visibility of entries and subentries | S | [RFC 3672](http://tools.ietf.org/html/rfc3672) | AP/OL |
| [SyncDoneValue](#syncdonevalue) | 1.3.6.1.4.1.4203.1.9.1.3 | Control sent when replication has been completed. It contains a cookie. | C | [RFC 4533](https://tools.ietf.org/html/rfc4533) | AP/OL |
| [SyncRequestValue](#syncrequestvalue) | 1.3.6.1.4.1.4203.1.9.1.1 | Controls the syncrepl process | S | [RFC 4533](https://tools.ietf.org/html/rfc4533) | AP/OL |
| [SyncStateValue](#syncstatevalue) | 1.3.6.1.4.1.4203.1.9.1.2 | Gives the syncrepl state | C | [RFC 4533](https://tools.ietf.org/html/rfc4533) | AP/OL |
| [VirtualListViewRequest](#virtuallistviewrequest) | 2.16.840.1.113730.3.4.9 | Sent to the server to request a subset of results | S | [Scrolling View Browsing of Search Results](https://tools.ietf.org/html/draft-ietf-ldapext-ldapv3-vlv-09) | AP |
| [VirtualListViewResponse](#virtuallistviewresponse) | 2.16.840.1.113730.3.4.10 | Sent back to the client to give the search current status | C | [Scrolling View Browsing of Search Results](https://tools.ietf.org/html/draft-ietf-ldapext-ldapv3-vlv-09) | AP |
## How to use controls
It's quite simple. You just have to instanciate the control you want to use, set its value, and add it to the request you will send to the server. Here is an example with the *ManageDsaIT* control :
:::Java
AddRequest addRequest = new AddRequestImpl(); // Create the request
ManageDsaIT manageDSAIT = new ManageDsaITImpl(); // Instanciate the control
manageDSAIT.setCritical( true ); // Set a value
addRequest.addControl( manageDSAIT ); // Add the control to the request
...
and that's it !
Note that you have to create an instance of teh operation you want to send to the server, if yu want to add a control to it.
On the client side, you may want to check if there is a control and read it if so. This is a bit more complex, because you need to know which kind of control you are expecting. We will see with a more complex control, the *Paged Search* control (which allows the user to get a specific number of enries at each call). Here, we will fetch 4 entries in one go, until all the entries have been read, and as we have 10 entries to read, we will send 3 *SearchRequest*, teh first two will return 4 entries and the last one only 2.
:::Java
try ( LdapConnection connection = new LdapNetworkConnection( "MyServer", 389 ) )
{
connection.bind( "cn=user,ou=system", "secret" );
// Create the control, and tell it we want 4 entries for every call
PagedResults pagedControl = new PagedResultsImpl();
pagedControl.setSize( 4 );
// Read all the elements
List<Entry> results = new ArrayList<>();
// Create the SearchRequest
SearchRequest searchRequest = new SearchRequestImpl();
searchRequest.setBase( new Dn( "dc=users,ou=system" ) );
searchRequest.setFilter( "(cn=*)" );
searchRequest.setScope( SearchScope.SUBTREE );
while ( true )
{
// Add the PagedSearch control to the SearchRequest
searchRequest.addControl( pagedControl );
// Do the search now
try ( SearchCursor cursor = connection.search( searchRequest ) )
{
// Loop on all the entries we got back (Should be 4, or less)
while ( cursor.next() )
{
Entry result = cursor.getEntry();
results.add( result );
}
// Check if we have reached the size limit
if ( cursor.getSearchResultDone().getLdapResult().getResultCode() == ResultCodeEnum.SIZE_LIMIT_EXCEEDED )
{
break;
}
// Now check the returned controls
Map<String, Control> controls = cursor.getSearchResultDone().getControls();
// We should get a PagedResult response
PagedResults responseControl = ( PagedResults ) controls.get( PagedResults.OID );
if ( responseControl != null )
{
// check if this is over, ie the cookie is empty
byte[] cookie = responseControl.getCookie();
if ( Strings.isEmpty( cookie ) )
{
// Ok, we are done
break;
}
// Prepare the next iteration, sending a bad cookie
pagedControl.setCookie( cookie );
}
else
{
break;
}
}
}
// At this point, we should have read 10 entries
}
This sounds like a bit complex, but actually, this control is complex. What we are interested in is the way we get the control returned by the server. In this case, the control we are interested in is attached to the *SearchResultDone* response, which is the last result we have when reading the cursor. As we may have more than one control, the response conains a _Map_ of controls, ot of which we should be able to retreive the *PagedSearch* control from its *OID*. This is what does this piece of code :
:::Java
...
// We should get a PagedResult response
PagedResults responseControl = ( PagedResults ) controls.get( PagedResults.OID );
...
If it's not null, we can proceed with the control.
Side note : in this piece of code, we don't close the connection nor the cursor, because they are *Closeable* : They will be close when we exit the _try_ scope. This is a feature added in *Java 7*, called *try with resources*
## Managed Controls detail
### AdDirSync
A control used to initiate a synchronization with an *Active Directory* server, and get back the results. Check [Microsoft LDAP Control for Directory Synchronization](https://tools.ietf.org/html/draft-armijo-ldap-dirsync-01) for a better understanding on how to use this control.
* OID : *1.2.840.113556.1.4.841*
* Criticality : TRUE
* ASN.1 description :
Sent to the server :
:::Text
realReplControlValue ::= SEQUENCE {
flags integer
maxBytes integer
cookie OCTET STRING
}
This control is only valid when send with a *SearchRequest*.
Received from the server :
:::Text
realReplControlValue ::= SEQUENCE {
flag integer
maxReturnLength integer
cookie OCTET STRING
}
The cookie read from this control has to be injected in the control sent to the server for the next search.
### AdPolicyHints
* OID : 1.2.840.113556.1.4.223
### AdShowDeleted
* OID : 1.2.840.113556.1.4.417
### Cascade
* OID : 1.3.6.1.4.1.18060.0.0.1
### ChangeNotifications
* OID : 1.2.840.113556.1.4.528
### EntryChange
* OID : 2.16.840.1.113730.3.4.7
### ManageDSAIT
* OID : *2.16.840.1.113730.3.4.2*
This is oe of the simplest control : it has no value, and he criticality flag may be absent.
It's used to get access to special objects, like *referrals* or *subentries* as normal objects. Let's see what that means for *referrals*.
A *Referral* is a special object which once returned to the client, will automatically will redirect the client to another part of teh *DIT* (or to another *LDAP* server). So to speak, the end user does not see the *referral*, but the reffered entry. The question is : how do we set a *referral*, or how do we modify it ? We use the *ManageDsaIT* control.
Let say we have such an entry, which is actually a referal :
:::Text
dn: uid=entryRef,ou=users,dc=acme,dc=com
objectClass: extensibleObject
objectClass: referral
objectClass: top
uid: entryref"
ref: ldap://localhost:10389/uid=entry,ou=users,dc=acme,dc=com
ref: ldap://trust:10389/uid=entry,ou=users,dc=trust,dc=com
A client searchingg for _uid=entryRef,ou=users,dc=acme,dc=com_ will be redirected to _uid=entry,ou=users,dc=acme,dc=com_ on the local server or on _uid=entry,ou=users,dc=trust,dc=com_ on the *trust* server (it's up to the client to decide which entry to fetch).
If we want to modify this referral, we need to add the *ManageDsaIT* control :
:::Java
try ( LdapConnection connection = new LdapNetworkConnection( "MyServer", 389 ) )
{
connection.bind( "cn=user,ou=system", "secret" );
// modify the referral
ModifyRequest modifyRequest = new ModifyRequestImpl();
modifyRequest.setName( new Dn( "uid=entryRef,ou=users,dc=acme,dc=com" ) );
modifyRequest.add( "ref", "ldap://subsidiary:10389/uid=entry,ou=users,dc=subsidiary,dc=com" );
// Add the control
modifyRequest.addControl( new ManageDsaITImpl() );
// And apply the modification
connection.modify( modifyRequest );
}
Now, if we fetch the entry (still using the control), it will looks like :
:::Text
dn: uid=entryRef,ou=users,dc=acme,dc=com
objectClass: extensibleObject
objectClass: referral
objectClass: top
uid: entryref"
ref: ldap://localhost:10389/uid=entry,ou=users,dc=acme,dc=com
ref: ldap://trust:10389/uid=entry,ou=users,dc=trust,dc=com
ref: ldap://subsidiary:10389/uid=entry,ou=users,dc=subsidiary,dc=com
As you can see, a third *ref* value has been added.
### PagedResults
* OID : *1.2.840.113556.1.4.319*
### PasswordPolicy
* OID : 1.3.6.1.4.1.42.2.27.8.5.1
### PermissiveModify
* OID : 1.2.840.113556.1.4.1413
### PersistentSearch
* OID : 2.16.840.1.113730.3.4.3
### ProxiedAuthz
* OID : 2.16.840.1.113730.3.4.18
### SortRequest
* OID : 1.2.840.113556.1.4.473
### SortResponse
* OID : 1.2.840.113556.1.4.474
### Subentries
* OID : 1.3.6.1.4.1.4203.1.10.1
### SyncDoneValue
* OID : 1.3.6.1.4.1.4203.1.9.1.3
### SyncRequestValue
* OID : 1.3.6.1.4.1.4203.1.9.1.1
### SyncStateValue
* OID : 1.3.6.1.4.1.4203.1.9.1.2
### VirtualListViewRequest
* OID : 2.16.840.1.113730.3.4.9
### VirtualListViewResponse
* OID : 2.16.840.1.113730.3.4.10