1
2
3
4
5
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.*;
18
19 import java.io.FileInputStream;
20 import java.io.FileOutputStream;
21 import java.io.InputStream;
22 import java.security.KeyStore;
23 import java.security.cert.CertificateFactory;
24 import java.security.cert.X509Certificate;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Iterator;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Properties;
31 import java.util.StringTokenizer;
32
33 import javax.naming.NameNotFoundException;
34 import javax.naming.NamingEnumeration;
35 import javax.naming.NamingException;
36 import javax.naming.directory.Attribute;
37 import javax.naming.directory.Attributes;
38 import javax.naming.directory.BasicAttribute;
39 import javax.naming.directory.BasicAttributes;
40 import javax.naming.directory.DirContext;
41 import javax.naming.directory.ModificationItem;
42 import javax.naming.directory.SearchControls;
43 import javax.naming.directory.SearchResult;
44 import javax.naming.ldap.InitialLdapContext;
45
46 import org.apache.commons.logging.Log;
47 import org.apache.commons.logging.LogFactory;
48
49
50
51
52
53 public class LDAPPersistenceFactory extends PersistenceFactory {
54 private static String gumsOU = "ou=GUMS";
55 static public String getTypeStatic() {
56 return "ldap";
57 }
58
59 private Log log = LogFactory.getLog(LDAPPersistenceFactory.class);
60 private Log adminLog = LogFactory.getLog(GUMS.resourceAdminLog);
61 private boolean synchGroups;
62 private List contexts = Collections.synchronizedList(new LinkedList());
63 private boolean skipReleaseContext = false;
64 private String trustStore = System.getProperty("java.home")+"/lib/security/cacerts";
65 private String trustStorePassword = "";
66 private String caCertFile = "";
67 private String accountField = "uid";
68 private String memberAccountField = "memberUid";
69 private String groupIdField = "gidNumber";
70
71
72
73
74 public LDAPPersistenceFactory() {
75 super();
76 }
77
78
79
80
81
82
83 public LDAPPersistenceFactory(Configuration configuration) {
84 super(configuration);
85 }
86
87
88
89
90
91
92
93 public LDAPPersistenceFactory(Configuration configuration, String name) {
94 super(configuration, name);
95 }
96
97
98
99
100
101
102
103
104
105 public void addMapEntry(String userDN, String account, String mapName, String mapDN) {
106 DirContext context = retrieveContext();
107 try {
108 try {
109 ModificationItem[] mods = new ModificationItem[1];
110 mods[0] = new ModificationItem(context.ADD_ATTRIBUTE, new BasicAttribute("user", userDN));
111 context.modifyAttributes("account=" + account + "," + mapDN, mods);
112 log.trace("Added user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "' (account entry present)");
113 } catch (NameNotFoundException e) {
114 Attributes atts = new BasicAttributes();
115 Attribute oc = new BasicAttribute("objectclass");
116 oc.add("GUMStruct");
117 oc.add("GUMSAccount");
118 Attribute userAtt = new BasicAttribute("user", userDN);
119 Attribute accountAtt = new BasicAttribute("account", account);
120 atts.put(oc);
121 atts.put(userAtt);
122 atts.put(accountAtt);
123 context.createSubcontext("account=" + account + "," + mapDN , atts);
124 log.trace("Added user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "' (account entry created)");
125 }
126 } catch (Exception e) {
127 log.info("LDAPPersistence error - addMapEntry - user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'", e);
128 throw new RuntimeException("Couldn't add mapping to LDAP map - user '" + userDN + "' / account '" + account + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
129 } finally {
130 releaseContext(context);
131 }
132 }
133
134
135
136
137
138
139
140 public void addToSecondaryGroup(String account, String groupname) {
141 DirContext context = retrieveContext();
142 try {
143 SearchControls ctrls = new SearchControls();
144 ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
145 NamingEnumeration result;
146 result = context.search("cn="+groupname+",ou=Group", "("+memberAccountField+"={0})", new Object[] {account}, ctrls);
147 if (result.hasMore()) return;
148 ModificationItem[] mods = new ModificationItem[1];
149 mods[0] = new ModificationItem(context.ADD_ATTRIBUTE, new BasicAttribute(memberAccountField, account));
150 context.modifyAttributes("cn="+groupname+",ou=Group", mods);
151 log.trace("Added secondary group to user - user '" + account + "' to group '" + groupname + "'");
152 } catch (Exception e) {
153 log.info("Couldn't add user to secondary group - user '" + account + "' to group '" + groupname + "'", e);
154 throw new RuntimeException("Couldn't add user to secondary group - user '" + account + "' to group '" + groupname + "': " + e.getMessage(), e);
155 } finally {
156 releaseContext(context);
157 }
158 }
159
160
161
162
163
164
165
166
167 public void addUserGroupEntry(String userDN, String groupName, String groupDN) {
168 DirContext context = retrieveContext();
169 try {
170 ModificationItem[] mods = new ModificationItem[1];
171 mods[0] = new ModificationItem(context.ADD_ATTRIBUTE,
172 new BasicAttribute("user", userDN));
173 context.modifyAttributes(groupDN, mods);
174 log.trace("Added user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'");
175 } catch (Exception e) {
176 log.info("LDAPPersistence error - addUserGroupEntry - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'", e);
177 throw new RuntimeException("Couldn't add user to LDAP group - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "': " + e.getMessage(), e);
178 } finally {
179 releaseContext(context);
180 }
181 }
182
183
184
185
186
187
188
189 public void changeGroupID(String account, String groupname) {
190 String gid = findGID(groupname);
191 if (gid == null) {
192 log.error("GID for group '" + groupname + "' wasn't found.");
193 throw new RuntimeException("GID for group '" + groupname + "' wasn't found.");
194 }
195 updateGID(account, gid);
196 }
197
198 public PersistenceFactory clone(Configuration configuration) {
199 LDAPPersistenceFactory persistenceFactory = new LDAPPersistenceFactory(configuration, new String(getName()));
200 persistenceFactory.setDescription(new String(getDescription()));
201
202
203 persistenceFactory.setAccountField(new String(getAccountField()));
204 persistenceFactory.setGroupIdField(new String(getGroupIdField()));
205 persistenceFactory.setMemberAccountField(new String(getMemberAccountField()));
206 persistenceFactory.setProperties((Properties)getProperties().clone());
207 persistenceFactory.setSynchGroups(persistenceFactory.isSynchGroups());
208 return persistenceFactory;
209 }
210
211
212
213
214
215
216
217
218
219 public void createAccountInMap(String account, String mapName, String mapDN) {
220 DirContext context = retrieveContext();
221 try {
222 Attributes atts = new BasicAttributes();
223 Attribute oc = new BasicAttribute("objectclass");
224 oc.add("GUMStruct");
225 oc.add("GUMSAccount");
226 Attribute accountAtt = new BasicAttribute("account", account);
227 atts.put(oc);
228 atts.put(accountAtt);
229 context.createSubcontext("account=" + account + "," + mapDN , atts);
230 log.trace("Added account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'");
231 } catch (Exception e) {
232 log.info("LDAPPersistence error - createAccountInMap - account '" + account + "' to map '" + mapName + "' at '" + mapDN + "'", e);
233 throw new RuntimeException("Couldn't add account to LDAP map - account '" + account + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
234 } finally {
235 releaseContext(context);
236 }
237 }
238
239
240
241
242
243
244
245 public void createMap(String mapName, String mapDN) {
246 DirContext context = retrieveContext();
247 try {
248 Attributes atts = new BasicAttributes();
249 Attribute oc = new BasicAttribute("objectclass");
250 oc.add("GUMStruct");
251 oc.add("GUMSMap");
252 Attribute map = new BasicAttribute("map", mapName);
253 atts.put(oc);
254 atts.put(map);
255 context.createSubcontext(mapDN , atts);
256 log.trace("Created LDAP map '" + mapName + "' at '" + mapDN + "'");
257 } catch (Exception e) {
258 log.info("LDAPPersistence error - createMap - map '" + mapName + "'", e);
259 throw new RuntimeException("Couldn't create LDAP map '" + mapName + "': " + e.getMessage(), e);
260 } finally {
261 releaseContext(context);
262 }
263 }
264
265
266
267
268
269
270
271 public void createUserGroup(String groupName, String groupDN) {
272 DirContext context = retrieveContext();
273 try {
274 Attributes atts = new BasicAttributes();
275 Attribute oc = new BasicAttribute("objectclass");
276 oc.add("GUMStruct");
277 oc.add("GUMSGroup");
278 Attribute group = new BasicAttribute("group", groupName);
279 atts.put(oc);
280 atts.put(group);
281 context.createSubcontext(groupDN , atts);
282 log.trace("Created user group '" + groupName + "' at '" + groupDN + "'");
283 } catch (Exception e) {
284 log.info("LDAPPersistence error - createUserGroup - group '" + groupName + "'", e);
285 throw new RuntimeException("Couldn't create LDAP group '" + groupName + "': " + e.getMessage(), e);
286 } finally {
287 releaseContext(context);
288 }
289 }
290
291
292
293
294
295
296
297 public boolean destroyAccountInMap(String account, String mapName, String mapDN) {
298 DirContext context = retrieveContext();
299 try {
300 context.destroySubcontext("account=" + account + "," + mapDN );
301 log.trace("Destroyed LDAP map '" + mapName + "' at '" + mapDN + "'");
302 } catch (Exception e) {
303 log.info("LDAPPersistence error - destroyMap - map '" + mapName + "'", e);
304 throw new RuntimeException("Couldn't destroy LDAP map '" + mapName + "': " + e.getMessage(), e);
305 } finally {
306 releaseContext(context);
307 }
308 return true;
309 }
310
311
312
313
314
315
316
317
318 public void destroyMap(String mapName, String mapDN) {
319 DirContext context = retrieveContext();
320 try {
321 SearchControls ctrls = new SearchControls();
322 ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
323 NamingEnumeration result = context.search(mapDN, "(objectclass=*)", ctrls);
324 while (result.hasMore()) {
325 SearchResult res = (SearchResult) result.next();
326 if ("".equals(res.getName().trim())) continue;
327 context.destroySubcontext(res.getName() + "," + mapDN);
328 }
329 context.destroySubcontext(mapDN);
330 log.trace("Destroyed LDAP map '" + mapName + "' at '" + mapDN + "'");
331 } catch (Exception e) {
332 log.info("LDAPPersistence error - destroyMap - map '" + mapName + "'", e);
333 throw new RuntimeException("Couldn't destroy LDAP map '" + mapName + "': " + e.getMessage(), e);
334 } finally {
335 releaseContext(context);
336 }
337 }
338
339 public String getAccountField() {
340 return accountField;
341 }
342
343 public String getCaCertFile() {
344 return caCertFile;
345 }
346
347 public String getGumsOU() {
348 return gumsOU;
349 }
350
351 public String getGroupIdField() {
352 return groupIdField;
353 }
354
355
356
357
358
359
360
361
362 public DirContext getLDAPContext() {
363 return retrieveContext();
364 }
365
366 public String getMemberAccountField() {
367 return memberAccountField;
368 }
369
370
371 public String getTrustStorePassword() {
372 return trustStorePassword;
373 }
374
375 public String getType() {
376 return "ldap";
377 }
378
379
380
381
382
383
384
385 public boolean isSynchGroups() {
386 return this.synchGroups;
387 }
388
389
390
391
392
393 public void releaseContext(DirContext context) {
394 if (skipReleaseContext) {
395 skipReleaseContext = false;
396 return;
397 }
398 contexts.add(0, context);
399 log.trace("LDAP connection returned to pool " + context);
400 }
401
402
403
404
405
406
407
408
409
410 public boolean removeMapEntry(String userDN, String mapName, String mapDN) {
411 DirContext context = retrieveContext();
412 boolean deleted = false;
413 try {
414 SearchControls ctrls = new SearchControls();
415 ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
416 NamingEnumeration result = context.search(mapDN, "(user={0})", new Object[] {userDN}, ctrls);
417 while (result.hasMore()) {
418 SearchResult res = (SearchResult) result.next();
419 if ("".equals(res.getName().trim())) continue;
420 ModificationItem[] mods = new ModificationItem[1];
421 mods[0] = new ModificationItem(context.REMOVE_ATTRIBUTE, new BasicAttribute("user", userDN));
422 context.modifyAttributes(res.getName() + "," + mapDN, mods);
423 deleted = true;
424 log.trace("Removed map entry - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "'");
425 }
426 return deleted;
427 } catch (Exception e) {
428 log.info("LDAPPersistence error - removeMapEntry - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "'", e);
429 throw new RuntimeException("Couldn't remove map entry from LDAP map - user '" + userDN + "' to map '" + mapName + "' at '" + mapDN + "': " + e.getMessage(), e);
430 } finally {
431 releaseContext(context);
432 }
433 }
434
435
436
437
438
439
440
441 public void removeUserGroupEntry(String userDN, String groupName, String groupDN) {
442 DirContext context = retrieveContext();
443 try {
444 ModificationItem[] mods = new ModificationItem[1];
445 mods[0] = new ModificationItem(context.REMOVE_ATTRIBUTE,
446 new BasicAttribute("user", userDN));
447 context.modifyAttributes(groupDN, mods);
448 log.trace("Removed user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'");
449 } catch (Exception e) {
450 log.info("LDAPPersistence error - removeUserGroupEntry - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "'", e);
451 throw new RuntimeException("Couldn't remove user to LDAP group - user '" + userDN + "' to group '" + groupName + "' at '" + groupDN + "': " + e.getMessage(), e);
452 } finally {
453 releaseContext(context);
454 }
455 }
456
457 public AccountPoolMapperDB retrieveAccountPoolMapperDB(String nameAndGroups) {
458 StringTokenizer tokens = new StringTokenizer(nameAndGroups, ".");
459 if (!tokens.hasMoreTokens()) {
460 log.trace("Creating LDAP AccountPoolMapperDB '" + nameAndGroups + "' (no GIDs)");
461 return new LDAPAccountMapperDB(this, nameAndGroups);
462 }
463
464 String pool = tokens.nextToken();
465 if (!tokens.hasMoreTokens()) {
466 log.trace("Creating LDAP AccountPoolMapperDB '" + nameAndGroups + "' (no GIDs)");
467 return new LDAPAccountMapperDB(this, nameAndGroups);
468 }
469
470 String group = tokens.nextToken();
471 List secondaryGroups = new ArrayList();
472 while (tokens.hasMoreTokens()) {
473 secondaryGroups.add(tokens.nextToken());
474 }
475
476 log.trace("Creating LDAP AccountPoolMapperDB '" + nameAndGroups + "' primary group '" + group + "' secondary groups '" + secondaryGroups + "'");
477 return new LDAPAccountMapperDB(this, pool, group, secondaryGroups);
478 }
479
480
481
482
483
484
485
486 public DirContext retrieveContext() {
487 DirContext context;
488 while (contexts.size() != 0) {
489 context = (DirContext) contexts.remove(0);
490 if (isContextValid(context)) {
491 log.trace("Using LDAP connection from pool " + context);
492 return context;
493 }
494 }
495 context = createLDAPContext();
496 log.trace("New LDAP connection created " + context);
497 return context;
498 }
499
500 public ManualAccountMapperDB retrieveManualAccountMapperDB(String name) {
501 log.trace("Creating LDAP ManualAccountMapperDB '" + name + "'");
502 return new LDAPAccountMapperDB(this, name);
503 }
504
505 public ManualUserGroupDB retrieveManualUserGroupDB(String name) {
506 log.trace("Creating LDAP ManualUserGroupDB '" + name + "'");
507 return new LDAPUserGroupDB(this, name);
508 }
509
510 public UserGroupDB retrieveUserGroupDB(String name) {
511 log.trace("Creating LDAP UserGroupDB '" + name + "'");
512 return new LDAPUserGroupDB(this, name);
513 }
514
515 public void setAccountField(String accountField) {
516 this.accountField = accountField;
517 }
518
519 public void setCaCertFile(String caCertFile) {
520
521 this.caCertFile = caCertFile;
522
523
524 }
525
526 public void setGroupIdField(String groupIdField) {
527 this.groupIdField = groupIdField;
528 }
529
530 public void setMemberAccountField(String memberAccountField) {
531 this.memberAccountField = memberAccountField;
532 }
533
534
535
536
537
538
539
540 public void setProperties(Properties properties) {
541 if (properties.getProperty("java.naming.factory.initial") == null) {
542 properties.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
543 }
544
545
546
547
548
549
550
551
552
553
554
555 super.setProperties(properties);
556 }
557
558
559
560
561
562
563 public void setSynchGroups(boolean synchGroups) {
564 this.synchGroups = synchGroups;
565 }
566
567 public void setTrustStorePassword(String trustStorePassword) {
568
569 this.trustStorePassword = trustStorePassword;
570
571
572 }
573
574 public String toXML() {
575 String retStr = "\t\t<ldapPersistenceFactory\n"+
576 "\t\t\tname='"+getName()+"'\n"+
577 "\t\t\tdescription='"+getDescription()+"'\n"+
578 "\t\t\tsynchGroups='"+synchGroups+"'\n"+
579
580
581 "\t\t\tgroupIdField='"+groupIdField+"'\n"+
582 "\t\t\taccountField='"+accountField+"'\n"+
583 "\t\t\tmemberAccountField='"+memberAccountField+"'\n";
584
585 Iterator keyIt = getProperties().keySet().iterator();
586 while(keyIt.hasNext()) {
587 String key = (String)keyIt.next();
588 retStr += "\t\t\t"+key+"='"+getProperties().getProperty(key)+"'\n";
589 }
590
591 if (retStr.charAt(retStr.length()-1)=='\n')
592 retStr = retStr.substring(0, retStr.length()-1);
593
594 retStr += "/>\n\n";
595
596 return retStr;
597 }
598
599 private void addCertToTrustStore() {
600 X509Certificate cert = null;
601 try {
602 InputStream inStream = new FileInputStream(caCertFile);
603 CertificateFactory cf = CertificateFactory.getInstance("X.509");
604 cert = (X509Certificate)cf.generateCertificate(inStream);
605 inStream.close();
606 } catch (Exception e) {
607 log.error("Cannot open " + caCertFile, e);
608 adminLog.error("Cannot open " + caCertFile + ": " + e.getMessage());
609 return;
610 }
611 try {
612 KeyStore ks = KeyStore.getInstance("jks");
613 ks.load(new FileInputStream(trustStore), trustStorePassword.toCharArray());
614 ks.setCertificateEntry("gumsldapda", cert);
615 FileOutputStream fos = new FileOutputStream(trustStore);
616 ks.store(fos, trustStorePassword.toCharArray());
617 fos.close();
618 } catch (Exception e) {
619 log.error("Couldn't put " + caCertFile + " into trust store", e);
620 adminLog.error("Couldn't put " + caCertFile + " into trust store: " + e.getMessage() );
621 }
622 }
623
624 private String findGID(String groupname) {
625 DirContext context = retrieveContext();
626 try {
627 NamingEnumeration result = context.search("ou=Group", "(cn={0})", new Object[] {groupname}, null);
628 String gid = null;
629 if (result.hasMore()) {
630 SearchResult item = (SearchResult) result.next();
631 Attributes atts = item.getAttributes();
632 Attribute gidAtt = atts.get(groupIdField);
633 if (gidAtt != null) {
634 gid = (String) gidAtt.get();
635 }
636 }
637 log.trace("Found gid '" + gid + "' for group '" + groupname + "'");
638 return gid;
639 } catch (Exception e) {
640 log.info("Couldn't retrieve gid for '" + groupname + "'", e);
641 throw new RuntimeException("Couldn't retrieve gid for '" + groupname + "': " + e.getMessage(), e);
642 } finally {
643 releaseContext(context);
644 }
645 }
646
647 private boolean isContextValid(DirContext context) {
648 try {
649 context.search(gumsOU, "(map=*)", null);
650 return true;
651 } catch (Exception e) {
652 log.trace("Removing stale LDAP connection from pool " + context, e);
653 adminLog.warn("LDAP connection test failed, discarding connection from pool: " + e.getMessage());
654 return false;
655 }
656 }
657
658 private void updateGID(String account, String gid) {
659 DirContext context = retrieveContext();
660 try {
661 ModificationItem[] mods = new ModificationItem[1];
662 mods[0] = new ModificationItem(context.REPLACE_ATTRIBUTE, new BasicAttribute(groupIdField, gid));
663 context.modifyAttributes(accountField+"="+account+",ou=People", mods);
664 log.trace("Changed primary gid for user '" + account + "' to gid '" + gid + "''");
665 } catch (Exception e) {
666 log.warn("Couldn't change gid for user '" + account + "' to gid '" + gid + "''", e);
667 throw new RuntimeException("Couldn't change gid for user '" + account + "' to gid '" + gid + "''", e);
668 } finally {
669 releaseContext(context);
670 }
671 }
672
673
674
675
676
677 protected DirContext createLDAPContext() {
678 try {
679 log.info("Trying to create LDAP connection with properties: " + getProperties());
680 return new InitialLdapContext(getProperties(), null);
681 } catch (NamingException e) {
682 log.warn("Couldn't create LDAP connection: " + e.getMessage() + " - parameters: " + getProperties(), e);
683 throw new RuntimeException("Couldn't create LDAP connection: " + e.getMessage());
684 }
685 }
686
687 }