package com.idega.core.ldap.client.jndi;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.Properties;
/**
* <p>JNDI Ops is a bare-bones utility class that takes up some
* of the over-head of making jndi calls. It is used by
* BasicOps which adds validation, error handling and logging.<p>
*
* <p>This utility class assumes you will be using ldap v3 with
* the environment defaults:
* "java.naming.ldap.deleteRDN" = "false"
* Context.REFERRAL (java.naming.referral), "follow");
* java.naming.ldap.attributes.binary", "photo jpegphoto jpegPhoto");
* java.naming.ldap.derefAliases", "finding");
*
*/
public class JNDIOps
{
private static final String DEFAULT_CTX = "com.sun.jndi.ldap.LdapCtxFactory";
// we may wish to extend this class to opening DSML connections in future.
//private static final String DEFAULT_DSML_CTX = "com.sun.jndi.dsmlv2.soap.DsmlSoapCtxFactory";
protected DirContext ctx = null;
protected static int ldapVersion = -1; // ldap version of the current connection (-1 = not connected)
/**
* Initialise a Basic Operation object with a context.
*/
public JNDIOps(DirContext c)
{
this.ctx = c;
}
/**
* This creates a jndi connection with a particular set of environment properties.
* Often this will be obtained by using one of the set...Properties methods to
* create a base list of properties and then modifing it before calling this
* constructor.
*/
public JNDIOps(Properties env)
throws NamingException
{
this.ctx = openContext(env); // create the connection!
}
/**
* This creates a simple, unauthenticated jndi connection to an ldap url.
* Note that this ftn may take some time to return...
*
* @param url a url of the form ldap://hostname:portnumber.
*/
public JNDIOps(String url)
throws NamingException
{
Properties env = new Properties(); // an environment for jndi context parameters
setupBasicProperties(env, url); // set up the bare minimum parameters
this.ctx = openContext(env); // create the connection!
}
/**
* <p>This creates a JNDIOps object using simple username + password authentication.</p>
*
* <p>This constructor opens an initial context. Note that this ftn may take some
* time to return...</p>
*
* @param url a url of the form ldap://hostname:portnumber.
* @param userDN the Manager User's distinguished name (optionally null if not used).
* @param pwd the Manager User's password - (is null if user is not manager).
*/
public JNDIOps (String url, String userDN, char[] pwd)
throws NamingException
{
Properties env = new Properties(); // an environment for jndi context parameters
setupBasicProperties(env, url); // set up the bare minimum parameters
setupSimpleSecurityProperties(env, userDN, pwd); // add the username + password parameters
this.ctx = openContext(env); // create the connection !
}
/**
* This creates a JNDIOps object with an SSL or SASL Connection.
*
* If only SSL is desired, the clientcerts, clientKeystorePwd and clientKeystoreType
* variables may be set to null.
*
* @param url a url of the form ldap://hostname:portnumber.
* @param cacerts the file containing the trusted server certificates (no keys).
* @param clientcerts the file containing client certificates.
* @param caKeystorePwd the password to the ca's keystore (may be null for non-client authenticated ssl).
* @param clientKeystorePwd the password to the client's keystore (may be null for non-client authenticated ssl).
* @param caKeystoreType the type of keystore file; e.g. 'JKS', or 'PKCS12'.
* @param clientKeystoreType the type of keystore file; e.g. 'JKS', or 'PKCS12'.
* @param tracing whether to set BER tracing on or not.
* @param sslTracing whether to set SSL tracing on or not.
*/
public JNDIOps(String url,
String cacerts, String clientcerts,
char[] caKeystorePwd, char[] clientKeystorePwd,
String caKeystoreType, String clientKeystoreType,
boolean tracing, boolean sslTracing)
throws Exception
{
Properties env = new Properties(); // an environment for jndi context parameters
setupBasicProperties(env, url); // set up the bare minimum parameters
setupSSLProperties(env, cacerts, // add the SSL ('ca...') and possible SASL ('client...') parameters
clientcerts, caKeystorePwd, clientKeystorePwd, caKeystoreType, clientKeystoreType, tracing, sslTracing);
this.ctx = openContext(env); // create the connection !
}
/**
* This sets the basic environment properties needed for a simple,
* unauthenticated jndi connection. It is used by openBasicContext().
*
* This method is provided as a convenience for people wishing to append
* or modify the jndi environment, without setting it up entirely from
* scratch.
*
* @param url
* @param env
* @throws NamingException
*/
public static void setupBasicProperties(Properties env, String url) throws NamingException
{
// sanity check
if (url == null) {
throw new NamingException("URL not specified in openContext()!");
}
env.put("java.naming.ldap.version", "3"); // always use ldap v3
env.put(Context.INITIAL_CONTEXT_FACTORY, DEFAULT_CTX); // use jndi provider
env.put("java.naming.ldap.deleteRDN", "false"); // usually what we want
env.put(Context.REFERRAL, "follow"); //could be: follow, ignore, throw
env.put("java.naming.ldap.attributes.binary", "photo jpegphoto jpegPhoto"); // special hack to handle non-standard binary atts
env.put("java.naming.ldap.derefAliases", "finding"); // could be: finding, searching, etc.
env.put(Context.SECURITY_AUTHENTICATION, "none"); // no authentication (may be modified by other code)
env.put(Context.PROVIDER_URL, url); // the ldap url to connect to; e.g. "ldap://ca.com:389"
}
/**
* This sets the environment properties needed for a simple username +
* password authenticated jndi connection. It is used by openSimpleSecurityContext().
*
* This method is provided as a convenience for people wishing to append
* or modify the jndi environment, without setting it up entirely from
* scratch.
*
* @param env
* @param userDN
* @param pwd
*/
public static void setupSimpleSecurityProperties(Properties env, String userDN, char[] pwd)
{
env.put(Context.SECURITY_AUTHENTICATION, "simple"); // 'simple' = username + password
env.put(Context.SECURITY_PRINCIPAL, userDN); // add the full user dn
env.put(Context.SECURITY_CREDENTIALS, new String(pwd)); // stupid jndi requires us to cast this to a string-
// this opens a security weakness with swapped memory etc.
}
/* This static ftn. sets the environment used to open an SSL or SASL context.
* It is used by openSSLContext.
*
* If only SSL is desired, the clientcerts, clientKeystorePwd and clientKeystoreType
* variables may be set to null.
*
* This method is provided as a convenience for people wishing to append
* or modify the jndi environment, without setting it up entirely from
* scratch.
*
* @param url a url of the form ldap://hostname:portnumber.
* @param tracing whether to set BER tracing on or not.
* @param cacerts the file containing the trusted server certificates (no keys).
* @param clientcerts the file containing client certificates.
* @param caKeystorePwd the password to the ca's keystore (may be null for non-client authenticated ssl).
* @param clientKeystorePwd the password to the client's keystore (may be null for non-client authenticated ssl).
* @param caKeystoreType the type of keystore file; e.g. 'JKS', or 'PKCS12'.
* @param clientKeystoreType the type of keystore file; e.g. 'JKS', or 'PKCS12'.
*
* @return The created context.
*/
public static void setupSSLProperties(Properties env,
String cacerts, String clientcerts,
char[] caKeystorePwd, char[] clientKeystorePwd,
String caKeystoreType, String clientKeystoreType,
boolean tracing, boolean sslTracing)
throws Exception
{
// sanity check
if (cacerts == null) {
throw new NamingException("Cannot use SSL without a trusted CA certificates JKS file.");
}
// the exact protocol (e.g. "TLS") set in JndiSocketFactory
env.put(Context.SECURITY_PROTOCOL, "ssl");
// Initialise the SSL Socket Factory. Due to architectural wierdnesses, this is
// a separate, static method in our own separate SSL Factory class.
JndiSocketFactory.init(cacerts, clientcerts,
caKeystorePwd, clientKeystorePwd,
caKeystoreType, clientKeystoreType);
// Tell JNDI to use our own, separate SSL Factory class with the keystores set as previously
env.put("java.naming.ldap.factory.socket", "com.ca.commons.jndi.JndiSocketFactory");
// try to use client authentication (SASL) if a clientcert keystore and pwd supplied
if (clientcerts != null && (clientKeystorePwd != null && clientKeystorePwd.length > 0))
{
env.put(Context.SECURITY_AUTHENTICATION, "EXTERNAL"); // Use sasl external (i.e., certificate) auth
}
// set the tracing level now, since (wierdly) it can't be set once the connection is open.
if (tracing) {
env.put("com.sun.jndi.ldap.trace.ber", System.err);
}
if (sslTracing) {
System.setProperty("javax.net.debug=all", "all");
}
}
/**
* This is a raw interface to javax.naming.directory.InitialDirContext, that allows
* an arbitrary environment string to be passed through. Often it will be
* convenient to create that environment list using aset...Properties call (or just
* use one of the constructors to create a JNDIOps object.
* @param env a list of environment variables for the context
* @return a newly created DirContext.
*/
public static DirContext openContext(Properties env)
throws NamingException
{
DirContext ctx = new InitialDirContext(env);
if (ctx == null) {
throw new NamingException("Internal Error with jndi connection: No Context was returned, however no exception was reported by jndi.");
}
return ctx;
}
/**
* <p>A wrapper for context.rename... changes the
* distinguished name of an object, checks for error.
* !! Only changes the final RDN.</p>
*
* <p>WARNING! this will fail for single valued manditory attributes.
*since using 'deleteRDN = false' - use renameEntry(old, new, deleteOldRdn)
* method instead - 30 May 2002.</p>
*
* @param oldDN current distinguished name of an object.
* @param newDN the name it is to be changed to.
*/
public void renameEntry (Name oldDN, Name newDN)
throws NamingException
{
Name rdn = newDN.getSuffix(newDN.size()-1);
Name oldRdn = oldDN.getSuffix(oldDN.size()-1);
if (oldRdn.toString().equals(rdn.toString()) == false) {
this.ctx.rename (oldDN, rdn);
}
}
/**
* Copies an object to a new DN by the simple expedient of adding
* an object with the new DN, and the attributes of the old object.
* @param fromDN the original object being copied
* @param toDN the new object being created
*/
public void copyEntry(Name fromDN, Name toDN)
throws NamingException
{
addEntry(toDN, read(fromDN));
}
/**
* creates a new object (subcontext) with the given
* dn and attributes.
*
* @param dn the distinguished name of the new object
* @param atts attributes for the new object
*/
public void addEntry (Name dn, Attributes atts)
throws NamingException
{
this.ctx.createSubcontext (dn, atts);
}
/**
* deletes a leaf entry (subcontext). It is
* an error to attempt to delete an entry which is not a leaf
* entry, i.e. which has children.
*
*/
public void deleteEntry (Name dn)
throws NamingException
{
this.ctx.destroySubcontext (dn);
}
/**
* Checks the existence of a particular DN, without (necessarily)
* reading any attributes.
* @param nodeDN the DN to check
* @return the existence of the nodeDN (or false if an error occurs).
*/
public boolean exists(Name nodeDN)
throws NamingException
{
return (this.ctx.lookup(nodeDN)!=null);
/* FUTURE - DSML bug
catch (NullPointerException e) //TE: thrown by sun at com.sun.jndi.dsmlv2.soap.DsmlSoapCtx.c_lookup(DsmlSoapCtx.java:571)
return false;
*/
}
/**
* Reads all the attribute type and values for the given entry.
* @param dn the ldap string distinguished name of entry to be read
* @return an 'Attributes' object containing a list of all Attribute
* objects.
*/
public synchronized Attributes read(Name dn)
throws NamingException
{
return read(dn, null);
}
/**
* Reads all the attribute type and values for the given entry.
* @param dn the ldap string distinguished name of entry to be read
* @param returnAttributes a list of specific attributes to return.
* @return an 'Attributes' object containing a list of all Attribute
* objects.
*/
public synchronized Attributes read(Name dn, String[] returnAttributes)
throws NamingException
{
return this.ctx.getAttributes(dn, returnAttributes);
}
/**
* Modifies an object's attributes, either adding, replacing or
* deleting the passed attributes.
*
* @param dn distinguished name of object to modify
* @param mod_type the modification type to be performed; one of
* DirContext.REPLACE_ATTRIBUTE, DirContext.DELETE_ATTRIBUTE, or
* DirContext.ADD_ATTRIBUTE.
* @param attr the new attributes to update the object with.
*/
public void modifyAttributes(Name dn, int mod_type, Attributes attr)
throws NamingException
{
this.ctx.modifyAttributes(dn, mod_type, attr);
}
/**
* Modifies an object's attributes, either adding, replacing or
* deleting the passed attributes.
*
* @param dn distinguished name of object to modify
* @param modList a list of ModificationItems
*/
public void modifyAttributes(Name dn, ModificationItem[] modList)
throws NamingException
{
this.ctx.modifyAttributes(dn, modList);
}
/**
* Updates an object with a new set of attributes
*
* @param dn distinguished name of object to update
* @param atts the new attributes to update the object with.
*/
public void updateEntry (Name dn, Attributes atts)
throws NamingException
{
modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, atts);
}
/**
* deletes an attribute from an object
*
* @param dn distinguished name of object
* @param a the attribute to delete
*/
public void deleteAttribute(Name dn, Attribute a)
throws NamingException
{
BasicAttributes atts = new BasicAttributes();
atts.put(a);
modifyAttributes(dn, DirContext.REMOVE_ATTRIBUTE, atts);
}
/**
* deletes a set of attribute-s from an object
*
* @param dn distinguished name of object
* @param a the Attributes object containing the
* list of attribute-s to delete
*/
public void deleteAttributes(Name dn, Attributes a)
throws NamingException
{
modifyAttributes(dn, DirContext.REMOVE_ATTRIBUTE, a);
}
/**
* updates an Attribute with a new value set
*
* @param dn distinguished name of object
* @param a the attribute to modify
*/
public void updateAttribute(Name dn, Attribute a)
throws NamingException
{
BasicAttributes atts = new BasicAttributes();
atts.put(a);
modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, atts);
}
/**
* updates a set of Attribute-s.
*
* @param dn distinguished name of object
* @param a an Attributes object containing the attribute-s to modify
*/
public void updateAttributes(Name dn, Attributes a)
throws NamingException
{
modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, a);
}
/**
* Adds a new attribute to a particular dn.
*
* @param dn distinguished name of object
* @param a the attribute to modify
*/
public void addAttribute(Name dn, Attribute a)
throws NamingException
{
BasicAttributes atts = new BasicAttributes();
atts.put(a);
modifyAttributes(dn, DirContext.ADD_ATTRIBUTE, atts);
}
/**
* Adds a set of attributes to a particular dn.
*
* @param dn distinguished name of object
* @param a the Attributes (set of attribute-s) to add
*/
public void addAttributes(Name dn, Attributes a)
throws NamingException
{
modifyAttributes(dn, DirContext.ADD_ATTRIBUTE, a);
}
/**
* returns the next level of a directory tree, returning
* a Enumeration of the results, *relative* to the SearchBase (i.e. not as
* absolute DNs), along with their object classes if possible.
*
* @param Searchbase the node in the tree to expand
* @return list of results (NameClassPair); the next layer of the tree...
*/
public NamingEnumeration list(Name Searchbase)
throws NamingException
{
// Attempt to read the names of the next level of subentries along with their object
// classes. Failing that, try to just read their names.
return rawOneLevelSearch(Searchbase, "(objectclass=*)", 0, 0, new String[] {"1.1"} );
}
/**
* Performs a one-level directory search (i.e. a search of immediate children), returning
* object classes if possible, otherwise just the names.
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
*
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchOneLevel(Name searchbase, String filter, int limit, int timeout)
throws NamingException
{
return searchOneLevel(searchbase, filter, limit, timeout, new String[] {"1.1"});
}
/**
* Performs a one-level directory search (i.e. a search of immediate children)
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @param returnAttributes an array of strings containing the names of attributes to search. (null = all, empty array = none)
*
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchOneLevel(Name searchbase, String filter, int limit,
int timeout, String[] returnAttributes)
throws NamingException
{
return rawOneLevelSearch(searchbase, filter, limit, timeout, returnAttributes);
}
/**
* Method that calls the actual search on the jndi context.
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @param returnAttributes an array of strings containing the names of attributes to search. (null = all, empty array = none)
* @return
* @throws NamingException
*/
private NamingEnumeration rawOneLevelSearch(Name searchbase, String filter, int limit,
int timeout, String[] returnAttributes ) throws NamingException
{
/* specify search constraints to search one level */
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
constraints.setCountLimit(limit);
constraints.setTimeLimit(timeout);
constraints.setReturningAttributes(returnAttributes);
NamingEnumeration results = this.ctx.search(searchbase, filter, null);
return results;
}
/**
* Performs a directory sub tree search (i.e. of the next level and all subsequent levels below),
* returning just dns);
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchSubTree(Name searchbase, String filter, int limit, int timeout)
throws NamingException
{
return searchSubTree(searchbase, filter, limit, timeout, new String[] {"1.1"});
}
/**
* Performs a directory sub tree search (i.e. of the next level and all subsequent levels below).
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @param returnAttributes an array of strings containing the names of attributes to search. (null = all, empty array = none)
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchSubTree(Name searchbase, String filter, int limit,
int timeout, String[] returnAttributes)
throws NamingException
{
NamingEnumeration result = null;
if (returnAttributes != null && returnAttributes.length == 0) {
returnAttributes = new String[] {"objectClass"};
}
/* specify search constraints to search subtree */
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setCountLimit(limit);
constraints.setTimeLimit(timeout);
constraints.setReturningAttributes(returnAttributes);
result = this.ctx.search(searchbase, filter, constraints);
return result;
}
/**
* Performs a base object search (i.e. just a search of the current entry, nothing below it),
* returning no attributes (i.e. just DNs);
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchBaseEntry(Name searchbase, String filter, int limit, int timeout)
throws NamingException
{
return searchBaseEntry(searchbase, filter, limit, timeout, new String[] {"objectClass"});
}
/**
* Performs a base object search (i.e. just a search of the current entry, nothing below it).
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @param returnAttributes an array of strings containing the names of attributes to search. (null = all, empty array = none)
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchBaseEntry(Name searchbase, String filter, int limit,
int timeout, String[] returnAttributes)
throws NamingException
{
NamingEnumeration result = null;
if (returnAttributes != null && returnAttributes.length == 0) {
returnAttributes = new String[] {"objectClass"};
}
/* specify search constraints to search subtree */
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.OBJECT_SCOPE);
constraints.setCountLimit(limit);
constraints.setTimeLimit(timeout);
constraints.setReturningAttributes(returnAttributes);
result = this.ctx.search(searchbase, filter, constraints);
return result;
}
/**
* This method allows an object to be renamed, while also specifying
* the exact fate of the old name.
* @param OldDN the original name to be changed
* @param NewDN the new name
* @param deleteOldRDN whether the rdn of the old name should be removed,
* or retained as a second attribute value.
*/
public void renameEntry (Name OldDN, Name NewDN, boolean deleteOldRDN)
throws NamingException
{
String value = (deleteOldRDN) ? "true" : "false" ;
try
{
this.ctx.addToEnvironment("java.naming.ldap.deleteRDN", value);
renameEntry(OldDN, NewDN);
this.ctx.addToEnvironment("java.naming.ldap.deleteRDN", "false"); // reset to default of 'false' afterwards.
}
catch (NamingException e)
{
this.ctx.addToEnvironment("java.naming.ldap.deleteRDN", "false"); // reset to default of 'false' afterwards.
throw e;
}
}
// *********************************
/**
* <p>A wrapper for context.rename... changes the
* distinguished name of an object.</p>
*
* <p>WARNING! this will fail for single valued manditory attributes.
*since using 'deleteRDN = false' - use renameEntry(old, new, deleteOldRdn)
* method instead - 30 May 2002.</p>
*
* @param oldDN current distinguished name of an object.
* @param newDN the name it is to be changed to.
*/
public void renameEntry (String oldDN, String newDN)
throws NamingException
{
this.ctx.rename (oldDN, newDN);
}
/**
* Copies an object to a new DN by the simple expedient of adding
* an object with the new DN, and the attributes of the old object.
* @param fromDN the original object being copied
* @param toDN the new object being created
*/
public void copyEntry(String fromDN, String toDN)
throws NamingException
{
addEntry(toDN, read(fromDN));
}
/**
* creates a new object (subcontext) with the given
* dn and attributes.
*
* @param dn the distinguished name of the new object
* @param atts attributes for the new object
*/
public void addEntry (String dn, Attributes atts)
throws NamingException
{
this.ctx.createSubcontext (dn, atts);
}
/**
* deletes a leaf entry (subcontext). It is
* an error to attempt to delete an entry which is not a leaf
* entry, i.e. which has children.
*
*/
public void deleteEntry (String dn)
throws NamingException
{
this.ctx.destroySubcontext (dn);
}
/**
* Checks the existence of a particular DN, without (necessarily)
* reading any attributes.
* @param nodeDN the DN to check
* @return the existence of the nodeDN (or false if an error occurs).
*/
public boolean exists(String nodeDN)
throws NamingException
{
return (this.ctx.lookup(nodeDN)!=null);
/* FUTURE - DSML bug
catch (NullPointerException e) //TE: thrown by sun at com.sun.jndi.dsmlv2.soap.DsmlSoapCtx.c_lookup(DsmlSoapCtx.java:571)
return false;
*/
}
/**
* Reads all the attribute type and values for the given entry.
* @param dn the ldap string distinguished name of entry to be read
* @return an 'Attributes' object containing a list of all Attribute
* objects.
*/
public synchronized Attributes read(String dn)
throws NamingException
{
return read(dn, null);
}
/**
* Reads all the attribute type and values for the given entry.
* @param dn the ldap string distinguished name of entry to be read
* @param returnAttributes a list of specific attributes to return.
* @return an 'Attributes' object containing a list of all Attribute
* objects.
*/
public synchronized Attributes read(String dn, String[] returnAttributes)
throws NamingException
{
Name name = getNameFromDNString(dn);
return this.ctx.getAttributes(name, returnAttributes);
}
/**
* Modifies an object's attributes, either adding, replacing or
* deleting the passed attributes.
*
* @param dn distinguished name of object to modify
* @param mod_type the modification type to be performed; one of
* DirContext.REPLACE_ATTRIBUTE, DirContext.DELETE_ATTRIBUTE, or
* DirContext.ADD_ATTRIBUTE.
* @param attr the new attributes to update the object with.
*/
public void modifyAttributes(String dn, int mod_type, Attributes attr)
throws NamingException
{
this.ctx.modifyAttributes(dn, mod_type, attr);
}
/**
* Modifies an object's attributes, either adding, replacing or
* deleting the passed attributes.
*
* @param dn distinguished name of object to modify
* @param modList a list of ModificationItems
*/
public void modifyAttributes(String dn, ModificationItem[] modList)
throws NamingException
{
this.ctx.modifyAttributes(dn, modList);
}
/**
* Updates an object with a new set of attributes
*
* @param dn distinguished name of object to update
* @param atts the new attributes to update the object with.
*/
public void updateEntry (String dn, Attributes atts)
throws NamingException
{
modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, atts);
}
/**
* deletes an attribute from an object
*
* @param dn distinguished name of object
* @param a the attribute to delete
*/
public void deleteAttribute(String dn, Attribute a)
throws NamingException
{
BasicAttributes atts = new BasicAttributes();
atts.put(a);
modifyAttributes(dn, DirContext.REMOVE_ATTRIBUTE, atts);
}
/**
* deletes a set of attribute-s from an object
*
* @param dn distinguished name of object
* @param a the Attributes object containing the
* list of attribute-s to delete
*/
public void deleteAttributes(String dn, Attributes a)
throws NamingException
{
modifyAttributes(dn, DirContext.REMOVE_ATTRIBUTE, a);
}
/**
* updates an Attribute with a new value set
*
* @param dn distinguished name of object
* @param a the attribute to modify
*/
public void updateAttribute(String dn, Attribute a)
throws NamingException
{
BasicAttributes atts = new BasicAttributes();
atts.put(a);
modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, atts);
}
/**
* updates a set of Attribute-s.
*
* @param dn distinguished name of object
* @param a an Attributes object containing the attribute-s to modify
*/
public void updateAttributes(String dn, Attributes a)
throws NamingException
{
modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, a);
}
/**
* Adds a new attribute to a particular dn.
*
* @param dn distinguished name of object
* @param a the attribute to modify
*/
public void addAttribute(String dn, Attribute a)
throws NamingException
{
BasicAttributes atts = new BasicAttributes();
atts.put(a);
modifyAttributes(dn, DirContext.ADD_ATTRIBUTE, atts);
}
/**
* Adds a set of attributes to a particular dn.
*
* @param dn distinguished name of object
* @param a the Attributes (set of attribute-s) to add
*/
public void addAttributes(String dn, Attributes a)
throws NamingException
{
modifyAttributes(dn, DirContext.ADD_ATTRIBUTE, a);
}
/**
* returns the next level of a directory tree, returning
* a Enumeration of the results, *relative* to the SearchBase (i.e. not as
* absolute DNs), along with their object classes if possible.
*
* @param Searchbase the node in the tree to expand
* @return list of results (NameClassPair); the next layer of the tree...
*/
public NamingEnumeration list(String Searchbase)
throws NamingException
{
// Attempt to read the names of the next level of subentries along with their object
// classes. Failing that, try to just read their names.
return rawOneLevelSearch(Searchbase, "(objectclass=*)", 0, 0, new String[] {"1.1"} );
}
/**
* Performs a one-level directory search (i.e. a search of immediate children), returning
* object classes if possible, otherwise just the names.
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
*
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchOneLevel(String searchbase, String filter, int limit, int timeout)
throws NamingException
{
return searchOneLevel(searchbase, filter, limit, timeout, new String[] {"1.1"});
}
/**
* Performs a one-level directory search (i.e. a search of immediate children)
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @param returnAttributes an array of strings containing the names of attributes to search. (null = all, empty array = none)
*
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchOneLevel(String searchbase, String filter, int limit,
int timeout, String[] returnAttributes)
throws NamingException
{
return rawOneLevelSearch(searchbase, filter, limit, timeout, returnAttributes);
}
/**
* Method that calls the actual search on the jndi context.
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @param returnAttributes an array of strings containing the names of attributes to search. (null = all, empty array = none)
* @return
* @throws NamingException
*/
private NamingEnumeration rawOneLevelSearch(String searchbase, String filter, int limit,
int timeout, String[] returnAttributes ) throws NamingException
{
Name name = getNameFromDNString(searchbase);
return rawOneLevelSearch(name,filter,limit,timeout,returnAttributes);
}
private Name getNameFromDNString(String searchbase) throws NamingException {
// Get the parser for this namespace
NameParser ldapParser = this.ctx.getNameParser("");
// Parse name
Name compound = ldapParser.parse(searchbase);
return compound;
}
/**
* Performs a directory sub tree search (i.e. of the next level and all subsequent levels below),
* returning just dns);
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchSubTree(String searchbase, String filter, int limit, int timeout)
throws NamingException
{
return searchSubTree(searchbase, filter, limit, timeout, new String[] {"1.1"});
}
/**
* Performs a directory sub tree search (i.e. of the next level and all subsequent levels below).
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @param returnAttributes an array of strings containing the names of attributes to search. (null = all, empty array = none)
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchSubTree(String searchbase, String filter, int limit,
int timeout, String[] returnAttributes)
throws NamingException
{
NamingEnumeration result = null;
if (returnAttributes != null && returnAttributes.length == 0) {
returnAttributes = new String[] {"objectClass"};
}
/* specify search constraints to search subtree */
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setCountLimit(limit);
constraints.setTimeLimit(timeout);
constraints.setReturningAttributes(returnAttributes);
result = this.ctx.search(searchbase, filter, constraints);
return result;
}
/**
* Performs a base object search (i.e. just a search of the current entry, nothing below it),
* returning no attributes (i.e. just DNs);
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchBaseEntry(String searchbase, String filter, int limit, int timeout)
throws NamingException
{
return searchBaseEntry(searchbase, filter, limit, timeout, new String[] {"objectClass"});
}
/**
* Performs a base object search (i.e. just a search of the current entry, nothing below it).
*
* @param searchbase the domain name (relative to initial context in ldap) to seach from.
* @param filter the non-null filter to use for the search
* @param limit the maximum number of results to return
* @param timeout the maximum time to wait before abandoning the search
* @param returnAttributes an array of strings containing the names of attributes to search. (null = all, empty array = none)
* @return list of search results ('SearchResult's); entries matching the search filter.
*/
public NamingEnumeration searchBaseEntry(String searchbase, String filter, int limit,
int timeout, String[] returnAttributes)
throws NamingException
{
NamingEnumeration result = null;
if (returnAttributes != null && returnAttributes.length == 0) {
returnAttributes = new String[] {"objectClass"};
}
/* specify search constraints to search subtree */
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.OBJECT_SCOPE);
constraints.setCountLimit(limit);
constraints.setTimeLimit(timeout);
constraints.setReturningAttributes(returnAttributes);
result = this.ctx.search(searchbase, filter, constraints);
return result;
}
/**
* Shuts down the current context.<p>
* nb. It is not an error to call this method multiple times.
*/
public void close()
throws NamingException
{
if (this.ctx == null) {
return; // it is not an error to multiply disconnect.
}
this.ctx.close();
}
/**
* This method allows an object to be renamed, while also specifying
* the exact fate of the old name.
* @param OldDN the original name to be changed
* @param NewDN the new name
* @param deleteOldRDN whether the rdn of the old name should be removed,
* or retained as a second attribute value.
*/
public void renameEntry (String OldDN, String NewDN, boolean deleteOldRDN)
throws NamingException
{
String value = (deleteOldRDN) ? "true" : "false" ;
try
{
this.ctx.addToEnvironment("java.naming.ldap.deleteRDN", value);
renameEntry(OldDN, NewDN);
this.ctx.addToEnvironment("java.naming.ldap.deleteRDN", "false"); // reset to default of 'false' afterwards.
}
catch (NamingException e)
{
this.ctx.addToEnvironment("java.naming.ldap.deleteRDN", "false"); // reset to default of 'false' afterwards.
throw e; // rethrow exception...
}
}
}