View Javadoc

1   /*
2    * LDAPMapDB.java
3    *
4    * Created on September 23, 2005, 11:29 AM
5    *
6    * To change this template, choose Tools | Options and locate the template under
7    * the Source Creation and Management node. Right-click the template and choose
8    * Open. You can then make changes to the template in the Source Editor.
9    */
10  
11  package gov.bnl.gums.db;
12  
13  import gov.bnl.gums.GUMS;
14  import gov.bnl.gums.GridUser;
15  import gov.bnl.gums.persistence.LDAPPersistenceFactory;
16  
17  import java.util.Collections;
18  import java.util.HashMap;
19  import java.util.Hashtable;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.naming.NameAlreadyBoundException;
25  import javax.naming.NamingEnumeration;
26  import javax.naming.directory.*;
27  import javax.naming.ldap.Control;
28  import javax.naming.ldap.LdapContext;
29  import javax.naming.ldap.PagedResultsControl;
30  
31  import org.apache.log4j.Logger;
32  
33  /**
34   * 
35   * @author Gabriele Carcassi, Jay Packard
36   */
37  public class LDAPAccountMapperDB implements AccountPoolMapperDB, ManualAccountMapperDB {
38  	private Logger log = Logger.getLogger(LDAPAccountMapperDB.class);
39  	private LDAPPersistenceFactory factory;
40  	private String map;
41  	private String mapDN;
42  	private String group;
43  	private List secondaryGroups;
44  	private static Map needsCacheRefresh = Collections.synchronizedMap(new HashMap());
45  
46  	/**
47  	 * Creates a new LDAP map, named "map=map" in the defaultGumsOU.
48  	 * 
49  	 * @param factory the LDAP factory that will provide LDAP connectivity
50  	 * @param map the name of the map
51  	 */
52  	public LDAPAccountMapperDB(LDAPPersistenceFactory factory, String map) {
53  		this.factory = factory;
54  		this.map = map;
55  		this.mapDN = "map=" + map + "," + factory.getGumsObject();
56  		createGroupIfNotExists();
57  		log.trace("LDAPMapDB object create: map '" + map + "' factory "
58  				+ factory);
59  	}
60  
61  	/**
62  	 * Creates a new LDAP map, named "map=map" in the defaultGumsOU. When
63  	 * accounts are assigned they will be associated with the gids for the UNIX
64  	 * groups given.
65  	 * 
66  	 * @param factorythe LDAP factory that will provide LDAP connectivity
67  	 * @param mapthe name of the map
68  	 * @param groupthe UNIX primary group for the accounts assigned
69  	 * @param secondaryGroups the UNIX secondary groups for the accounts assigned
70  	 */
71  	public LDAPAccountMapperDB(LDAPPersistenceFactory factory, String map, String group, List secondaryGroups) {
72  		this(factory, map);
73  		this.group = group;
74  		this.secondaryGroups = secondaryGroups;
75  		if (group==null)
76  			log.info("No primary group: factory '" + factory + "'");
77  		log.trace("LDAPMapDB object create: map '" + map + "' factory "
78  				+ factory + " primary group '" + group + "' secondary groups '"
79  				+ secondaryGroups + "'");
80  	}
81  
82  	public void addAccount(String account) {
83  		try {
84  			factory.createAccountInMap(account, map, mapDN);
85  			setNeedsCacheRefresh(true);
86  		} catch (Exception e) {
87  			if (e.getCause() instanceof NameAlreadyBoundException) {
88  				throw new IllegalArgumentException("Account '" + account
89  						+ "' is already present in LDAP pool '" + map + "'");
90  			}
91  		}
92  	}
93  	
94  	public String assignAccount(GridUser user) {
95  		DirContext context = factory.retrieveGumsDirContext();
96  		String account = null;
97  		Control[] controlsBackup = null;
98  		String userDN = user.getCertificateDN();
99  		String email = user.getEmail();
100 		try {
101 			LdapContext ldapContext = (LdapContext) context;
102 			controlsBackup = ldapContext.getRequestControls();
103 			ldapContext.setRequestControls(new Control[] { new PagedResultsControl(	100, Control.CRITICAL) });
104 			SearchControls ctrls = new SearchControls();
105 			ctrls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
106 			NamingEnumeration result = context.search(mapDN, "(!(user=*))", null, ctrls);
107 			while (result.hasMore()) {
108 				SearchResult res = (SearchResult) result.next();
109 				Attributes atts = res.getAttributes();
110 				Attribute accounts = atts.get("account");
111 				if (accounts != null) {
112 					String newAccount = (String) accounts.get();
113 					if (account == null) {
114 						account = newAccount;
115 					} else {
116 						if (account.compareTo(newAccount) > 0) {
117 							account = newAccount;
118 						}
119 					}
120 				}
121 			}
122 		} catch (Exception e) {
123 			log.error("Couldn't assign account from LDAP map '" + map
124 					+ "' to user '" + userDN + "'", e);
125 			throw new RuntimeException(
126 					"Couldn't assign account from LDAP map '" + map
127 							+ "' to user '" + userDN + "': " + e.getMessage(),
128 					e);
129 		} finally {
130 			if (controlsBackup != null) {
131 				try {
132 					((LdapContext) context).setRequestControls(controlsBackup);
133 				} catch (Exception e) {
134 					log.error("Couldn't reset controls", e);
135 				}
136 			}
137 			factory.releaseContext(context);
138 		}
139 		
140 		if (account != null) {
141 			if (group!=null) {
142 				assignGroups(account, group, secondaryGroups);
143 				log.trace("Assigned gids for user '" + userDN + "' account '" + account + "'");
144 			}
145 			if (email!=null) {
146 				assignEmail(account, email);
147 				log.trace("Assigned email for account '" + account + "' to email '" + email + "'");
148 			}
149 			factory.addMapEntry(userDN, account, map, mapDN);
150 			log.trace("Assigned account for LDAP map '" + map + "' user '"
151 					+ userDN + "' account '" + account + "'");
152 		} else {
153 			log.trace("No account to assign for LDAP map '" + map + "' user '"
154 					+ userDN + "' account '" + account + "'");
155 		}
156 		setNeedsCacheRefresh(true);
157 		
158 		return account;
159 	}
160 	
161 	public void createGroupIfNotExists() {
162 		if (!doesMapExist()) {
163 			factory.createMap(map, mapDN);
164 			log.trace("LDAP group '" + map + "' didn't exist, and it was created");
165 			setNeedsCacheRefresh(true);
166 		}
167 	}
168 	
169 	public void createMapping(String userDN, String account) {
170 		factory.addMapEntry(userDN, account, map, mapDN);
171 		setNeedsCacheRefresh(true);
172 	}
173 
174 	public boolean doesMapExist() {
175 		DirContext context = factory.retrieveGumsDirContext();
176 		try {
177 			SearchControls ctrls = new SearchControls();
178 			ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
179 			log.trace("Checking if LDAP map '" + map + "' exists");
180 			NamingEnumeration result = context.search(factory.getGumsObject(), "(map={0})", new Object[] { map },
181 					ctrls);
182 			return result.hasMore();
183 		} catch (Exception e) {
184 			log.error("Couldn't determine if LDAP map exists '" + map + "'", e);
185 			throw new RuntimeException(
186 					"Couldn't determine if LDAP map exists '" + map + "': "
187 							+ e.getMessage(), e);
188 		} finally {
189 			factory.releaseContext(context);
190 		}
191 	}
192 
193     public String getMap() {
194     	return map;
195     }
196 	
197 	public boolean needsCacheRefresh() {
198 		if (needsCacheRefresh.get(map) != null)
199 			return ((Boolean)needsCacheRefresh.get(map)).booleanValue();
200 		else
201 			return true;
202 	}
203 
204 	public boolean removeAccount(String account) {
205 		try {
206 			boolean retVal = factory.destroyAccountInMap(account, map, mapDN);
207 			setNeedsCacheRefresh(true);
208 			return retVal;
209 		} catch (RuntimeException e) {
210 			if (e.getCause() instanceof NameAlreadyBoundException)
211 				throw new IllegalArgumentException("Cannot remove '" + account + "' from LDAP pool '" + map + "'");
212 			throw e;
213 		}
214 	}
215 
216 	public boolean removeMapping(String userDN) {
217 		try {
218 			boolean retVal = factory.removeMapEntry(userDN, map, mapDN);
219 			setNeedsCacheRefresh(true);
220 			return retVal;
221 		} catch (RuntimeException e) {
222 			if (e.getCause() instanceof NoSuchAttributeException)
223 				return false;
224 			throw e;
225 		}
226 	}
227 
228 	public String retrieveAccount(GridUser user) {
229 		String userDN = user.getCertificateDN();
230 		String account = retrieveMapping(userDN);
231 		log.trace("Retrieving account from LDAP map '" + map + "' for user '"
232 				+ userDN + "' account '" + account + "'");
233 		if (account != null) {
234 			reassignGroups(account, group, secondaryGroups);
235 			reassignEmail(account, user.getEmail());
236 			log.trace("Reassigned gids for user '" + userDN + "' account '"
237 					+ account + "'");
238 		}
239 		return account;
240 	}
241 
242 	public java.util.Map retrieveAccountMap() {
243 		DirContext context = factory.retrieveGumsDirContext();
244 		Map map = new Hashtable();
245 		try {
246 			SearchControls ctrls = new SearchControls();
247 			ctrls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
248 			NamingEnumeration result = context.search(mapDN, "(user=*)", null,
249 					ctrls);
250 			while (result.hasMore()) {
251 				SearchResult res = (SearchResult) result.next();
252 				Attributes atts = res.getAttributes();
253 				Attribute accounts = atts.get("account");
254 				if (accounts != null) {
255 					String account = (String) accounts.get();
256 					Attribute users = atts.get("user");
257 					String user = (String) users.get();
258 					map.put(user, account);
259 				}
260 			}
261 			log.trace("Retrieved LDAP map '" + map + "'");
262 			return map;
263 		} catch (Exception e) {
264 			log.error("Couldn't retrieve LDAP map '" + map + "'", e);
265 			throw new RuntimeException("Couldn't retrieve LDAP map '" + map
266 					+ "': " + e.getMessage(), e);
267 		} finally {
268 			factory.releaseContext(context);
269 		}
270 	}
271 
272 	public String retrieveMapping(String userDN) {
273 		DirContext context = factory.retrieveGumsDirContext();
274 		try {
275 			SearchControls ctrls = new SearchControls();
276 			ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
277 			NamingEnumeration result = context.search(mapDN, "(user={0})",
278 					new Object[] { userDN }, ctrls);
279 			if (result.hasMore()) {
280 				SearchResult res = (SearchResult) result.next();
281 				Attributes atts = res.getAttributes();
282 				Attribute map = atts.get("account");
283 				if (map == null)
284 					return null;
285 				String account = (String) map.get();
286 				log.trace("Retrieved map entry in map '" + map + "' for user '"
287 						+ userDN + "' to account '" + account + "'");
288 				return account;
289 			}
290 			return null;
291 		} catch (Exception e) {
292 			log.error("Couldn't retrieve entry from LDAP map '" + map
293 					+ "' for user '" + userDN + "'", e);
294 			throw new RuntimeException(
295 					"Couldn't retrieve entry from LDAP map '" + map
296 							+ "' for user '" + userDN + "': " + e.getMessage(),
297 					e);
298 		} finally {
299 			factory.releaseContext(context);
300 		}
301 	}
302 
303 	public java.util.Map retrieveReverseAccountMap() {
304 		DirContext context = factory.retrieveGumsDirContext();
305 		Map map = new Hashtable();
306 		try {
307 			SearchControls ctrls = new SearchControls();
308 			ctrls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
309 			NamingEnumeration result = context.search(mapDN, "(account=*)",
310 					null, ctrls);
311 			while (result.hasMore()) {
312 				SearchResult res = (SearchResult) result.next();
313 				Attributes atts = res.getAttributes();
314 				Attribute accounts = atts.get("account");
315 				String account = (String) accounts.get();
316 				Attribute users = atts.get("user");
317 				String user = users!=null ? (String) users.get() : "";
318 				map.put(account, user);
319 			}
320 			log.trace("Retrieved LDAP map '" + map + "'");
321 			return map;
322 		} catch (Exception e) {
323 			log.error("Couldn't retrieve LDAP map '" + map + "'", e);
324 			throw new RuntimeException("Couldn't retrieve LDAP map '" + map
325 					+ "': " + e.getMessage(), e);
326 		} finally {
327 			factory.releaseContext(context);
328 		}
329 	}
330 
331 	public java.util.List retrieveUsersNotUsedSince(java.util.Date date) {
332 		throw new UnsupportedOperationException(
333 				"retrieveUsersNotUsedSince is not supported anymore");
334 	}
335 	
336 	public String retrieveEmail(String account) {
337 		return factory.retrieveEmail(account);
338 	}
339 
340 	public synchronized void setCacheRefreshed() {
341 		needsCacheRefresh.put(map, new Boolean(false));
342 	}
343 
344 	public void unassignAccount(String account) {
345 		DirContext context = factory.retrieveGumsDirContext();
346 		try {
347 			SearchControls ctrls = new SearchControls();
348 			ctrls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
349 			NamingEnumeration result = context.search(mapDN, "(account={0})", new Object[] { account }, ctrls);
350 			while (result.hasMore()) {
351 				SearchResult res = (SearchResult) result.next();
352 				Attributes atts = res.getAttributes();
353 				Attribute users = atts.get("user");
354 				if (users!=null) {
355 					String user = (String) users.get();
356 					factory.removeMapEntry(user, map, mapDN);
357 				}
358 			}
359 			setNeedsCacheRefresh(true);
360 			log.trace("Unassigned account '" + account + "' at LDAP map '" + map + "'");
361 		} catch (Exception e) {
362 			log.error("Unassigned account '" + account + "' at LDAP map '" + map + "'", e);
363 			throw new RuntimeException("Couldn't retrieve LDAP map '" + map
364 					+ "': " + e.getMessage(), e);
365 		} finally {
366 			factory.releaseContext(context);
367 		}
368 	}
369 	
370 
371 	public void unassignUser(String userDN) {
372 		factory.removeMapEntry(userDN, map, mapDN);
373 		setNeedsCacheRefresh(true);
374 	}
375     
376     /**
377      * Assigns the groups to the account.
378      * 
379      * @param account A UNIX account (i.e. 'carcassi')
380      * @param primary A UNIX group name (i.e. 'usatlas')
381      * @param secondary A list of Strings representing secondary UNIX group names
382      */
383 	private void assignGroups(String account, String primaryGroup, List secondaryGroups) {
384         try {
385         	factory.changeGroupID(account, primaryGroup);
386             log.trace("Assigned '" + primaryGroup + "' to '" + account + "'");
387             if (secondaryGroups == null) return;
388             Iterator iter = secondaryGroups.iterator();
389             while (iter.hasNext()) {
390                 String group = (String) iter.next();
391                 factory.addToSecondaryGroup(account, group);
392                 log.trace("Assigned secondary group '" + group + "' to '" + account + "'");
393             }
394         } catch (Exception e) {
395             log.warn("Couldn't assign GIDs. account '" + account + "' - primary group '" + primaryGroup + "' - secondary '" + secondaryGroups + "'", e);
396             throw new RuntimeException("Couldn't assign GIDs: " + e.getMessage() + ". account '" + account + "' - primary group '" + primaryGroup + "' - secondary '" + secondaryGroups + "' - " + e.getMessage());
397         }
398     }
399 	
400     /**
401      * Assigns the email to an account.
402      * 
403      * @param account A UNIX account (i.e. 'carcassi')
404      * @param email
405      */
406 	private void assignEmail(String account, String email) {
407         try {
408         	factory.changeEmail(account, email);
409             log.trace("Assigned '" + email + "' to '" + account + "'");
410         } catch (Exception e) {
411             log.warn("Couldn't assign email. account '" + account + "' - email '" + email + "'", e);
412             throw new RuntimeException("Couldn't assign email: " + e.getMessage() + ". account '" + account + "' - email '" + email + "' - " + e.getMessage());
413         }
414     }
415     
416     /**
417      * Reassigns the groups to the account, refreshing something that should be
418      * already be present in LDAP. The LDAP factory controls whether this
419      * actually is performed by setting the synchGroups property.
420      * 
421      * @param account A UNIX account (i.e. 'carcassi')
422      * @param primary A UNIX group name (i.e. 'usatlas')
423      * @param secondary A list of Strings representing secondary UNIX group names
424      */
425 	private void reassignGroups(String account, String primary, List secondary) {
426         if (factory.isSynch()) {
427             assignGroups(account, primary, secondary);
428         } else {
429             log.trace("Skip reassign groups for account '" + account + "' - primary group '" + primary + "' - secondary '" + secondary + "'");
430         }
431     }
432 	
433     /**
434      * Reassigns the email to the account, refreshing something that should be
435      * already be present in LDAP.
436      * 
437      * @param account A UNIX account (i.e. 'carcassi')
438      * @param email
439      */
440 	private void reassignEmail(String account, String email) {
441         if (factory.isSynch()) {
442             assignEmail(account, email);
443         } else {
444             log.trace("Skip reassign email for account '" + account + "' - email '" + email + "'");
445         }
446     }	
447 	
448     private void setNeedsCacheRefresh(boolean value) {
449     	needsCacheRefresh.put(map, new Boolean(value));
450     }
451 
452 }