1
2
3
4
5
6
7 package gov.bnl.gums.ldap;
8
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.Enumeration;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.MissingResourceException;
15 import java.util.Properties;
16 import java.util.PropertyResourceBundle;
17 import java.util.ResourceBundle;
18 import java.util.StringTokenizer;
19 import javax.naming.Name;
20 import javax.naming.NameNotFoundException;
21 import javax.naming.NamingEnumeration;
22 import javax.naming.NamingException;
23 import javax.naming.directory.Attribute;
24 import javax.naming.directory.Attributes;
25 import javax.naming.directory.BasicAttribute;
26 import javax.naming.directory.BasicAttributes;
27 import javax.naming.directory.DirContext;
28 import javax.naming.directory.InitialDirContext;
29 import javax.naming.directory.ModificationItem;
30 import javax.naming.directory.SearchControls;
31 import javax.naming.directory.SearchResult;
32 import javax.naming.ldap.InitialLdapContext;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import gov.bnl.gums.*;
36
37 /***
38 *
39 * @author carcassi
40 */
41 public class LDAPPersistenceFactory implements PersistenceFactory {
42
43 private Log log = LogFactory.getLog(LDAPPersistenceFactory.class);
44 private Log adminLog = LogFactory.getLog(GUMS.resourceAdminLog);
45
46 private String name;
47 private Properties properties;
48 private String defaultGumsOU = "ou=GUMS";
49 private String updateGIDdomains;
50 private List domains;
51 private boolean synchGroups;
52
53
54
55 private List contexts = Collections.synchronizedList(new LinkedList());
56
57 /*** Create a new LDAP DirContext based on the configuration.
58 *
59 * @return a new LDAP DirContext
60 */
61 protected DirContext createLDAPContext() {
62 try {
63 return new InitialLdapContext(properties, null);
64 } catch (NamingException e) {
65 log.warn("Couldn't create LDAP connection: " + e.getMessage() + " - parameters: " + properties, e);
66 throw new RuntimeException("Couldn't create LDAP connection: " + e.getMessage());
67 }
68 }
69
70 /*** Retrieves an LDAP DirContext from the pool, if available and still valid,
71 * or creates a new DirContext if none are found.
72 *
73 * @return an LDAP DirContext
74 */
75 DirContext retrieveContext() {
76 DirContext context;
77 while (contexts.size() != 0) {
78 context = (DirContext) contexts.remove(0);
79 if (isContextValid(context)) {
80 log.trace("Using LDAP connection from pool " + context);
81 return context;
82 }
83 }
84 context = createLDAPContext();
85 log.trace("New LDAP connection created " + context);
86 return context;
87 }
88
89 private boolean skipReleaseContext = false;
90
91 /*** Returns the LDAP DirContext to the pool, so that it can be reused.
92 *
93 * @param context the LDAP context to be returned
94 */
95 void releaseContext(DirContext context) {
96 if (skipReleaseContext) {
97 skipReleaseContext = false;
98 return;
99 }
100 contexts.add(0, context);
101 log.trace("LDAP connection returned to pool " + context);
102 }
103
104 private boolean isContextValid(DirContext context) {
105 try {
106 context.search(getDefaultGumsOU(), "(map=*)", null);
107 return true;
108 } catch (Exception e) {
109 log.trace("Removing stale LDAP connection from pool " + context, e);
110 adminLog.warn("LDAP connection test failed, discarding connection from pool: " + e.getMessage());
111 return false;
112 }
113 }
114
115 /*** Returns a Context ready to be used (taken from the pool).
116 * This is the entry point for the pool, and it can be used
117 * by test cases to prepare the LDAP server.
118 *
119 * @return an LDAP context
120 */
121 public DirContext getLDAPContext() {
122 return retrieveContext();
123 }
124
125
126
127 public UserGroupDB retrieveUserGroupDB(String name) {
128 log.trace("Creating LDAP UserGroupDB '" + name + "'");
129 return new LDAPUserGroupDB(this, name);
130 }
131
132 public ManualUserGroupDB retrieveManualUserGroupDB(String name) {
133 log.trace("Creating LDAP ManualUserGroupDB '" + name + "'");
134 return new LDAPUserGroupDB(this, name);
135 }
136
137 public ManualAccountMapperDB retrieveManualAccountMapperDB(String name) {
138 log.trace("Creating LDAP ManualAccountMapperDB '" + name + "'");
139 return new LDAPMapDB(this, name);
140 }
141
142 public AccountPoolMapperDB retrieveAccountPoolMapperDB(String name) {
143 StringTokenizer tokens = new StringTokenizer(name, ".");
144 if (!tokens.hasMoreTokens()) {
145 log.trace("Creating LDAP AccountPoolMapperDB '" + name + "' (no GIDs)");
146 return new LDAPMapDB(this, name);
147 }
148
149 String pool = tokens.nextToken();
150 if (!tokens.hasMoreTokens()) {
151 log.trace("Creating LDAP AccountPoolMapperDB '" + name + "' (no GIDs)");
152 return new LDAPMapDB(this, name);
153 }
154
155 String group = tokens.nextToken();
156 List secondaryGroups = new ArrayList();
157 while (tokens.hasMoreTokens()) {
158 secondaryGroups.add(tokens.nextToken());
159 }
160
161 log.trace("Creating LDAP AccountPoolMapperDB '" + name + "' primary group '" + group + "' secondary groups '" + secondaryGroups + "'");
162 return new LDAPMapDB(this, pool, group, secondaryGroups);
163 }
164
165
166 LDAPGroupIDAssigner assigner;
167 /*** Returns the gid assigner for the given ldap, with the configured
168 * ldap domains for the factory.
169 *
170 * @return an LDAPGroupIDAssigner preconfigured and ready to use
171 */
172 LDAPGroupIDAssigner retrieveAssigner() {
173 if (assigner == null) {
174 assigner = new LDAPGroupIDAssigner(this, domains);
175 log.trace("New LDAPGroupsIDAssigner created " + assigner);
176 }
177 return assigner;
178 }
179
180
181
182 /*** Returns the name of the factory.
183 *
184 * @return the name of the factory as indicated in the configuration
185 */
186 public String getName() {
187 return name;
188 }
189
190 /*** Changes the name of the factory.
191 *
192 * @param name the name of the factory as indicated in the configuration
193 */
194 public void setName(String name) {
195 this.name = name;
196 }
197
198 /***
199 * Changes the GUMS DN in which the GUMS objects will be placed.
200 * This DN will be relative to the DN as specified in the LDAP url
201 * within the LDAP connection parameters.
202 * @return the GUMS base DN (defaults to "ou=GUMS")
203 */
204 public String getDefaultGumsOU() {
205
206 return this.defaultGumsOU;
207 }
208
209 /***
210 * Changes the GUMS DN in which the GUMS objects will be placed.
211 * This DN will be relative to the DN as specified in the LDAP url
212 * within the LDAP connection parameters.
213 * @param baseDN the GUMS base DN (defaults to "ou=GUMS")
214 */
215 public void setDefaultGumsOU(String defaultGumsOU) {
216
217 this.defaultGumsOU = defaultGumsOU;
218 }
219
220 /***
221 * Changes the list of domains to update when an account of the pool is
222 * assigned. The values required are a comma separated set of domain DNs
223 * in the LDAP server. The domain DNs must be relative to the baseDN
224 * specified in the LDAP url within the LDAP connection parameters.
225 * <p>
226 * If set to null, no gid update will be performed.
227 * @return domains for updating the GID (i.e. "dc=usatlas")
228 */
229 public String getUpdateGIDdomains() {
230 return this.updateGIDdomains;
231 }
232
233 /***
234 * Changes the list of domains to update when an account of the pool is
235 * assigned. The values required are a comma separated set of domain DNs
236 * in the LDAP server. The domain DNs must be relative to the baseDN
237 * specified in the LDAP url within the LDAP connection parameters.
238 * <p>
239 * If set to null, no gid update will be performed.
240 * @param updateGIDdomains domains for updating the GID (i.e. "dc=usatlas")
241 */
242 public void setUpdateGIDdomains(String updateGIDdomains) {
243 assigner = null;
244 this.updateGIDdomains = updateGIDdomains;
245 }
246
247 /***
248 * This property forces the gid update for account pools at every access.
249 * It's handy for when gids gets out of synch.
250 * @return if true gids are updated every time accounts from the pool are returned.
251 */
252 public boolean isSynchGroups() {
253 return this.synchGroups;
254 }
255
256 /***
257 * This property forces the gid update for account pools at every access.
258 * It's handy for when gids gets out of synch.
259 * @param synchGroups if true gids are updated every time accounts from the pool are returned.
260 */
261 public void setSynchGroups(boolean synchGroups) {
262 this.synchGroups = synchGroups;
263 }
264
265 /***
266 * Returns the list of properties to be used to connect to LDAP, that is
267 * to create the JNDI context.
268 * @return a set of JNDI properties
269 */
270 public Properties getProperties() {
271 return this.properties;
272 }
273
274 /***
275 * Returns the list of properties to be used to connect to LDAP, that is
276 * to create the JNDI context.
277 * @param properties a set of JNDI properties
278 */
279 public void setProperties(Properties properties) {
280 if (properties.getProperty("java.naming.factory.initial") == null) {
281 properties.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
282 }
283 this.properties = properties;
284 }
285
286 /***
287 * Retrieves the JNDI properties to be used to create the LDAP context
288 * by looking at the ldap.properties file in the classpath. This is mainly
289 * used for unit testing.
290 */
291 public void setConnectionFromLdapProperties() {
292 try {
293 setProperties(readLdapProperties());
294 } catch (MissingResourceException e) {
295 throw new RuntimeException("Couldn't find LDAP configuration file (ldap.properties)", e);
296 }
297 }
298
299 private Properties readLdapProperties() {
300 log.trace("Retrieving LDAP properties from ldap.properties in the classpath");
301 PropertyResourceBundle prop = (PropertyResourceBundle) ResourceBundle.getBundle("ldap");
302 Properties prop2 = new Properties();
303 Enumeration keys = prop.getKeys();
304 while (keys.hasMoreElements()) {
305 String key = (String) keys.nextElement();
306 prop2.setProperty(key, prop.getString(key));
307 }
308 return prop2;
309 }
310
311
312
313 /*** Creates a new "map=mapName" entry in the LDAP GUMS tree.
314 *
315 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
316 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap")
317 */
318 void createMap(String mapName, String mapDN) {
319 DirContext context = retrieveContext();
320 try {
321 Attributes atts = new BasicAttributes();
322 Attribute oc = new BasicAttribute("objectclass");
323 oc.add("GUMStruct");
324 oc.add("GUMSMap");
325 Attribute map = new BasicAttribute("map", mapName);
326 atts.put(oc);
327 atts.put(map);
328 context.createSubcontext(mapDN , atts);
329 log.trace("Created LDAP map '" + mapName + "' at '" + mapDN + "'");
330 } catch (Exception e) {
331 log.info("LDAPPersistence error - createMap - map '" + mapName + "'", e);
332 throw new RuntimeException("Couldn't create LDAP map '" + mapName + "': " + e.getMessage(), e);
333 } finally {
334 releaseContext(context);
335 }
336 }
337
338 /*** Deletes the "map=mapName" map in the LDAP GUMS tree. Will completely
339 * delete the map.
340 *
341 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
342 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap")
343 */
344 void destroyMap(String mapName, String mapDN) {
345 DirContext context = retrieveContext();
346 try {
347 SearchControls ctrls = new SearchControls();
348 ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
349 NamingEnumeration result = context.search(mapDN, "(objectclass=*)", ctrls);
350 while (result.hasMore()) {
351 SearchResult res = (SearchResult) result.next();
352 if ("".equals(res.getName().trim())) continue;
353 context.destroySubcontext(res.getName() + "," + mapDN);
354 }
355 context.destroySubcontext(mapDN);
356 log.trace("Destroyed LDAP map '" + mapName + "' at '" + mapDN + "'");
357 } catch (Exception e) {
358 log.info("LDAPPersistence error - destroyMap - map '" + mapName + "'", e);
359 throw new RuntimeException("Couldn't destroy LDAP map '" + mapName + "': " + e.getMessage(), e);
360 } finally {
361 releaseContext(context);
362 }
363 }
364
365 /*** Adds a userDN -> account mapping entry in the "map=mapName" LDAP map.
366 *
367 * @param userDN the certificate DN of the user (i.e. "/DC=org/DC=doegrids/OU=People/CN=Gabriele Carcassi 12345")
368 * @param account the account to whith to map the DN (i.e. "carcassi")
369 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
370 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap")
371 */
372 void addMapEntry(String userDN, String account, String mapName, String mapDN) {
373 DirContext context = retrieveContext();
374 try {
375 try {
376 ModificationItem[] mods = new ModificationItem[1];
377 mods[0] = new ModificationItem(context.ADD_ATTRIBUTE,
378 new BasicAttribute("user", userDN));
379 context.modifyAttributes("account=" + account + "," + mapDN, mods);
380 log.trace("Added user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "' (account entry present)");
381 } catch (NameNotFoundException e) {
382 Attributes atts = new BasicAttributes();
383 Attribute oc = new BasicAttribute("objectclass");
384 oc.add("GUMStruct");
385 oc.add("GUMSAccount");
386 Attribute userAtt = new BasicAttribute("user", userDN);
387 Attribute accountAtt = new BasicAttribute("account", account);
388 atts.put(oc);
389 atts.put(userAtt);
390 atts.put(accountAtt);
391 context.createSubcontext("account=" + account + "," + mapDN , atts);
392 log.trace("Added user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "' (account entry created)");
393 }
394 } catch (Exception e) {
395 log.info("LDAPPersistence error - addMapEntry - user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'", e);
396 throw new RuntimeException("Couldn't add mapping to LDAP map - user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
397 } finally {
398 releaseContext(context);
399 }
400 }
401
402 /*** Creates an account in the map "map=mapName", without having a userDN: this is useful
403 * for pools of accounts.
404 *
405 * @param account the account to whith to map the DN (i.e. "grid0001")
406 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
407 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap")
408 */
409 void createAccountInMap(String account, String mapName, String mapDN) {
410 DirContext context = retrieveContext();
411 try {
412 Attributes atts = new BasicAttributes();
413 Attribute oc = new BasicAttribute("objectclass");
414 oc.add("GUMStruct");
415 oc.add("GUMSAccount");
416 Attribute accountAtt = new BasicAttribute("account", account);
417 atts.put(oc);
418 atts.put(accountAtt);
419 context.createSubcontext("account=" + account + "," + mapDN , atts);
420 log.trace("Added account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'");
421 } catch (Exception e) {
422 log.info("LDAPPersistence error - createAccountInMap - account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'", e);
423 throw new RuntimeException("Couldn't add account to LDAP map - account '" + account + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
424 } finally {
425 releaseContext(context);
426 }
427 }
428
429 /*** Removes a userDN -> acount mapping entry in the "map=mapName LDAP map.
430 * It will only remove the user entry, while leaving the account entry.
431 *
432 * @param userDN the certificate DN of the user (i.e. "/DC=org/DC=doegrids/OU=People/CN=Gabriele Carcassi 12345")
433 * @param mapName the name of the map (i.e. "usatlasSpecialMap")
434 * @param mapDN the map DN (i.e. "map=usatlasSpecialMap")
435 * @return false if no mapping was removed
436 */
437 boolean removeMapEntry(String userDN, String mapName, String mapDN) {
438 DirContext context = retrieveContext();
439 boolean deleted = false;
440 try {
441 SearchControls ctrls = new SearchControls();
442 ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
443 NamingEnumeration result = context.search(mapDN, "(user={0})", new Object[] {userDN}, ctrls);
444 while (result.hasMore()) {
445 SearchResult res = (SearchResult) result.next();
446 if ("".equals(res.getName().trim())) continue;
447 ModificationItem[] mods = new ModificationItem[1];
448 mods[0] = new ModificationItem(context.REMOVE_ATTRIBUTE,
449 new BasicAttribute("user", userDN));
450 context.modifyAttributes(res.getName() + "," + mapDN, mods);
451 deleted = true;
452 log.trace("Removed map entry - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "'");
453 }
454 return deleted;
455 } catch (Exception e) {
456 log.info("LDAPPersistence error - removeMapEntry - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "'", e);
457 throw new RuntimeException("Couldn't remove map entry from LDAP map - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
458 } finally {
459 releaseContext(context);
460 }
461 }
462
463
464
465 /*** Creates a new "group=groupName" entry in the LDAP GUMS tree.
466 *
467 * @param groupName the name of the group (i.e. "usatlas")
468 * @param groupDN the group DN (i.e. "group=usatlas")
469 */
470 void createUserGroup(String groupName, String groupDN) {
471 DirContext context = retrieveContext();
472 try {
473 Attributes atts = new BasicAttributes();
474 Attribute oc = new BasicAttribute("objectclass");
475 oc.add("GUMStruct");
476 oc.add("GUMSGroup");
477 Attribute group = new BasicAttribute("group", groupName);
478 atts.put(oc);
479 atts.put(group);
480 context.createSubcontext(groupDN , atts);
481 log.trace("Created user group '" + groupName + "' at '" + groupDN + "'");
482 } catch (Exception e) {
483 log.info("LDAPPersistence error - createUserGroup - group '" + groupName + "'", e);
484 throw new RuntimeException("Couldn't create LDAP group '" + groupName + "': " + e.getMessage(), e);
485 } finally {
486 releaseContext(context);
487 }
488 }
489
490 /*** Adds a certificate DN to the group "group=groupName".
491 *
492 * @param userDN the certificate DN of the user (i.e. "/DC=org/DC=doegrids/OU=People/CN=Gabriele Carcassi 12345")
493 * @param groupName the name of the group (i.e. "usatlas")
494 * @param groupDN the group DN (i.e. "group=usatlas")
495 */
496 void addUserGroupEntry(String userDN, String groupName, String groupDN) {
497 DirContext context = retrieveContext();
498 try {
499 ModificationItem[] mods = new ModificationItem[1];
500 mods[0] = new ModificationItem(context.ADD_ATTRIBUTE,
501 new BasicAttribute("user", userDN));
502 context.modifyAttributes(groupDN, mods);
503 log.trace("Added user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'");
504 } catch (Exception e) {
505 log.info("LDAPPersistence error - addUserGroupEntry - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'", e);
506 throw new RuntimeException("Couldn't add user to LDAP group - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "': " + e.getMessage(), e);
507 } finally {
508 releaseContext(context);
509 }
510 }
511
512 /*** Removes a certificate DN to the group "group=groupName".
513 *
514 * @param userDN the certificate DN of the user (i.e. "/DC=org/DC=doegrids/OU=People/CN=Gabriele Carcassi 12345")
515 * @param groupName the name of the group (i.e. "usatlas")
516 * @param groupDN the group DN (i.e. "group=usatlas")
517 */
518 void removeUserGroupEntry(String userDN, String groupName, String groupDN) {
519 DirContext context = retrieveContext();
520 try {
521 ModificationItem[] mods = new ModificationItem[1];
522 mods[0] = new ModificationItem(context.REMOVE_ATTRIBUTE,
523 new BasicAttribute("user", userDN));
524 context.modifyAttributes(groupDN, mods);
525 log.trace("Removed user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'");
526 } catch (Exception e) {
527 log.info("LDAPPersistence error - removeUserGroupEntry - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'", e);
528 throw new RuntimeException("Couldn't remove user to LDAP group - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "': " + e.getMessage(), e);
529 } finally {
530 releaseContext(context);
531 }
532 }
533
534
535
536 private String findGID(String domain, String groupname) {
537 DirContext context = retrieveContext();
538 try {
539 DirContext subcontext = getDomainContext(context, domain);
540 NamingEnumeration result = subcontext.search("ou=Group", "(cn={0})", new Object[] {groupname}, null);
541 String gid = null;
542 if (result.hasMore()) {
543 SearchResult item = (SearchResult) result.next();
544 Attributes atts = item.getAttributes();
545 Attribute gidAtt = atts.get("gidNumber");
546 if (gidAtt != null) {
547 gid = (String) gidAtt.get();
548 }
549 }
550 log.trace("Found gid '" + gid + "' for group '" + groupname + "' at '" + domain + "'");
551 return gid;
552 } catch (Exception e) {
553 log.info("Couldn't retrieve gid for '" + groupname + "' at '" + domain + "'", e);
554 throw new RuntimeException("Couldn't retrieve gid for '" + groupname + "' at '" + domain + "': " + e.getMessage(), e);
555 } finally {
556 releaseContext(context);
557 }
558 }
559
560 private void updateGID(String domain, String username, String gid) {
561 DirContext context = retrieveContext();
562 try {
563 DirContext subcontext = getDomainContext(context, domain);
564 ModificationItem[] mods = new ModificationItem[1];
565 mods[0] = new ModificationItem(subcontext.REPLACE_ATTRIBUTE,
566 new BasicAttribute("gidNumber", gid));
567
568 subcontext.modifyAttributes("uid="+username+",ou=People", mods);
569 log.trace("Changed primary gid for user '" + username + "' to gid '" + gid + "' at '" + domain + "'");
570 } catch (Exception e) {
571 log.info("Couldn't change gid for user '" + username + "' to gid '" + gid + "' at '" + domain + "'", e);
572 throw new RuntimeException("Couldn't change gid for user '" + username + "' to gid '" + gid + "' at '" + domain + "': " + e.getMessage(), e);
573 } finally {
574 releaseContext(context);
575 }
576 }
577
578 /*** Changes the primary gid for the given username. It expects the base DN
579 * provided by the LDAP connection url to point to a domain (i.e. "dc-usatlas,dc=bnl,dc=gov")
580 * that will contain a "ou=People" and a "ou=Group" subtree.
581 *
582 * @param username the username to change the primary group (i.e. "carcassi")
583 * @param groupname the primary group name (i.e. "usatlas")
584 */
585 public void changeGroupID(String username, String groupname) {
586 String gid = findGID(null, groupname);
587 if (gid == null) {
588 throw new RuntimeException("GID for group '" + groupname + "' wasn't found.");
589 }
590 updateGID(null, username, gid);
591 }
592
593 /*** Changes the primary gid for the given username. It expects the domain to be
594 * relative to the base DN
595 * provided by the LDAP connection url and to point to a domain (i.e. "dc-usatlas,dc=bnl,dc=gov")
596 * that will contain a "ou=People" and a "ou=Group" subtree.
597 *
598 * @param domain the domain DN relative to be base DN
599 * @param username the username to change the primary group (i.e. "carcassi")
600 * @param groupname the primary group name (i.e. "usatlas")
601 */
602 public void changeGroupID(String domain, String username, String groupname) {
603 String gid = findGID(domain, groupname);
604 if (gid == null) {
605 throw new RuntimeException("GID for group '" + groupname + "' wasn't found.");
606 }
607 updateGID(domain, username, gid);
608 }
609
610 /*** Adds the username to the given secondary group. It expects the base DN
611 * provided by the LDAP connection url to point to a domain (i.e. "dc-usatlas,dc=bnl,dc=gov")
612 * that will contain a "ou=People" and a "ou=Group" subtree.
613 *
614 * @param username the username to add to the secondary group (i.e. "carcassi")
615 * @param groupname the secondary group name (i.e. "usatlas")
616 */
617 public void addToSecondaryGroup(String username, String groupname) {
618 addToSecondaryGroup(null, username, groupname);
619 }
620
621 /*** Adds the username to the given secondary group. It expects the domain to be
622 * relative to the base DN
623 * provided by the LDAP connection url and to point to a domain (i.e. "dc-usatlas,dc=bnl,dc=gov")
624 * that will contain a "ou=People" and a "ou=Group" subtree.
625 *
626 * @param domain the domain DN relative to be base DN
627 * @param username the username to add to the secondary group (i.e. "carcassi")
628 * @param groupname the secondary group name (i.e. "usatlas")
629 */
630 public void addToSecondaryGroup(String domain, String username, String groupname) {
631 DirContext context = retrieveContext();
632 try {
633 DirContext subcontext = getDomainContext(context, domain);
634 SearchControls ctrls = new SearchControls();
635 ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
636 NamingEnumeration result = subcontext.search("cn=usatlas, ou=Group", "(memberUid={0})", new Object[] {username}, ctrls);
637 if (result.hasMore()) return;
638 ModificationItem[] mods = new ModificationItem[1];
639 mods[0] = new ModificationItem(subcontext.ADD_ATTRIBUTE,
640 new BasicAttribute("memberUid", username));
641
642 subcontext.modifyAttributes("cn="+groupname+",ou=Group", mods);
643 log.trace("Added secondary group to user - user '" + username + "' to group '" + groupname + "' at '" + domain + "'");
644 } catch (Exception e) {
645 log.info("Couldn't add user to secondary group - user '" + username + "' to group '" + groupname + "' at '" + domain + "'", e);
646 throw new RuntimeException("Couldn't add user to secondary group - user '" + username + "' to group '" + groupname + "' at '" + domain + "': " + e.getMessage(), e);
647 } finally {
648 releaseContext(context);
649 }
650 }
651
652 private DirContext getDomainContext(DirContext context, String domain) throws Exception {
653 if (domain == null) return context;
654 return (DirContext) context.lookup(domain);
655 }
656
657 }