Discover ITDS extended operations with JNDI

Introduction to ITDS extended operations

This article introduces readers to the world of extended operations in IBM® Tivoli® Directory Server (ITDS). The article makes users aware of extended operations using examples in the Java™ Naming and Directory Interface (JNDI).

Prashant Srivastava (pk.srivastava@in.ibm.com), Systems Software Engineer, IBM Software Labs, India

Prashant SrivastavaPrashant K. Srivastava is a member of the Tivoli Directory Server Java Development team and the developer for ITDS JNDI Toolkit - library of extensions and controls for ITDS v6.1.



26 April 2007

LDAP

Lightweight Directory Access Protocol (LDAP) is the most widely used directory protocol today. It is powerful, rich in capabilities and highly flexible. It has massive scaling ability, supports distributed directories and provides excellent authentication, authorization, and auditing (AAA) support. In fact, LDAP is the de facto industry standard for directory services. The Tivoli offering of the LDAP server is IBM Tivoli Directory Server (ITDS). It is amongst the leading products in identity management middleware and is a part of many of Tivoli middleware offerings.

A request by a client to the LDAP server is called an "operation". Operations specified by LDAP v3 protocol include

  • bind - new connection
  • unbind - end connection
  • search - search the directory
  • compare - compare entries on the server
  • add - add an entry
  • modify - modify an entry
  • delete - delete an entry
  • modify RDN/DN - rename an entry/change its parent
  • abandon - abandon an outstanding operation
  • startTLS - start a TLS session
  • extended operations

What are extended operations?

Extended operations (exops) is a mechanism that allows additional operations that are not defined in the protocol to be supported for services that LDAP v3 servers provide. Exops provide a server the extensibility in terms of operations that it can support. RFC 2251 describes the LDAP v3 protocol and the provision for exops. It does not define the kind of exops a server can or should support. All additional operations that a server supports are to be sent by the client as an extended operation. The exops a server supports is vendor dependent, LDAP v3 protocol does not mandate any particular exop to be supported. Though some of the more popular ones have been standardized, many are proprietary to vendors. Consequently, many of the exops are not portable, that is, a client is bound to a particular server if it uses its proprietary exop.

ITDS fully implements the LDAP v3 standard, it supports a variety of exops to perform otherwise tedious operations. These include finding the account status of a user, finding the groups which a user belongs to, and reading server logs. Most of the operations are proprietary and work exclusively on ITDS and are not supported by other vendors.

RFC 2251

RFC 2251 describes LDAP v3 protocol and extended operations." Lightweight Directory Access Protocol (v3) ".

How it works

An extended operation is invoked by a client by sending an extended request and receiving an extended response in response from the server.

Broadly the sequence of events is:

  1. A client sends an extended request to the server.
  2. If the server recognizes the request, it performs the operation
  3. An extended response is sent back with the result, if any, of the operation

ASN.1

ASN.1 is a notation for describing data structures. ASN.1 and BER.

Structure

An extended request is made of two parts:

  1. requestName
    requestName is a globally unique dotted-decimal representation of the OID (Object Identifier) that identifies the request. The OID namespace is hierarchically divided; every authority that can define an OID is assigned a prefix which it uses to identify its OIDs, for example, all ITDS exops start with 1.3.18.0.2.12 .
  2. requestValue
    Data needed to execute the request. The format of the data is predefined for every exop. Some exops do not require any data to be associated with the request.

The extended request is encoded before being sent to the server. ITDS expects its extended request to be ASN.1 BER encoded.

The structure of the extended response is similar to extended response in that it can contain the OID and value, both of which are optional, and a field describing the result code of the operation.
An extended response is made of three parts:

  1. resultcode
    Result code of the operation. One of the defined LDAP error codes, for example, LDAP_SUCCESS, LDAP_OPERATIONS_ERROR, and so forth
  2. responseName
    responseName is OID of the response. It might or might not be same as the request OID.
  3. responseValue
    Result of the operation, if any.

The response from the server is encoded (ASN.1 BER) and should be decoded by the client when received.


How do I know if an exop is supported?

RootDSE

RootDSE (Root DSA-Specific Entry) - the root entry of an LDAP v3 server. It contains information about configuration and capabilities of the server.

DSA is a legacy term for a directory server in X.500 parlance.

A RootDSE search can be fired by specifying the search base as null and scope as base .
JNDI example:
Attributes results = context.getAttributes("");

Different vendors support different exops. Support may even vary between versions from the same vendor. If an unsupported exop is requested by a client, the server sends back an LDAP error code 52 - unsupported operation message. If the request is malformed, the LDAP error code 2 - protocol error is sent.

The server advertises the supported extensions through the supportedExtension attribute in the RootDSE. The attribute values are OIDs for the supported extended operations.

