001 package railo.runtime.net.ldap; 002 003 import java.io.IOException; 004 import java.security.Security; 005 import java.util.Enumeration; 006 import java.util.Hashtable; 007 008 import javax.naming.NamingEnumeration; 009 import javax.naming.NamingException; 010 import javax.naming.directory.Attribute; 011 import javax.naming.directory.Attributes; 012 import javax.naming.directory.BasicAttribute; 013 import javax.naming.directory.BasicAttributes; 014 import javax.naming.directory.DirContext; 015 import javax.naming.directory.InitialDirContext; 016 import javax.naming.directory.ModificationItem; 017 import javax.naming.directory.SearchControls; 018 import javax.naming.directory.SearchResult; 019 import javax.naming.ldap.Control; 020 import javax.naming.ldap.InitialLdapContext; 021 022 import railo.commons.lang.ClassException; 023 import railo.commons.lang.ClassUtil; 024 import railo.commons.lang.StringUtil; 025 import railo.runtime.exp.PageException; 026 import railo.runtime.op.Caster; 027 import railo.runtime.type.Collection; 028 import railo.runtime.type.KeyImpl; 029 import railo.runtime.type.List; 030 import railo.runtime.type.Query; 031 import railo.runtime.type.QueryImpl; 032 033 import com.sun.jndi.ldap.ctl.SortControl; 034 import com.sun.jndi.ldap.ctl.SortKey; 035 import com.sun.net.ssl.internal.ssl.Provider; 036 037 038 /** 039 * Ldap Client 040 */ 041 public final class LDAPClient { 042 043 /** 044 * Field <code>SECURE_NONE</code> 045 */ 046 public static final short SECURE_NONE=0; 047 /** 048 * Field <code>SECURE_CFSSL_BASIC</code> 049 */ 050 public static final short SECURE_CFSSL_BASIC=1; 051 /** 052 * Field <code>SECURE_CFSSL_CLIENT_AUTH</code> 053 */ 054 public static final short SECURE_CFSSL_CLIENT_AUTH=2; 055 056 /** 057 * Field <code>SORT_TYPE_CASE</code> 058 */ 059 public static final int SORT_TYPE_CASE = 0; 060 /** 061 * Field <code>SORT_TYPE_NOCASE</code> 062 */ 063 public static final int SORT_TYPE_NOCASE = 1; 064 065 /** 066 * Field <code>SORT_DIRECTION_ASC</code> 067 */ 068 public static final int SORT_DIRECTION_ASC = 0; 069 /** 070 * Field <code>SORT_DIRECTION_DESC</code> 071 */ 072 public static final int SORT_DIRECTION_DESC = 1; 073 074 Hashtable env=new Hashtable(); 075 076 077 /** 078 * constructor of the class 079 * @param server 080 * @param port 081 * @param binaryColumns 082 */ 083 public LDAPClient(String server, int port,String[] binaryColumns) { 084 085 env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory"); 086 env.put("java.naming.provider.url", "ldap://" + server+":"+port); 087 088 // rEAD AS bINARY 089 for(int i = 0; i < binaryColumns.length; i++)env.put("java.naming.ldap.attributes.binary", binaryColumns[i]); 090 091 // Referral 092 env.put("java.naming.referral", "ignore"); 093 } 094 095 /** 096 * sets username password for the connection 097 * @param username 098 * @param password 099 */ 100 public void setCredential(String username, String password) { 101 if(username != null) { 102 env.put("java.naming.security.principal", username); 103 env.put("java.naming.security.credentials", password); 104 } 105 else { 106 env.remove("java.naming.security.principal"); 107 env.remove("java.naming.security.credentials"); 108 } 109 } 110 111 /** 112 * sets the secure Level 113 * @param secureLevel [SECURE_CFSSL_BASIC, SECURE_CFSSL_CLIENT_AUTH, SECURE_NONE] 114 * @throws ClassNotFoundException 115 * @throws ClassException 116 */ 117 public void setSecureLevel(short secureLevel) throws ClassException { 118 // Security 119 if(secureLevel==SECURE_CFSSL_BASIC) { 120 env.put("java.naming.security.protocol", "ssl"); 121 env.put("java.naming.ldap.factory.socket", "javax.net.ssl.SSLSocketFactory"); 122 //Class.orName("com.sun.net.ssl.internal.ssl.Provider"); 123 ClassUtil.loadClass("com.sun.net.ssl.internal.ssl.Provider"); 124 125 Security.addProvider(new Provider()); 126 127 } 128 else if(secureLevel==SECURE_CFSSL_CLIENT_AUTH) { 129 env.put("java.naming.security.protocol", "ssl"); 130 env.put("java.naming.security.authentication", "EXTERNAL"); 131 } 132 else { 133 env.put("java.naming.security.authentication", "simple"); 134 env.remove("java.naming.security.protocol"); 135 env.remove("java.naming.ldap.factory.socket"); 136 } 137 } 138 139 /** 140 * sets thr referral 141 * @param referral 142 */ 143 public void setReferral(int referral) { 144 if(referral > 0) { 145 env.put("java.naming.referral", "follow"); 146 env.put("java.naming.ldap.referral.limit", Caster.toString(referral)); 147 } 148 else { 149 env.put("java.naming.referral", "ignore"); 150 env.remove("java.naming.ldap.referral.limit"); 151 } 152 } 153 154 155 156 /** 157 * adds LDAP entries to LDAP server 158 * @param dn 159 * @param attributes 160 * @param delimiter 161 * @throws NamingException 162 * @throws PageException 163 */ 164 public void add(String dn, String attributes, String delimiter, String seperator) throws NamingException, PageException { 165 DirContext ctx = new InitialDirContext(env); 166 ctx.createSubcontext(dn, toAttributes(attributes,delimiter,seperator)); 167 ctx.close(); 168 } 169 170 /** 171 * deletes LDAP entries on an LDAP server 172 * @param dn 173 * @throws NamingException 174 */ 175 public void delete(String dn) throws NamingException{ 176 DirContext ctx = new InitialDirContext(env); 177 ctx.destroySubcontext(dn); 178 ctx.close(); 179 } 180 181 /** 182 * modifies distinguished name attribute for LDAP entries on LDAP server 183 * @param dn 184 * @param attributes 185 * @throws NamingException 186 */ 187 public void modifydn(String dn, String attributes) throws NamingException{ 188 DirContext ctx = new InitialDirContext(env); 189 ctx.rename(dn, attributes); 190 ctx.close(); 191 } 192 193 public void modify(String dn, int modifytype, String strAttributes, String delimiter, String separator) throws NamingException, PageException { 194 195 DirContext context = new InitialDirContext(env); 196 String strArrAttributes[] = toStringAttributes(strAttributes,delimiter); 197 198 int count = 0; 199 for(int i=0; i<strArrAttributes.length; i++) { 200 String[] attributesValues = getAttributesValues(strArrAttributes[i], separator); 201 if(attributesValues == null)count++; 202 else count+=attributesValues.length; 203 } 204 205 ModificationItem modItems[] = new ModificationItem[count]; 206 BasicAttribute basicAttr = null; 207 int k = 0; 208 for(int i = 0; i < strArrAttributes.length; i++) { 209 String attribute = strArrAttributes[i]; 210 String type = getAttrValueType(attribute); 211 String values[] = getAttributesValues(attribute,separator); 212 213 if(modifytype==DirContext.REPLACE_ATTRIBUTE) { 214 if(values == null) basicAttr = new BasicAttribute(type); 215 else basicAttr = new BasicAttribute(type, values[0]); 216 217 modItems[k] = new ModificationItem(modifytype, basicAttr); 218 k++; 219 if(values != null && values.length > 1) { 220 for(int j = 1; j < values.length; j++) { 221 basicAttr = new BasicAttribute(type, values[j]); 222 modItems[k] = new ModificationItem(DirContext.ADD_ATTRIBUTE, basicAttr); 223 k++; 224 } 225 } 226 } 227 else { 228 for(int j = 0; j < values.length; j++) { 229 if(type != null || modifytype==DirContext.ADD_ATTRIBUTE) 230 basicAttr = new BasicAttribute(type, values[j]); 231 else basicAttr = new BasicAttribute(values[j]); 232 modItems[k] = new ModificationItem(modifytype, basicAttr); 233 k++; 234 } 235 } 236 } 237 238 context.modifyAttributes(dn, modItems); 239 context.close(); 240 241 } 242 243 244 245 /** 246 * @param dn 247 * @param strAttributes 248 * @param scope 249 * @param startrow 250 * @param maxrows 251 * @param timeout 252 * @param sort 253 * @param sortType 254 * @param sortDirection 255 * @param start 256 * @param separator 257 * @param filter 258 * @return 259 * @throws NamingException 260 * @throws PageException 261 * @throws IOException 262 */ 263 public Query query(String strAttributes,int scope, int startrow, int maxrows, int timeout, 264 String[] sort, int sortType, int sortDirection, 265 String start, String separator, String filter) 266 throws NamingException, PageException, IOException { 267 //strAttributes=strAttributes.trim(); 268 boolean attEQAsterix=strAttributes.trim().equals("*"); 269 String[] attributes = attEQAsterix?new String[]{"name","value"}:toStringAttributes(strAttributes,","); 270 271 272 273 // Control 274 SearchControls controls = new SearchControls(); 275 controls.setReturningObjFlag(true); 276 controls.setSearchScope(scope); 277 if(!attEQAsterix)controls.setReturningAttributes(toStringAttributes(strAttributes,",")); 278 if(maxrows>0)controls.setCountLimit(startrow + maxrows + 1); 279 if(timeout>0)controls.setTimeLimit(timeout); 280 281 282 InitialLdapContext context = new InitialLdapContext(env, null); 283 284 285 // Sort 286 if(sort!=null && sort.length>0) { 287 boolean isSortAsc=sortDirection==SORT_DIRECTION_ASC; 288 289 SortKey keys[] = new SortKey[sort.length]; 290 for(int i=0;i<sort.length;i++) { 291 String item=sort[i].equalsIgnoreCase("dn")?"name":sort[i]; 292 if(item.indexOf(' ')!=-1)item=List.first(item," ",true); 293 keys[i] = new SortKey(item,isSortAsc ,sortType==LDAPClient.SORT_TYPE_CASE?null/*"CASE"*/:null); 294 //keys[i] = new SortKey(item); 295 } 296 context.setRequestControls(new Control[]{new SortControl(keys, Control.CRITICAL)}); 297 } 298 299 // Search 300 Query qry=new QueryImpl(attributes,0,"query"); 301 try { 302 NamingEnumeration results = context.search(start, filter, controls); 303 304 // Fill result 305 int row=1; 306 if(!attEQAsterix) { 307 while(results.hasMoreElements()) { 308 SearchResult resultRow = (SearchResult)results.next(); 309 if(row++<startrow)continue; 310 311 int len=qry.addRow(); 312 NamingEnumeration rowEnum = resultRow.getAttributes().getAll(); 313 String dn = resultRow.getNameInNamespace(); 314 qry.setAtEL("dn",len,dn); 315 while(rowEnum.hasMore()) { 316 Attribute attr = (Attribute)rowEnum.next(); 317 Collection.Key key = KeyImpl.init(attr.getID()); 318 Enumeration values = attr.getAll(); 319 Object value; 320 String existing,strValue; 321 while(values.hasMoreElements()) { 322 value = values.nextElement(); 323 strValue=Caster.toString(value,null); 324 existing=Caster.toString(qry.getAt(key, len,null),null); 325 if(!StringUtil.isEmpty(existing) && !StringUtil.isEmpty(strValue)) { 326 value = existing + separator + strValue; 327 } 328 else if(!StringUtil.isEmpty(existing)) 329 value = existing; 330 331 qry.setAtEL(key,len,value); 332 } 333 } 334 if(maxrows>0 && len>=maxrows)break; 335 } 336 } 337 else { 338 339 outer:while(results.hasMoreElements()) { 340 SearchResult resultRow = (SearchResult)results.next(); 341 if(row++<startrow)continue; 342 343 Attributes attributesRow = resultRow.getAttributes(); 344 NamingEnumeration rowEnum = attributesRow.getIDs(); 345 while(rowEnum.hasMoreElements()) { 346 int len=qry.addRow(); 347 String name = Caster.toString(rowEnum.next()); 348 Object value=null; 349 350 try { 351 value=attributesRow.get(name).get(); 352 }catch(Exception e) {} 353 354 qry.setAtEL("name",len,name); 355 qry.setAtEL("value",len,value); 356 if(maxrows>0 && len>=maxrows)break outer; 357 } 358 qry.setAtEL("name",qry.size(),"dn"); 359 } 360 } 361 } 362 finally { 363 if(context!=null)context.close(); 364 } 365 366 return qry; 367 } 368 369 private static String[] toStringAttributes(String strAttributes,String delimeter) throws PageException { 370 return List.toStringArrayTrim(List.listToArrayRemoveEmpty(strAttributes,delimeter)); 371 } 372 373 private static Attributes toAttributes(String strAttributes,String delimeter, String separator) throws PageException { 374 String[] arrAttr = toStringAttributes(strAttributes,delimeter); 375 376 377 BasicAttributes attributes = new BasicAttributes(); 378 for(int i=0; i<arrAttr.length; i++) { 379 String strAttr = arrAttr[i]; 380 381 // Type 382 int eqIndex=strAttr.indexOf('='); 383 Attribute attr = new BasicAttribute((eqIndex != -1)?strAttr.substring(0, eqIndex).trim():null); 384 385 // Value 386 String strValue = (eqIndex!=-1)?strAttr.substring( eqIndex+ 1):strAttr; 387 String[] arrValue=List.toStringArray(List.listToArrayRemoveEmpty(strValue,separator)); 388 389 // Fill 390 for(int y=0; y<arrValue.length; y++) { 391 attr.add(arrValue[y]); 392 } 393 attributes.put(attr); 394 } 395 return attributes; 396 397 } 398 399 private String getAttrValueType(String attribute) { 400 int eqIndex=attribute.indexOf("="); 401 if(eqIndex != -1) return attribute.substring(0, eqIndex).trim(); 402 return null; 403 } 404 405 private String[] getAttributesValues(String attribute, String separator) throws PageException { 406 String strValue = attribute.substring(attribute.indexOf("=") + 1); 407 if(strValue.length() == 0) return null; 408 return List.toStringArray(List.listToArrayRemoveEmpty(strValue,separator.equals(", ") ? "," : separator)); 409 } 410 411 }