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