RootDSE search for supportedextension attribute
                #ldapsearch -h lnike -p 4389 -D cn=root -w root -s base objectclass=* supportedextension

                supportedextension=1.3.18.0.2.12.1 
                supportedextension=1.3.18.0.2.12.3
                supportedextension=1.3.18.0.2.12.5 
                supportedextension=1.3.18.0.2.12.6
                supportedextension=1.3.18.0.2.12.64 
                supportedextension=1.3.18.0.2.12.15
                supportedextension=1.3.18.0.2.12.16 
                supportedextension=1.3.18.0.2.12.17
                supportedextension=1.3.18.0.2.12.19 
                supportedextension=1.3.18.0.2.12.54
                supportedextension=1.3.18.0.2.12.56 
                supportedextension=2.16.840.1.113730.3.4.3
                supportedextension=1.3.18.0.2.12.44 
                supportedextension=1.3.18.0.2.12.50 
                . 
                . 
                .

Further, some extensions might be disabled and so cannot be used. The list of enabled extensions (among other capabilities) can be fetched using the operational attribute ibm-enabledCapabilities in the root DSE. An operational attribute needs to be explicitly requested during a search.

Root DSE search for ibm-enabledcapabilities operational attribute

Click to see code listing

Root DSE search for ibm-enabledcapabilities operational attribute

                #ldapsearch -h lnike -p 4389 -D cn=root -w root -b "" -s base objectclass=* ibm-enabledcapabilities

                ibm-enabledcapabilities=1.3.18.0.2.32.1 
                ibm-enabledcapabilities=1.3.18.0.2.32.2
                ibm-enabledcapabilities=1.3.18.0.2.32.3 
                ibm-enabledcapabilities=1.3.18.0.2.32.4
                ibm-enabledcapabilities=1.3.18.0.2.32.5 
                ibm-enabledcapabilities=1.3.18.0.2.32.6
                ibm-enabledcapabilities=1.3.18.0.2.32.8 
                ibm-enabledcapabilities=1.3.18.0.2.32.9
                ibm-enabledcapabilities=1.3.18.0.2.32.15 
                ibm-enabledcapabilities=1.3.18.0.2.32.10
                ibm-enabledcapabilities=1.3.18.0.2.32.17 
                . 
                . 
                .

The OIDs of enabled capabilities are returned. Capabilities include not only exops but control and other server capabilities too.

Below is a list of some of the exops ITDS 6.0 supports:

Table 1. ITDS 6.0 Extended Operations
Extended OperationDescription
Account statusGets the account status of a user
Attribute types Gets the attributes by supported capability: operational , language tag , attribute cache , unique or configuration .
Begin transactionBegins a transaction context
Cascading replication operationCascades the replication information to all the servers consumers
Clear logClears the specified server log
Control replicationForce, suspend, resume replication
Control queueSkips changes in the replication queue
DN normalizationNormalizes a sequence of DNs
Dynamic server traceStarts or stops server tracing
Dynamic update requestsUpdates the server configuration
End transactionEnds the given transaction context
Event notification register requestRegisters for an event on the server
Event notification unregister requestUnregisters for events that were registered for
Get linesGets the specified lines of the specified server log
Get number of linesGets the total number of lines of the specified server log
Group evaluationGets all the groups that the user belongs to
Kill connectionRequest to kill all or some specific connections to the server
LDAP trace facilityControls server tracing
Quiesce or unquiesce replication contextQuiesces or Unquiesces replication subtrees
Replication error logControls the server error log
Replication topologyTriggers replication of the given replication context
Start, stop serversRemotely starts/stops server
Start TLSInitiates TLS session
Unique attributesGets the list of all non-unique values for an attribute
Update configurationUpdate the configuration of the server
User typeGets the type and roles of the bound user

Extended operations and JNDI

JNDI inherently supports extended operations since Java 1.3. We'll look at the classes involved in creating an extended request and response and firing it for ITDS using a simple example. The interface javax.naming.ldap.ExtendedRequest describes an exop request.

As described earlier, OID and value are two properties for an exop request. getID() and getEncodedValue() are the defined methods for the two.

Interface javax.naming.ldap.ExtendedResponse describes an exop response. It contains the same methods as ExtendedRequest . What happens to the result code we talked about earlier? JNDI intercepts the response and if it is something other that LDAP_SUCCESS , throws a NamingException citing the error.

LDAP operations are performed in JNDI through the interface LDAPContext . LDAPContext#extendedOperation() is the method used to send an exop request to the server. It accepts an instance of ExtendedRequest and returns an instance of type ExtendedResponse .


Building a sample extended request and response

Let's build a request and response classes for an extended operation for ITDS 6.0. This particular exop gets the account status of a directory user.

The exop request for account status extended request is defined in ASN notation as:
SEQUENCE {
userDN OCTETSTRING
}

The structure says that the request value contains a single element userDN .
userDN : DN of the user, the status of whose account is needed.

