/* * Copyright (C) 2000 - 2008 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ package com.nary.net.ldap; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.AttributeModificationException; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.NoSuchAttributeException; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import com.nary.util.FastMap; /** * ldapConnection * * A class for creating a connection to an ldap server, and performing * a query, entry addition, entry deletion, or entry modification. */ public class ldapConnection{ public final static int ONELEVEL_SCOPE = SearchControls.ONELEVEL_SCOPE, SUBTREE_SCOPE = SearchControls.SUBTREE_SCOPE, BASE_SCOPE = SearchControls.OBJECT_SCOPE; public final static byte MODIFY_ADD=0, MODIFY_DELETE=1, MODIFY_REPLACE=2; private Hashtable<String, String> env; private String dn = null; private int scope; // params for search private String searchbase; // the entry in the directory where a search will begin from private String filter; // e.g. "(sn=Carter)"; private String server; // attributes - req'd for search, add, modify, modifydn private String [] attributes = null; private char separator; // char that separates values in multivalue attributes // max. number of entries to be returned in a query. O equiv. to return all. private int maxEntries; byte modifyType; public ldapConnection(String _server){ // defaults scope = SearchControls.ONELEVEL_SCOPE; maxEntries = 0; // translated as no limit when sent to server modifyType = MODIFY_REPLACE; separator = ','; searchbase = null; filter = null; server = _server; env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); if ( _server.indexOf(':') == -1 ){ env.put(Context.PROVIDER_URL, "ldap://" + _server + ":389"); }else{ env.put(Context.PROVIDER_URL, "ldap://" + _server); } env.put("java.naming.ldap.referral.limit", "0"); env.put(Context.REFERRAL, "follow"); }// ldapConnection() /** * set the scope of the ldap connection. * This must either be one level (default), base, or subtree */ public void setScope(int _scope){ scope = _scope; }// setScope() /** * set the port to connect to on the ldap server (defaults to 389). */ public void setPort(int _port){ // overwrites the port number if the server doesn't provide one if ( server.indexOf(':') == -1 ){ env.put(Context.PROVIDER_URL, "ldap://" + server + ":" + _port); } }// setPort() /** * set the username and password for a simple authentication. */ public void addAuthentication(String _username, String _password){ env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, _username); env.put(Context.SECURITY_CREDENTIALS, _password); }// addAuthentication() // 0 disables cfldaps ability to use referred addresses thus no data is returned // For other values of _referralLimit, entries that go beyond this hop limit will be ignored. public void setReferral(int _referralLimit){ if( _referralLimit == 0 ){ env.put(Context.REFERRAL, "ignore"); }else{ env.put("java.naming.ldap.referral.limit", "" + _referralLimit); env.put(Context.REFERRAL, "follow"); } }// setReferral() // required for query // specifies the distinguished name to be used to start the search public void setStart(String _start){ searchbase = _start; }// setStart() public void setMaxEntries(int _maxEntries){ maxEntries = _maxEntries; }// setMaxEntries() // required for query, add, modifydn, modify public void setAttributes(String[] _attribs){ if ( _attribs.length == 1 && _attribs[0].equals( "*" ) ){ attributes = null; }else{ attributes = _attribs; } }// setAttributes() //req'd for query public void setFilter(String _filter){ filter = _filter; }// setFilter() // required for add, modify, modifydn and delete public void setDN(String _dn){ dn = _dn; }//setDN() // add, delete or replace(default) public void setModifyType(byte _modifyType){ modifyType = _modifyType; }// setModifyType() public void setSSL(){ env.put(Context.SECURITY_PROTOCOL, "ssl"); } /** * sets the character that is used to separate values in multi-valued attributes. */ public void setSeparator(char _separator){ separator = _separator; }// setSeparator() public void modify() throws AttributeModificationException, NamingException{ //Get a reference to a directory context int modType; // decode the modification type to one which the context will understand switch (modifyType){ case ldapConnection.MODIFY_REPLACE: // attributes require name=value pairs modType = DirContext.REPLACE_ATTRIBUTE; break; case ldapConnection.MODIFY_ADD: modType = DirContext.ADD_ATTRIBUTE; // attributes require name=value pairs break; case ldapConnection.MODIFY_DELETE: modType = DirContext.REMOVE_ATTRIBUTE; // attributes require names only break; default: modType = DirContext.REPLACE_ATTRIBUTE; }// switch DirContext ctx = new InitialDirContext(env); Attributes attributes = processAttributes(); ctx.modifyAttributes(dn, modType, attributes); ctx.close(); }// modify() /** * modifies the dn to the one provided by the attributes. * No attempt is made to verify the attributes are valid i.e * in the format dn = .... */ public void modifyDN() throws NamingException{ DirContext ctx = new InitialDirContext(env); ctx.rename(dn, attributes[0]); ctx.close(); }// modifyDN() /** * adds the entry given when attributes were set, to the directory * with the set DN. */ public void add() throws NamingException{ DirContext ctx = new InitialDirContext(env); ldapEntry e = new ldapEntry(processAttributes()); ctx.bind(dn, e); ctx.close(); }// add() /** * deletes the entry with the set DN. */ public void delete() throws NamingException{ //Get a reference to a directory context DirContext ctx = new InitialDirContext(env); ctx.destroySubcontext(dn); ctx.close(); }// delete() /** * searches for the entries with the given start, search scope, attributes and filter; * returning the max entries set. * Returns the results in a Vector of Strings. */ public List<Map<String, String>> search() throws NamingException, NoSuchAttributeException{ boolean returnDN = false; DirContext ctx = new InitialDirContext(env); SearchControls constraints = new SearchControls(); constraints.setSearchScope(scope); constraints.setCountLimit(maxEntries); constraints.setReturningAttributes(attributes); if (attributes != null ){ // restricts the attributes of the entries that will be returned // otherwise all attributes are returned for ( int i = 0; i < attributes.length; i++ ){ // Only return the DN attribute if it is listed in the attributes parameter - refer to bug #1302 if ( attributes[i].equalsIgnoreCase( "DN" ) ){ returnDN = true; break; } } } NamingEnumeration<SearchResult> results = ctx.search(searchbase, filter, constraints); List<Map<String, String>> resultVector = new ArrayList<Map<String, String>>(); while (results != null && results.hasMore()) { SearchResult sr = (SearchResult) results.next(); Map<String, String> entry = new FastMap<String, String>( FastMap.CASE_INSENSITIVE ); if ( returnDN ){ entry.put( "dn", sr.getNameInNamespace() ); } Attributes attrs; attrs = sr.getAttributes(); for (NamingEnumeration<? extends Attribute> ne = attrs.getAll(); ne.hasMoreElements();) { Attribute attr = ne.next(); String attrID = attr.getID(); /* Only store the first attribute value to match the behaviour of CFMX - refer to bug #1301 * entry.put( attrID.toLowerCase(), attr.get() ); //this was causing bug #1353, so it's been replaced with the code below * * More details regarding attributes in the LDAP server that have multiple values: * Matt M's testing showed that a cfldap query using attributes="*" * will display the attribute values (when they are comprised of multiple values) differently * between BD and CFMX 6.1. * For example if the value of an attribute in the LDAP server is "a, b" then using * attributes="*" in the query, BD will return "a, b" as the value for that attribute, while * CFMX 6.1 will only return the 1st value "a". This would appear to be a bug in CFMX 6.1. * * So for now we won't try to match CFMX behavior in the attributes="*" scenario. * * Note that explicitly listing the attribute(s) you want (instead of using "*") gets CFMX * to give the full list of values for a multi-valued attribute. */ NamingEnumeration<?> nameEnum = attr.getAll(); if(nameEnum != null) { StringBuilder valueBuffer = new StringBuilder(); int i=0; while(nameEnum.hasMore()) { Object o = nameEnum.next(); if(o != null) { if(i > 0) { valueBuffer.append(separator); valueBuffer.append(" "); } valueBuffer.append(o); ++i; } } entry.put(attrID, valueBuffer.toString()); //removed the .toLowerCase() call on attrID to fix bug #2035 } } resultVector.add(entry); } ctx.close(); return resultVector; }// search() // req'd to be called when modifying or adding an entry // takes the attribute list and produces a Attributes object from it. private Attributes processAttributes(){ String name; // name of the attribute Attribute att; // the attribute itself, used to build up the Attributes object Attributes attrs = new BasicAttributes(true); for (int i = 0; i < attributes.length; i++){ int indexOfEquals = attributes[i].indexOf('='); // in the case of modify delete, attributes don't have values if (indexOfEquals == -1){ name = attributes[i]; att = new BasicAttribute(name); attrs.put(att); continue; } name = attributes[i].substring(0, indexOfEquals); att = new BasicAttribute(name); String values = attributes[i].substring(indexOfEquals+1).trim(); int valStartIndex = 0; int indexOfSeparator = values.indexOf(separator); while (indexOfSeparator != -1){ att.add(values.substring(valStartIndex, indexOfSeparator).trim()); valStartIndex = indexOfSeparator+1; indexOfSeparator = values.indexOf(separator, indexOfSeparator+1); } att.add(values.substring(valStartIndex).trim()); // add the attribute to the set of attributes attrs.put(att); } return attrs; }// processAttributes() public static void main(String [] args){ // ldap://ldap.bigfoot.com:389/o=airius.com?cn,mail,?sub?(cn=billybob thornton) /* // Exchange server testing ldapConnection conn = new ldapConnection("arran.n-ary.com"); conn.setFilter("(&(uid=A*)(sn=W*))"); conn.setDN(""); conn.setStart("");//cn=AndrewW, cn=Recipients, ou=N-ARY, o=n-ary group"); conn.addAuthenticate("dc=N-ARY, cn=administrator, ou=N-ARY, o=n-ary group", "nokia1989"); conn.setScope(ldapConnection.SUBTREE_SCOPE); conn.search(); */ try{ ldapConnection conn = new ldapConnection("62.189.55.7");//"arden.n-ary.com");// conn.setFilter("(&(objectclass=*))"); conn.setStart("dc=example,dc=com"); conn.addAuthentication("cn=Administrator, dc=example, dc=com", "secret"); conn.setScope(ldapConnection.SUBTREE_SCOPE); List<Map<String, String>> results = conn.search(); for ( int i = 0; i < results.size(); i++ ) { System.out.println( results.get( i ) ); } }catch(NamingException e){ e.printStackTrace(); } }// main() }// ldapConnection