View Javadoc

1   
2   /*
3    * LDAPPersistenceFactory.java
4    *
5    * Created on January 21, 2005, 9:37 AM
6    */
7   
8   package gov.bnl.gums.persistence;
9   
10  import gov.bnl.gums.configuration.Configuration;
11  import gov.bnl.gums.db.AccountPoolMapperDB;
12  import gov.bnl.gums.db.LDAPAccountMapperDB;
13  import gov.bnl.gums.db.LDAPUserGroupDB;
14  import gov.bnl.gums.db.ManualAccountMapperDB;
15  import gov.bnl.gums.db.ManualUserGroupDB;
16  import gov.bnl.gums.db.UserGroupDB;
17  import gov.bnl.gums.db.ConfigurationDB;
18  import gov.bnl.gums.db.LDAPConfigurationDB;
19  import gov.bnl.gums.*;
20  
21  import java.io.FileInputStream;
22  import java.io.FileOutputStream;
23  import java.io.InputStream;
24  import java.security.KeyStore;
25  import java.security.cert.CertificateFactory;
26  import java.security.cert.X509Certificate;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.Iterator;
30  import java.util.LinkedList;
31  import java.util.List;
32  import java.util.Properties;
33  import java.util.StringTokenizer;
34  
35  import javax.naming.*;
36  import javax.naming.directory.*;
37  import javax.naming.ldap.InitialLdapContext;
38  
39  import org.apache.log4j.Logger;
40  
41  /**
42   *
43   * @author Gabriele Carcassi, Jay Packard
44   */
45  public class LDAPPersistenceFactory extends PersistenceFactory {
46  	static public String getTypeStatic() {
47  		return "ldap";
48  	}
49  
50  	private Logger log = Logger.getLogger(LDAPPersistenceFactory.class);
51  	private Logger gumsAdminLog = Logger.getLogger(GUMS.gumsAdminLogName);
52  	private boolean synch;
53  	private List contexts = Collections.synchronizedList(new LinkedList());// *** LDAP connection pool management    
54  	private boolean skipReleaseContext = false;
55  	private String trustStore = System.getProperty("java.home")+"/lib/security/cacerts"; // doesn't do anything anymore because it required tomcat restart
56  	private String trustStorePassword = ""; // doesn't do anything anymore because it required tomcat restart
57  	private String caCertFile = ""; // doesn't do anything anymore because it required tomcat restart
58  	private String uidField = "uid";
59  	private String emailField = "mail";
60  	private String memberUidField = "memberUid";
61  	private String gidNumberField = "gidNumber";
62  	private String groupCnField = "cn";
63  	private String peopleTree = "";
64  	private String peopleObject = "ou=People";
65  	private String peopleContext = null;
66  	private String groupTree = "";
67  	private String groupObject = "ou=Group";
68  	private String groupContext = null; 
69  	private String gumsTree = "";
70  	private String gumsObject = "ou=GUMS";
71  	private String gumsContext = null; 
72  
73  	/**
74  	 * Create a new ldap persistence factory.  This empty constructor is needed by the XML Digester.
75  	 */
76  	public LDAPPersistenceFactory() {
77  		super();
78  	}
79  
80  	/**
81  	 * Create a new ldap persistence factory with a configuration.
82  	 * 
83  	 * @param configuration
84  	 */
85  	public LDAPPersistenceFactory(Configuration configuration) {
86  		super(configuration);
87  	}
88  
89  	/**
90  	 * Create a new ldap persistence factory with a configuration and a name.
91  	 * 
92  	 * @param configuration
93  	 * @param name
94  	 */
95  	public LDAPPersistenceFactory(Configuration configuration, String name) {
96  		super(configuration, name);
97  	}
98  
99  	/** 
100 	 * Adds a userDN -> account mapping entry in the "map=mapName" LDAP map.
101 	 * 
102 	 * @param userDN the certificate DN of the user (i.e. "/DC=org/DC=doegrids/OU=People/CN=John Smith")
103 	 * @param account the account to whith to map the DN (i.e. "carcassi")
104 	 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
105 	 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap, ou=GUMS")
106 	 */
107 	public void addMapEntry(String userDN, String account, String mapName, String mapDN) {
108 		DirContext context = retrieveGumsDirContext();
109 		try {
110 			try {
111 				ModificationItem[] mods = new ModificationItem[1];
112 				mods[0] = new ModificationItem(context.ADD_ATTRIBUTE, new BasicAttribute("user", userDN));
113 				context.modifyAttributes("account=" + account + "," + mapDN, mods);
114 				log.trace("Added user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "' (account entry present)");
115 			} catch (NameNotFoundException e) {
116 				Attributes atts = new BasicAttributes();
117 				Attribute oc = new BasicAttribute("objectclass");
118 				oc.add("GUMStruct");
119 				oc.add("GUMSAccount");
120 				Attribute userAtt = new BasicAttribute("user", userDN);
121 				Attribute accountAtt = new BasicAttribute("account", account);
122 				atts.put(oc);
123 				atts.put(userAtt);
124 				atts.put(accountAtt);
125 				context.createSubcontext("account=" + account + "," + mapDN , atts);
126 				log.trace("Added user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "' (account entry created)");
127 			}
128 		} catch (Exception e) {
129 			log.info("LDAPPersistence error - addMapEntry - user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'", e);
130 			throw new RuntimeException("Couldn't add mapping to LDAP map - user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
131 		} finally {
132 			releaseContext(context);
133 		}
134 	}
135 
136 	/** 
137 	 * Adds the account to the given secondary group.
138 	 * 
139 	 * @param account the account to add to the secondary group (i.e. "carcassi")
140 	 * @param groupname the secondary group name (i.e. "usatlas")
141 	 */
142 	public void addToSecondaryGroup(String account, String groupname) {
143 		DirContext context = null;
144 		try {
145 			context = retrieveGroupContext();
146 			SearchControls ctrls = new SearchControls();
147 			ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
148 			NamingEnumeration result;
149 			result = context.search(groupCnField+"="+groupname+","+groupObject, "("+memberUidField+"={0})", new Object[] {account}, ctrls);
150 			if (result.hasMore()) return;
151 			ModificationItem[] mods = new ModificationItem[1];
152 			mods[0] = new ModificationItem(context.ADD_ATTRIBUTE, new BasicAttribute(memberUidField, account));
153 			context.modifyAttributes(groupCnField+"="+groupname+","+groupObject, mods);
154 			log.trace("Added secondary group to user - user '" + account + "' to group '" + groupname + "'");
155 		} catch (Exception e) {
156 			log.info("Couldn't add user to secondary group - user '" + account + "' to group '" + groupname + "'", e);
157 			throw new RuntimeException("Couldn't add user to secondary group - user '" + account + "' to group '" + groupname + "': " + e.getMessage(), e);
158 		} finally {
159 			releaseContext(context);
160 		}
161 	}
162 
163 	/** 
164 	 * Adds a certificate DN to the group "group=groupName".
165 	 * 
166 	 * @param userDN the certificate DN of the user (i.e. "/DC=org/DC=doegrids/OU=People/CN=John Smith")
167 	 * @param groupName the name of the group (i.e. "usatlas")
168 	 * @param groupDN the group DN (i.e. "group=usatlas, ou=GUMS")
169 	 */
170 	public void addUserGroupEntry(String userDN, String groupName, String groupDN) {
171 		DirContext context = retrieveGumsDirContext();
172 		try {
173 			ModificationItem[] mods = new ModificationItem[1];
174 			mods[0] = new ModificationItem(context.ADD_ATTRIBUTE,
175 					new BasicAttribute("user", userDN));
176 			context.modifyAttributes(groupDN, mods);
177 			log.trace("Added user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'");
178 		} catch (Exception e) {
179 			log.info("LDAPPersistence error - addUserGroupEntry - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'", e);
180 			throw new RuntimeException("Couldn't add user to LDAP group - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "': " + e.getMessage(), e);
181 		} finally {
182 			releaseContext(context);
183 		}
184 	}
185 
186 	/** 
187 	 * Changes the email for the given account.
188 	 * 
189 	 * @param account the account to change the primary group (i.e. "carcassi")
190 	 * @param email
191 	 */
192 	public void changeEmail(String account, String email) {
193 		if (emailField!=null && emailField.length()>0 && email!=null && email.length()>0) {
194 			DirContext context = retrievePeopleContext();
195 			try {
196 				ModificationItem[] mods = new ModificationItem[1];
197 				mods[0] = new ModificationItem(context.REPLACE_ATTRIBUTE, new BasicAttribute(emailField, email));
198 				context.modifyAttributes(uidField+"="+account+","+peopleObject, mods);
199 				log.trace("Changed email for user '" + account + "' to email '" + email + "''");
200 			} catch (Exception e) {
201 				log.warn("Couldn't change email for user '" + account + "' to email '" + email + "'", e);
202 				throw new RuntimeException("Couldn't change email for user '" + account + "' to email '" + email + "'", e);
203 			} finally {
204 				releaseContext(context);
205 			}
206 		}
207 	}
208 	
209 	/** 
210 	 * Changes the primary gid for the given account.
211 	 * 
212 	 * @param account the account to change the primary group (i.e. "carcassi")
213 	 * @param groupname the primary group name (i.e. "usatlas")
214 	 */
215 	public void changeGroupID(String account, String groupname) {
216 		try { 
217 			String gid = findGID(groupname);
218 			if (gid == null) {
219 				log.error("GID for group '" + groupname + "' wasn't found.");
220 				throw new RuntimeException("GID for group '" + groupname + "' wasn't found.");
221 			}
222 			updateGID(account, gid);
223 		}
224 		catch(NamingException e) {
225 			log.error(e.getMessage());
226 			throw new RuntimeException(e.getMessage());
227 		}
228 	}	
229 
230 	public PersistenceFactory clone(Configuration configuration) {
231 		LDAPPersistenceFactory persistenceFactory = new LDAPPersistenceFactory(configuration, new String(getName()));
232 		persistenceFactory.setDescription(new String(getDescription()));
233 		persistenceFactory.setStoreConfig(getStoreConfig());
234 //		persistenceFactory.setCaCertFile(getCaCertFile());
235 //		persistenceFactory.setTrustStorePassword(getTrustStorePassword());
236 		persistenceFactory.setUidField(new String(getUidField()));
237 		persistenceFactory.setGroupCnField(new String(getGroupCnField()));
238 		persistenceFactory.setGidNumberField(new String(getGidNumberField()));
239 		persistenceFactory.setMemberUidField(new String(getMemberUidField()));
240 		persistenceFactory.setEmailField(new String(getEmailField()));
241 		persistenceFactory.setGroupTree(new String(getGroupTree()));
242 		persistenceFactory.setPeopleTree(new String(getPeopleTree()));
243 		persistenceFactory.setGumsTree(new String(getGumsTree()));
244 		persistenceFactory.setProperties((Properties)getProperties().clone());
245 		persistenceFactory.setSynch(persistenceFactory.isSynch());
246 		return persistenceFactory;
247 	}
248 
249 	/** 
250 	 * Creates an account in the map "map=mapName", without having a userDN: this is useful
251 	 * for pools of accounts.
252 	 * 
253 	 * @param account the account to whith to map the DN (i.e. "grid0001")
254 	 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
255 	 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap, ou=GUMS")
256 	 */
257 	public void createAccountInMap(String account, String mapName, String mapDN) {
258 		DirContext context = retrieveGumsDirContext();
259 		try {
260 			Attributes atts = new BasicAttributes();
261 			Attribute oc = new BasicAttribute("objectclass");
262 			oc.add("GUMStruct");
263 			oc.add("GUMSAccount");
264 			Attribute accountAtt = new BasicAttribute("account", account);
265 			atts.put(oc);
266 			atts.put(accountAtt);
267 			context.createSubcontext("account=" + account + "," + mapDN , atts);
268 			log.trace("Added account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'");
269 		} catch (Exception e) {
270 			log.info("LDAPPersistence error - createAccountInMap - account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'", e);
271 			throw new RuntimeException("Couldn't add account to LDAP map - account '" + account + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
272 		} finally {
273 			releaseContext(context);
274 		}
275 	}
276 
277 	/** 
278 	 * Creates a new "map=mapName" entry in the LDAP GUMS tree.
279 	 * 
280 	 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
281 	 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap, ou=GUMS")
282 	 */
283 	public void createMap(String mapName, String mapDN) {
284 		DirContext context = retrieveGumsDirContext();
285 		try {
286 			Attributes atts = new BasicAttributes();
287 			Attribute oc = new BasicAttribute("objectclass");
288 			oc.add("GUMStruct");
289 			oc.add("GUMSMap");
290 			Attribute map = new BasicAttribute("map", mapName);
291 			atts.put(oc);
292 			atts.put(map);
293 			context.createSubcontext(mapDN , atts);
294 			log.trace("Created LDAP map '" + mapName + "' at '" + mapDN + "'");
295 		} catch (Exception e) {
296 			log.info("LDAPPersistence error - createMap - map '" + mapName + "'", e);
297 			throw new RuntimeException("Couldn't create LDAP map '" + mapName + "': " + e.getMessage(), e);
298 		} finally {
299 			releaseContext(context);
300 		}
301 	}
302 
303 	/** 
304 	 * Creates a new "group=groupName" entry in the LDAP GUMS tree.
305 	 * 
306 	 * @param groupName the name of the group (i.e. "usatlas")
307 	 * @param groupDN the group DN (i.e. "group=usatlas, ou=GUMS")
308 	 */
309 	public void createUserGroup(String groupName, String groupDN) {
310 		DirContext context = retrieveGumsDirContext();
311 		try {
312 			Attributes atts = new BasicAttributes();
313 			Attribute oc = new BasicAttribute("objectclass");
314 			oc.add("GUMStruct");
315 			oc.add("GUMSGroup");
316 			Attribute group = new BasicAttribute("group", groupName);
317 			atts.put(oc);
318 			atts.put(group);
319 			context.createSubcontext(groupDN , atts);
320 			log.trace("Created user group '" + groupName + "' at '" + groupDN + "'");
321 		} catch (Exception e) {
322 			log.info("LDAPPersistence error - createUserGroup - group '" + groupName + "'", e);
323 			throw new RuntimeException("Couldn't create LDAP group '" + groupName + "': " + e.getMessage(), e);
324 		} finally {
325 			releaseContext(context);
326 		}
327 	}
328 
329 	/** 
330 	 * Deletes the account in map.
331 	 * 
332 	 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
333 	 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap, ou=GUMS")
334 	 */
335 	public boolean destroyAccountInMap(String account, String mapName, String mapDN) {
336 		DirContext context = retrieveGumsDirContext();
337 		try {
338 			context.destroySubcontext("account=" + account + "," + mapDN );
339 			log.trace("Destroyed LDAP map '" + mapName + "' at '" + mapDN + "'");
340 		} catch (Exception e) {
341 			log.info("LDAPPersistence error - destroyMap - map '" + mapName + "'", e);
342 			throw new RuntimeException("Couldn't destroy LDAP map '" + mapName + "': " + e.getMessage(), e);
343 		} finally {
344 			releaseContext(context);
345 		}
346 		return true;
347 	}
348 
349 	/** 
350 	 * Deletes the "map=mapName" map in the LDAP GUMS tree. Will completely
351 	 * delete the map.
352 	 * 
353 	 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
354 	 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap, ou=GUMS")
355 	 */
356 	public void destroyMap(String mapName, String mapDN) {
357 		DirContext context = retrieveGumsDirContext();
358 		try {
359 			SearchControls ctrls = new SearchControls();
360 			ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
361 			NamingEnumeration result = context.search(mapDN, "(objectclass=*)", ctrls);
362 			while (result.hasMore()) {
363 				SearchResult res = (SearchResult) result.next();
364 				if ("".equals(res.getName().trim())) continue;
365 				context.destroySubcontext(res.getName() + "," + mapDN);
366 			}
367 			context.destroySubcontext(mapDN);
368 			log.trace("Destroyed LDAP map '" + mapName + "' at '" + mapDN + "'");
369 		} catch (Exception e) {
370 			log.info("LDAPPersistence error - destroyMap - map '" + mapName + "'", e);
371 			throw new RuntimeException("Couldn't destroy LDAP map '" + mapName + "': " + e.getMessage(), e);
372 		} finally {
373 			releaseContext(context);
374 		}
375 	}    
376 
377 	/* @depricated */
378 	public String getAccountField() {
379 		return uidField;
380 	}
381 
382 	public String getCaCertFile() {
383 		return caCertFile;
384 	}
385 
386 	public String getEmailField() {
387 		return emailField;
388 	}
389 
390 	public String getGidNumberField() {
391 		return gidNumberField;
392 	}
393 
394 	public String getGroupCnField() {
395 		return groupCnField;
396 	}
397 	
398 	public String getGroupField() {
399 		return groupCnField;
400 	}
401 
402 	/* @depricated */
403 	public String getGroupIdField() {
404 		return gidNumberField;
405 	}
406 
407 	public String getGroupTree() {
408 		return groupTree;
409 	}
410 	
411 	public String getGumsObject() {
412 		return gumsObject;
413 	}
414 	
415 	public String getGumsTree() {
416 		return gumsTree;
417 	}
418 
419 	/* @depricated */
420 	public String getMemberAccountField() {
421 		return memberUidField;
422 	}
423 
424 	public String getMemberUidField() {
425 		return memberUidField;
426 	}
427 
428 	public String getPeopleTree() {
429 		return peopleTree;
430 	}
431 
432 	public String getTrustStorePassword() {
433 		return trustStorePassword;
434 	}
435 
436 	public String getType() {
437 		return "ldap";
438 	}
439 
440 	public String getUidField() {
441 		return uidField;
442 	}	
443 	/**
444 	 * This property forces the update for account pools at every access.
445 	 * It's handy for when gid and email gets out of synch.
446 	 * 
447 	 * @return if true information is updated every time accounts from the pool are returned.
448 	 */
449 	public boolean isSynch() {
450 		return this.synch;
451 	}
452 
453 	/*
454 	 * @depricated
455 	 */
456 	public boolean isSynchGroups() {
457 		return this.synch;
458 	}
459 
460 	/** Returns the LDAP DirContext to the pool, so that it can be reused.
461 	 * 
462 	 * @param context the LDAP context to be returned
463 	 */
464 	public void releaseContext(DirContext context) {
465 		if (skipReleaseContext) {
466 			skipReleaseContext = false;
467 			return;
468 		}
469 		contexts.add(0, context);
470 		log.trace("LDAP connection returned to pool " + context);
471 	}
472 
473 	/** Removes a userDN -> acount mapping entry in the "map=mapName LDAP map.
474 	 * It will only remove the user entry, while leaving the account entry.
475 	 * 
476 	 * @param userDN the certificate DN of the user (i.e. "/DC=org/DC=doegrids/OU=People/CN=Gabriele Carcassi 12345")
477 	 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
478 	 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap")
479 	 * @return false if no mapping was removed
480 	 */
481 	public boolean removeMapEntry(String userDN, String mapName, String mapDN) {
482 		DirContext context = retrieveGumsDirContext();
483 		boolean deleted = false;
484 		try {
485 			SearchControls ctrls = new SearchControls();
486 			ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
487 			NamingEnumeration result = context.search(mapDN, "(user={0})", new Object[] {userDN}, ctrls);
488 			while (result.hasMore()) {
489 				SearchResult res = (SearchResult) result.next();
490 				if ("".equals(res.getName().trim())) continue;
491 				ModificationItem[] mods = new ModificationItem[1];
492 				mods[0] = new ModificationItem(context.REMOVE_ATTRIBUTE, new BasicAttribute("user", userDN));
493 				context.modifyAttributes(res.getName() + "," + mapDN, mods);
494 				deleted = true;
495 				log.trace("Removed map entry - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "'");
496 			}
497 			return deleted;
498 		} catch (Exception e) {
499 			log.info("LDAPPersistence error - removeMapEntry - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "'", e);
500 			throw new RuntimeException("Couldn't remove map entry from LDAP map - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
501 		} finally {
502 			releaseContext(context);
503 		}
504 	}
505 
506 	/** Removes a certificate DN to the group "group=groupName".
507 	 * 
508 	 * @param userDN the certificate DN of the user (i.e. "/DC=org/DC=doegrids/OU=People/CN=Gabriele Carcassi 12345")
509 	 * @param groupName the name of the group (i.e. "usatlas")
510 	 * @param groupDN the group DN (i.e. "group=usatlas")
511 	 */
512 	public void removeUserGroupEntry(String userDN, String groupName, String groupDN) {
513 		DirContext context = retrieveGumsDirContext();
514 		try {
515 			ModificationItem[] mods = new ModificationItem[1];
516 			mods[0] = new ModificationItem(context.REMOVE_ATTRIBUTE,
517 					new BasicAttribute("user", userDN));
518 			context.modifyAttributes(groupDN, mods);
519 			log.trace("Removed user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'");
520 		} catch (Exception e) {
521 			log.info("LDAPPersistence error - removeUserGroupEntry - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'", e);
522 			throw new RuntimeException("Couldn't remove user to LDAP group  - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "': " + e.getMessage(), e);
523 		} finally {
524 			releaseContext(context);
525 		}
526 	}
527 	
528 	public AccountPoolMapperDB retrieveAccountPoolMapperDB(String nameAndGroups) {
529 		StringTokenizer tokens = new StringTokenizer(nameAndGroups, ".");
530 		if (!tokens.hasMoreTokens()) {
531 			log.trace("Creating LDAP AccountPoolMapperDB '" + nameAndGroups + "' (no GIDs)");
532 			return new LDAPAccountMapperDB(this, nameAndGroups);
533 		}
534 
535 		String pool = tokens.nextToken();
536 		if (!tokens.hasMoreTokens()) {
537 			log.trace("Creating LDAP AccountPoolMapperDB '" + nameAndGroups + "' (no GIDs)");
538 			return new LDAPAccountMapperDB(this, nameAndGroups);
539 		}
540 
541 		String group = tokens.nextToken();
542 		List secondaryGroups = new ArrayList();
543 		while (tokens.hasMoreTokens()) {
544 			secondaryGroups.add(tokens.nextToken());
545 		}
546 
547 		log.trace("Creating LDAP AccountPoolMapperDB '" + nameAndGroups + "' primary group '" + group + "' secondary groups '" + secondaryGroups + "'");
548 		return new LDAPAccountMapperDB(this, pool, group, secondaryGroups);
549 	}	
550 	
551 	public ConfigurationDB retrieveConfigurationDB() {
552 		log.trace("Creating LDAP ConfigurationDB");
553 		return new LDAPConfigurationDB(this);
554 	}
555 
556 	public String retrieveEmail(String uid) {
557 		DirContext context = retrievePeopleContext();
558 		try {
559 			NamingEnumeration result = context.search(peopleObject, "("+uidField+"={0})", new Object[] {uid}, null);
560 			String email = null;
561 			if (result.hasMore()) {
562 				SearchResult item = (SearchResult) result.next();
563 				Attributes atts = item.getAttributes();
564 				Attribute emailAtt = atts.get(emailField);
565 				if (emailAtt != null) {
566 					email = (String) emailAtt.get();
567 				}
568 			}
569 			log.trace("Found email '" + email + "' for uid '" + uid + "'");
570 			return email;
571 		} catch (Exception e) {
572 			log.info("Couldn't retrieve email for uid '" + uid + "'", e);
573 			throw new RuntimeException("Couldn't retrieve email for account '" + uid + "': " + e.getMessage(), e);
574 		} finally {
575 			releaseContext(context);
576 		}		
577 	}
578 	
579 	public DirContext retrieveGroupContext() {
580 		DirContext context;
581 		while (contexts.size() != 0) {
582 			context = (DirContext) contexts.remove(0);
583 			if (isContextValid(context)) {
584 				log.trace("Using LDAP connection from pool " + context);
585 				return context;
586 			}
587 		}
588 		context = createGroupContext();
589 		log.trace("New LDAP connection created " + context);
590 		return context;	
591 	}
592 
593 	public DirContext retrieveGumsDirContext() {
594 		DirContext context;
595 		while (contexts.size() != 0) {
596 			context = (DirContext) contexts.remove(0);
597 			if (isContextValid(context)) {
598 				log.trace("Using LDAP connection from pool " + context);
599 				return context;
600 			}
601 		}
602 		context = createGumsContext();
603 		log.trace("New LDAP connection created " + context);
604 		return context;
605 	}
606 
607 	public ManualAccountMapperDB retrieveManualAccountMapperDB(String name) {
608 		log.trace("Creating LDAP ManualAccountMapperDB '" + name + "'");
609 		return new LDAPAccountMapperDB(this, name);
610 	}
611 
612 	public ManualUserGroupDB retrieveManualUserGroupDB(String name) {
613 		log.trace("Creating LDAP ManualUserGroupDB '" + name + "'");
614 		return new LDAPUserGroupDB(this, name);
615 	}
616 
617 	public DirContext retrievePeopleContext() {
618 		DirContext context;
619 		while (contexts.size() != 0) {
620 			context = (DirContext) contexts.remove(0);
621 			if (isContextValid(context)) {
622 				log.trace("Using LDAP connection from pool " + context);
623 				return context;
624 			}
625 		}
626 		context = createPeopleContext();
627 		log.trace("New LDAP connection created " + context);
628 		return context;		
629 	}
630 	
631 	public UserGroupDB retrieveUserGroupDB(String name) {
632 		log.trace("Creating LDAP UserGroupDB '" + name + "'");
633 		return new LDAPUserGroupDB(this, name);
634 	}
635 
636 	// @depricated
637 	public void setAccountField(String accountField) {
638 		this.uidField = accountField;
639 	}
640 
641 	public void setCaCertFile(String caCertFile) {
642 		//System.setProperty("javax.net.ssl.trustStore", trustStore );
643 		this.caCertFile = caCertFile;
644 		//if (!trustStorePassword.equals(""))
645 		//addCertToTrustStore();
646 	}
647 	
648 	public void setEmailField(String emailField) {
649 		this.emailField = emailField;
650 	}
651 	
652 	public void setGidNumberField(String gidNumberField) {
653 		this.gidNumberField = gidNumberField;
654 	}
655 
656 	public void setGroupCnField(String groupCnField) {
657 		this.groupCnField = groupCnField;
658 	}
659 	
660 	// @depricated
661 	public void setGroupField(String groupField) {
662 		this.groupCnField = groupField;
663 	}
664 	
665 	// @depricated 
666 	public void setGroupIdField(String groupIdField) {
667 		this.gidNumberField = groupIdField;
668 	}
669 
670 	public void setGroupTree(String groupTree) {
671 		if (groupTree.length()>0) {
672 			this.groupTree = groupTree;
673 			if (groupTree.indexOf(',')!=-1) {
674 				this.groupObject = groupTree.substring(0, groupTree.indexOf(','));
675 				this.groupContext = groupTree.substring(groupTree.indexOf(',')+1);   
676 			}
677 			else
678 				this.groupObject = groupTree;
679 		} 
680 	}
681 	
682 	public void setGumsTree(String gumsTree) {
683 		if (gumsTree.length()>0) {
684 			this.gumsTree = gumsTree;
685 			if (gumsTree.indexOf(',')!=-1) {
686 				this.gumsObject = gumsTree.substring(0, gumsTree.indexOf(','));
687 				this.gumsContext = gumsTree.substring(gumsTree.indexOf(',')+1);   
688 			}
689 			else
690 				this.gumsObject = gumsTree;
691 		} 
692 	}
693 
694 	// @depricated
695 	public void setMemberAccountField(String memberAccountField) {
696 		this.memberUidField = memberAccountField;
697 	}
698 
699 	public void setMemberUidField(String memberUidField) {
700 		this.memberUidField = memberUidField;
701 	}
702 
703 	public void setPeopleTree(String peopleTree) {
704 		if (peopleTree.length()>0) {
705 			this.peopleTree = peopleTree;
706 			if (peopleTree.indexOf(',')!=-1) {
707 				this.peopleObject = peopleTree.substring(0, peopleTree.indexOf(','));
708 				this.peopleContext = peopleTree.substring(peopleTree.indexOf(',')+1);   
709 			}
710 			else
711 				this.peopleObject = peopleTree;
712 		} 
713 	}
714 	/**
715 	 * Sets the list of properties to be used to connect to LDAP, that is
716 	 * to create the JNDI context.
717 	 * 
718 	 * @param properties a set of JNDI properties
719 	 */
720 	public void setProperties(Properties properties) {
721 		if (properties.getProperty(Context.INITIAL_CONTEXT_FACTORY) == null) {
722 			properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
723 		}
724 
725 		// For JUnit test only
726 		/*if (properties.getProperty("caCertFile")!=null) {
727         	setCaCertFile(properties.getProperty("caCertFile"));
728         	properties.remove("caCertFile");
729         }
730         if (properties.getProperty("trustStorePassword")!=null) {
731         	setTrustStorePassword(properties.getProperty("trustStorePassword"));
732         	properties.remove("trustStorePassword");
733         }*/
734 
735 		super.setProperties(properties);
736 	}
737 
738 	/**
739 	 * This property forces the update for account pools at every access.
740 	 * It's handy for when gid or email gets out of synch.
741 	 * @param synchGroups if information is updated every time accounts from the pool are returned.
742 	 */
743 	public void setSynch(boolean synch) {
744 		this.synch = synch;
745 	}
746 
747 	/*
748 	 * @depricated
749 	 */
750 	public void setSynchGroups(boolean synchGroups) {
751 		this.synch = synchGroups;
752 	}
753 
754 	public void setTrustStorePassword(String trustStorePassword) {
755 		//System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword );
756 		this.trustStorePassword = trustStorePassword;
757 		//if (!caCertFile.equals(""))
758 		//addCertToTrustStore();
759 	}
760 
761 	public void setUidField(String uidField) {
762 		this.uidField = uidField;
763 	}
764 
765 	public String toXML() {
766 		String retStr = "\t\t<ldapPersistenceFactory\n"+
767 		"\t\t\tname='"+getName()+"'\n"+
768 		"\t\t\tdescription='"+getDescription()+"'\n"+
769 		"\t\t\tstoreConfig='"+(getStoreConfig()?"true":"false")+"'\n"+
770 		"\t\t\tsynch='"+synch+"'\n"+
771 //		"\t\t\tcaCertFile='"+getCaCertFile()+"'\n"+
772 //		"\t\t\ttrustStorePassword='"+trustStorePassword+"'\n"+
773 		"\t\t\tgidNumberField='"+gidNumberField+"'\n"+
774 		"\t\t\tuidField='"+uidField+"'\n"+
775 		"\t\t\tgroupCnField='"+groupCnField+"'\n"+
776 		"\t\t\tmemberUidField='"+memberUidField+"'\n"+
777 		"\t\t\temailField='"+emailField+"'\n"+
778 		"\t\t\tgroupTree='"+groupTree+"'\n"+
779 		"\t\t\tpeopleTree='"+peopleTree+"'\n"+
780 		"\t\t\tgumsTree='"+gumsTree+"'\n";
781 
782 		Iterator keyIt = getProperties().keySet().iterator();
783 		while(keyIt.hasNext()) {
784 			String key = (String)keyIt.next();
785 			retStr += "\t\t\t"+key+"='"+getProperties().getProperty(key)+"'\n";
786 		}
787 
788 		if (retStr.charAt(retStr.length()-1)=='\n')
789 			retStr = retStr.substring(0, retStr.length()-1);    	
790 
791 		retStr += "/>\n\n";
792 
793 		return retStr;
794 	}
795 
796 /*	private void addCertToTrustStore() {
797 		X509Certificate cert = null;
798 		try {
799 			InputStream inStream = new FileInputStream(caCertFile);
800 			CertificateFactory cf = CertificateFactory.getInstance("X.509");
801 			cert = (X509Certificate)cf.generateCertificate(inStream);
802 			inStream.close();
803 		} catch (Exception e) {
804 			log.error("Cannot open " + caCertFile, e);
805 			gumsAdminLog.error("Cannot open " + caCertFile + ": " + e.getMessage());
806 			return;
807 		}
808 		try {
809 			KeyStore ks = KeyStore.getInstance("jks");
810 			ks.load(new FileInputStream(trustStore), trustStorePassword.toCharArray());
811 			ks.setCertificateEntry("gumsldapda", cert);
812 			FileOutputStream fos = new FileOutputStream(trustStore);
813 			ks.store(fos, trustStorePassword.toCharArray());
814 			fos.close();
815 		} catch (Exception e) {
816 			log.error("Couldn't put " + caCertFile + " into trust store", e);
817 			gumsAdminLog.error("Couldn't put " + caCertFile + " into trust store: " + e.getMessage() );
818 		}
819 	}*/
820 	
821 	private String findGID(String groupname) throws NamingException {
822 		DirContext context = retrieveGroupContext();
823 		try {
824 			NamingEnumeration result = context.search(groupObject, "("+groupCnField+"={0})", new Object[] {groupname}, null);
825 			String gid = null;
826 			if (result.hasMore()) {
827 				SearchResult item = (SearchResult) result.next();
828 				Attributes atts = item.getAttributes();
829 				Attribute gidAtt = atts.get(gidNumberField);
830 				if (gidAtt != null) {
831 					gid = (String) gidAtt.get();
832 				}
833 			}
834 			log.trace("Found gid '" + gid + "' for group '" + groupname + "'");
835 			return gid;
836 		} catch (Exception e) {
837 			log.info("Couldn't retrieve gid for '" + groupname + "'", e);
838 			throw new RuntimeException("Couldn't retrieve gid for '" + groupname + "': " + e.getMessage(), e);
839 		} finally {
840 			releaseContext(context);
841 		}
842 	}
843 
844 	private boolean isContextValid(DirContext context) {
845 		try {
846 			context.search(gumsObject, "(map=*)", null);
847 			return true;
848 		} catch (Exception e) {
849 			log.trace("Removing stale LDAP connection from pool " + context, e);
850 			gumsAdminLog.warn("LDAP connection test failed, discarding connection from pool: " + e.getMessage());
851 			return false;
852 		}
853 	}
854 
855 	private void updateGID(String account, String gid) throws NamingException {
856 		DirContext context = retrievePeopleContext();
857 		try {
858 			ModificationItem[] mods = new ModificationItem[1];
859 			mods[0] = new ModificationItem(context.REPLACE_ATTRIBUTE, new BasicAttribute(gidNumberField, gid));
860 			context.modifyAttributes(uidField+"="+account+","+peopleObject, mods);
861 			log.trace("Changed primary gid for user '" + account + "' to gid '" + gid + "''");
862 		} catch (Exception e) {
863 			log.warn("Couldn't change gid for user '" + account + "' to gid '" + gid + "''", e);
864 			throw new RuntimeException("Couldn't change gid for user '" + account + "' to gid '" + gid + "''", e);
865 		} finally {
866 			releaseContext(context);
867 		}
868 	}
869 
870 	protected DirContext createGroupContext() {
871 		try {
872 			Properties properties = (Properties)getProperties().clone();
873 			if (groupContext!=null)
874 				properties.setProperty("java.naming.provider.url", properties.getProperty("java.naming.provider.url")+"/"+(groupContext!=null?groupContext:""));
875 			log.info("Trying to create LDAP connection with properties: " + properties);
876 			return (DirContext)new InitialLdapContext(properties, null);
877 		} catch (NamingException e) {
878 			log.warn("Couldn't create LDAP connection: " + e.getMessage() + " - parameters: " + getProperties(), e);
879 			throw new RuntimeException("Couldn't create LDAP connection: " + e.getMessage());
880 		}
881 	}
882 
883 	/** Create a new LDAP DirContext based on the configuration.
884 	 * 
885 	 * @return a new LDAP DirContext
886 	 */
887 	protected DirContext createGumsContext() {
888 		try {		 
889 			Properties properties = (Properties)getProperties().clone();
890 			if (gumsContext!=null)
891 				properties.setProperty(Context.PROVIDER_URL, properties.getProperty(Context.PROVIDER_URL)+"/"+(gumsContext!=null?gumsContext:""));
892 			log.info("Trying to create LDAP connection with properties: " + properties);
893 			return (DirContext)new InitialLdapContext(properties, null);
894 		} catch (NamingException e) {
895 			log.warn("Couldn't create LDAP connection: " + e.getMessage() + " - parameters: " + getProperties(), e);
896 			throw new RuntimeException("Couldn't create LDAP connection: " + e.getMessage());
897 		}
898 	}
899 
900 	protected DirContext createPeopleContext() {
901 		try {
902 			Properties properties = (Properties)getProperties().clone();
903 			if (peopleContext!=null)
904 				properties.setProperty(Context.PROVIDER_URL, properties.getProperty(Context.PROVIDER_URL)+"/"+(peopleContext!=null?peopleContext:""));
905 			log.info("Trying to create LDAP connection with properties: " + properties);
906 			return (DirContext)new InitialLdapContext(properties, null);
907 		} catch (NamingException e) {
908 			log.warn("Couldn't create LDAP connection: " + e.getMessage() + " - parameters: " + getProperties(), e);
909 			throw new RuntimeException("Couldn't create LDAP connection: " + e.getMessage());
910 		}
911 	}	
912 
913 }