Account status extended request
                import javax.naming.NamingException;
                import javax.naming.ldap.ExtendedRequest;
                import javax.naming.ldap.ExtendedResponse;

                import com.ibm.ldap.bp.asn1.ASN1Exception;
                import com.ibm.ldap.bp.asn1.BEREncoder;


                public class AccountStatusRequest implements ExtendedRequest {

                    
                    public static final String OID = "1.3.18.0.2.12.58";
                    
                    private final String userDN;

                    /** 
                     * Creates an account status exnteded operation request 
                     * @param userDN non-null DN of the user
                     *
                     */ 
                    public AccountStatusRequest(String userDN) { 
                        this.userDN = userDN; 
                    }
    
                    //creates response method creates the response to this request. 
                    //This is what is returned by LDAPContext#extendedOperation()
                    @override
                    public ExtendedResponse createExtendedResponse(String id, byte[] 
                    berValue, int offset, int length) throws NamingException { 
                        AccountStatusResponse response = new 
                        AccountStatusResponse(berValue, offset, length); 
                        return response; 
                    }
                        
                    //gets the encoded value for the request. 
                    @override
                    public byte[] getEncodedValue() {
                        byte[] berValue = null; 
                        try {
        
                        //We'll use IBM java BER encoder for encoding. 
                        //the library is shipped with ITDS  (IBMJavaBer.jar in 
                        <ITDS_HOME/javalib>)
                            BEREncoder encoder = new BEREncoder(); 
                            int seq_nr = encoder.encodeSequence();
                            encoder.encodeOctetString(this.userDN.getBytes("utf-8")); 
                            encoder.endOf(seq_nr);
                            berValue = encoder.toByteArray(); 
                        } catch (ASN1Exception e) {
                            e.printStackTrace(); } 
                        catch (UnsupportedEncodingException e) {
                            e.printStackTrace(); 
                        } 
                        return berValue; 
                    }
                    
    
                    //returns the OID. 
                    @override
                    public String getID() { 
                        return AccountStatusRequest.OID; 
                    } 
                }

AccountStatusRequest implements the interface ExtendedRequest . Encoded data from getEncodedValue() is sent to the server along with the OID from getID() . When a response is received, the response value is passed to createExtendedResponse() to get the response to this request. This is the object that is returned to the client when LDAPContext#extendedOperation() returns.

Now lets create the extended response to the request.

The exop request for account status extended response is defined in ASN notation as:
SEQUENCE {
responseVAlue ::= SEQUENCE {
status ENUMERATION
}
status::= ENUMERATED {
ACCOUNT_OPEN (0),
ACCOUNT_LOCKED (1),
ACCOUNT_EXPIRED (2)
}

The structure tells us that the response contains a single element status and it can take one of the three enumaerated values.
status : account status. One of
ACCOUNT_OPEN
,
ACCOUNT_LOCKED
and
ACCOUNT_EXPIRED
.

Account status extended response
                import javax.naming.ldap.ExtendedResponse;

                import com.ibm.ldap.bp.asn1.ASN1Exception;
                import com.ibm.ldap.bp.exception.BPException;
                import com.ibm.ldap.bp.util.BERUtils


                public class AccountStatusResponse implements ExtendedResponse {

                    public static final int ACCOUNT_OPEN = 0; 
                    public static final int ACCOUNT_LOCKED = 1; 
                    public static final int ACCOUNT_EXPIRED = 2;
    
                    public static final String OID = "1.3.18.0.2.12.59";
    
                    private int acc_status;
    
                    private byte[] berValue;
    
                    
                    AccountStatusResponse(final byte[] berValue, final int offset, 
                    final int length) { 
                        this.berValue = new byte[length]; 
                        
                        System.arraycopy(berValue, offset, this.berValue, 0, length);
    
                        //We'll use IBM java BER decoder for decoding. 
                        BERDecoder decoder = new BERDecoder(this.berValue);
    
                        int id = decoder.decodeSequenceOf();
    
                        //get the single enumeration in the structure 
                        this.acc_status = decoder.decodeEnumeration();
    
                        decoder.endOf(id); 
                    }
                    
                    /** 
                     * Gets the account status of the user DN for which this extended 
                     operation 
                     * was fired 
                     * 
                     * @return one of {ACCOUNT_OPEN|ACCOUNT_EXPIRED|ACCOUNT_LOCKED} 
                     */
                    public int getAccountStatus() {
                        return this.acc_status; 
                    }
                    
                    
                    @override
                    public byte[] getEncodedValue() { 
                        return this.berValue; 
                    }
                    
                    @override
                    public String getID() { 
                        return AccountStatusResponse.OID; 
                    } 
                }

AccountStatusResponse implements the interface ExtendedRequest . An instance of the response is created by its associated request object. Our response object gets the client the account status through the method getAccountStatus(). An extendedResponse might not always have a response value (for example, Clear Log extended operation). Such a response object would return only the OID and null response value. It should be noted that AccountStatusResponse has a different OID than its associated request AccountStatusRequest. This might not always be the case, request and response can very well share the same OID (for example, Normalize DN extended operation).

That is all there is to creating extended requests and responses. The example taken here was simple, exops with more complex BER structures exist. Complex structures require a better understanding of encoding and decoding BER structures. Please refer the IBM Java BER decoder documentation for details.


Conclusion

In this article an introduction to extended operations and an example of supported extension were provided. ITDS v6.0 supports a number of useful extensions for tasks related to logs, replication, effective password policy and such. A link to the list can be found in the Resources section.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Tivoli (service management) on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Tivoli (service management), Tivoli
ArticleID=199941
ArticleTitle=Discover ITDS extended operations with JNDI
publish-date=04262007