1 package edu.internet2.middleware.grouper.pspng;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.IOException;
20 import java.io.Reader;
21 import java.io.StringReader;
22 import java.util.*;
23
24 import com.unboundid.ldap.sdk.DN;
25 import com.unboundid.ldap.sdk.Filter;
26 import com.unboundid.ldap.sdk.LDAPException;
27 import com.unboundid.ldap.sdk.RDN;
28 import org.apache.commons.collections.MultiMap;
29 import org.apache.commons.collections.map.MultiValueMap;
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.log4j.MDC;
32 import org.ldaptive.*;
33 import org.ldaptive.io.LdifReader;
34
35 import edu.internet2.middleware.grouper.cache.GrouperCache;
36 import edu.internet2.middleware.grouper.util.GrouperUtil;
37 import edu.internet2.middleware.subject.Subject;
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public abstract class LdapProvisioner <ConfigurationClass extends LdapProvisionerConfiguration>
52 extends Provisioner<ConfigurationClass, LdapUser, LdapGroup>
53 {
54
55 private static final String LDAP_MOD_LIST = "LDAP_MODS";
56
57
58 private final Set<String> dnEscapedStrings = new HashSet<>();
59 private final Set<String> ldapFilterEscapedStrings = new HashSet<>();
60
61 private Set<DN> existingOUs = new HashSet<DN>();
62 protected LdapSystem ldapSystem;
63
64
65
66
67
68 public static Set<ResultCode> schemaRelatedLdapErrors = new HashSet<>();
69 static {
70 schemaRelatedLdapErrors.add(ResultCode.CONSTRAINT_VIOLATION);
71 schemaRelatedLdapErrors.add(ResultCode.LDAP_NOT_SUPPORTED);
72 schemaRelatedLdapErrors.add(ResultCode.UNWILLING_TO_PERFORM);
73 schemaRelatedLdapErrors.add(ResultCode.OBJECT_CLASS_VIOLATION);
74 }
75
76 public LdapProvisioner(String provisionerName, ConfigurationClass config, boolean fullSyncMode)
77 {
78 super(provisionerName, config, fullSyncMode);
79
80 LOG.debug("Constructing LdapProvisioner: {}", provisionerName);
81
82
83 try {
84 if (!getLdapSystem().test()) {
85 throw new RuntimeException("Unable to make ldap connection");
86 }
87 } catch (PspException e) {
88 LOG.error("{}: Unable to make ldap connection", getDisplayName(), e);
89 throw new RuntimeException("Unable to make ldap connection");
90 }
91 }
92
93
94
95
96
97
98
99
100
101
102 public static void stringHasBeenDnEscaped(String dnString) {
103 if ( activeProvisioner.get() == null || !(activeProvisioner.get() instanceof LdapProvisioner) ) {
104
105
106 return;
107 }
108
109 ((LdapProvisioner) activeProvisioner.get()).dnEscapedStrings.add(dnString);
110 }
111
112
113
114
115
116
117
118 public boolean isStringDnEscaped(String dnString) {
119 return dnEscapedStrings.contains(dnString);
120 }
121
122
123
124
125
126
127
128
129
130
131
132 public static void stringHasBeenLdapFilterEscaped(String ldapFilterValue) {
133 if ( activeProvisioner.get() == null || !(activeProvisioner.get() instanceof LdapProvisioner) ) {
134
135
136 return;
137 }
138
139 ((LdapProvisioner) activeProvisioner.get()).ldapFilterEscapedStrings.add(ldapFilterValue);
140 }
141
142
143
144
145
146
147
148
149
150 public boolean isStringEscapedForLdapFilter(String filterString) {
151 if ( ldapFilterEscapedStrings.contains(filterString) ) {
152 return true;
153 }
154
155
156
157
158
159 if ( !filterString.contains("=") ) {
160 return false;
161 }
162
163
164 String ldapFilterValue = StringUtils.substringAfter(filterString, "=");
165 return ldapFilterEscapedStrings.contains(ldapFilterValue);
166 }
167
168
169 @Override
170 public void finishCoordination(List<ProvisioningWorkItem> workItems, boolean wasSuccessful) {
171
172 ldapFilterEscapedStrings.clear();
173 dnEscapedStrings.clear();
174
175
176 super.finishCoordination(workItems, wasSuccessful);
177 }
178
179
180
181
182
183
184
185
186
187 protected Map<Subject, LdapUser> fetchTargetSystemUsers( Collection<Subject> subjectsToFetch)
188 throws PspException {
189 LOG.debug("Fetching {} users from target system", subjectsToFetch.size());
190
191 if ( subjectsToFetch.size() > config.getUserSearch_batchSize() )
192 throw new IllegalArgumentException("LdapProvisioner.fetchTargetSystemUsers: invoked with too many subjects to fetch");
193
194 StringBuilder combinedLdapFilter = new StringBuilder();
195
196
197 combinedLdapFilter.append("(|");
198
199 for ( Subject subject : subjectsToFetch ) {
200 SearchFilter f = getUserLdapFilter(subject);
201
202 String filterString = f.format();
203
204
205 if ( filterString.startsWith("(") )
206 combinedLdapFilter.append(filterString);
207 else
208 combinedLdapFilter.append('(').append(filterString).append(')');
209 }
210 combinedLdapFilter.append(')');
211
212
213 List<LdapObject> searchResult;
214
215 try {
216 searchResult = getLdapSystem().performLdapSearchRequest(
217 subjectsToFetch.size(), config.getUserSearchBaseDn(),
218 SearchScope.SUBTREE,
219 Arrays.asList(config.getUserSearchAttributes()),
220 combinedLdapFilter.toString());
221
222 LOG.debug("Read {} user objects from directory", searchResult.size());
223
224 if (shouldLogAboutMissingSubjects(subjectsToFetch, searchResult)) {
225 LOG.warn("Several subjects were not found: only {} subjects found with filter {}", searchResult.size(), combinedLdapFilter);
226 }
227 }
228 catch (PspException e) {
229 LOG.error("Problem searching for subjects with filter {} on base {}",
230 new Object[] {combinedLdapFilter, config.getUserSearchBaseDn(), e} );
231 throw e;
232 }
233
234
235
236
237
238
239
240
241 Map<Subject, LdapUser> result = new HashMap<Subject, LdapUser>();
242
243 Set<LdapObject> matchedFetchResults = new HashSet<LdapObject>();
244
245
246 for ( Subject subjectToFetch : subjectsToFetch ) {
247 SearchFilter f = getUserLdapFilter(subjectToFetch);
248
249 for ( LdapObject aFetchedLdapObject : searchResult ) {
250 if ( aFetchedLdapObject.matchesLdapFilter(f)) {
251 result.put(subjectToFetch, new LdapUser(aFetchedLdapObject));
252 matchedFetchResults.add(aFetchedLdapObject);
253 break;
254 }
255 }
256 }
257
258 Set<LdapObject> unmatchedFetchResults = new HashSet<LdapObject>(searchResult);
259 unmatchedFetchResults.removeAll(matchedFetchResults);
260
261 for ( LdapObject unmatchedFetchResult : unmatchedFetchResults )
262 LOG.error("{}: User data from ldap server was not matched with a grouper subject "
263 + "(perhaps attributes are used in userSearchFilter ({}) that are not included "
264 + "in userSearchAttributes ({})?): {}",
265 new Object[] {getDisplayName(), config.getUserSearchFilter(), config.getUserSearchAttributes(),
266 unmatchedFetchResult.getDn()});
267
268 return result;
269 }
270
271 protected SearchFilter getUserLdapFilter(Subject subject) throws PspException {
272 String result = evaluateJexlExpression("UserSearchFilter", config.getUserSearchFilter(), subject, null, null, null);
273 if ( StringUtils.isEmpty(result) )
274 throw new RuntimeException("User searching requires userSearchFilter to be configured correctly");
275
276
277 String filterPieces[] = result.split("\\|\\|");
278
279
280 SearchFilter filter = new SearchFilter(filterPieces[0]);
281
282
283 if ( filterPieces.length == 1 ) {
284 try {
285
286 Filter.create(result);
287 }
288 catch (LDAPException e) {
289 LOG.warn("{}: User ldap filter was invalid. " +
290 "Perhaps its filter clauses needed to be escaped with utils.escapeLdapFilter or use ldap-filter positional parameters. " +
291 "Subject={}. Bad filter={}. ",
292 new Object[]{getDisplayName(), subject, result});
293
294
295
296 }
297 } else {
298
299
300 for (int i = 1; i < filterPieces.length; i++)
301 filter.setParameter(i - 1, filterPieces[i].trim());
302 }
303
304 LOG.debug("{}: User LDAP filter for subject {}: {}",
305 new Object[]{getDisplayName(), subject.getId(), filter});
306 return filter;
307 }
308
309 @Override
310 protected LdapUser createUser(Subject personSubject) throws PspException {
311 GrouperUtil.assertion(config.isCreatingMissingUsersEnabled(), "Can't create users unless createMissingUsers is enabled");
312 GrouperUtil.assertion(StringUtils.isNotEmpty(config.getUserCreationLdifTemplate()), "Can't create users unless userCreationLdifTemplate is defined");
313 GrouperUtil.assertion(StringUtils.isNotEmpty(config.getUserCreationBaseDn()), "Can't create users unless userCreationBaseDn is defined");
314
315 LOG.info("Creating LDAP account for Subject: {} ", personSubject);
316 String ldif = config.getUserCreationLdifTemplate();
317 ldif = ldif.replaceAll("\\|\\|", "\n");
318 ldif = evaluateJexlExpression("UserTemplate", ldif, personSubject, null, null, null);
319
320 ldif = sanityCheckDnAttributesOfLdif(ldif, "User-creation ldif for %s", personSubject);
321
322 Connection conn = getLdapSystem().getLdapConnection();
323 try {
324 Reader reader = new StringReader(ldif);
325 LdifReader ldifReader = new LdifReader(reader);
326 SearchResult ldifResult = ldifReader.read();
327 LdapEntry ldifEntry = ldifResult.getEntry();
328
329
330 String actualDn = String.format("%s,%s", ldifEntry.getDn(),config.getUserCreationBaseDn());
331 ldifEntry.setDn(actualDn);
332
333 performLdapAdd(ldifEntry);
334
335
336 LOG.debug("Reading account that was just added to ldap server: {}", personSubject);
337 return fetchTargetSystemUser(personSubject);
338 } catch (PspException e) {
339 LOG.error("Problem while creating new user: {}: {}", ldif, e);
340 throw e;
341 } catch ( IOException e ) {
342 LOG.error("Problem while processing ldif to create new user: {}", ldif, e);
343 throw new PspException("LDIF problem creating user: %s", e.getMessage());
344 }
345 finally {
346 conn.close();
347 }
348 }
349
350
351
352
353
354
355
356
357 protected String sanityCheckDnAttributesOfLdif(String ldif, String ldifSourceFormat, Object... ldifSourceArgs)
358 throws PspException
359 {
360 String ldifSource = String.format(ldifSourceFormat, ldifSourceArgs);
361
362
363 String ldifLines[] = ldif.split("\\r?\\n");
364 for ( String ldifLine : ldifLines ) {
365 ldifLine = ldifLine.trim();
366
367
368 for ( String dnAttribute : getConfig().getAttributesNeededingDnEscaping() ) {
369 if ( ldifLine.toLowerCase().matches(String.format("^%s *:.*", dnAttribute)) ) {
370 String value = StringUtils.substringAfter(ldifLine, ":");
371
372 if (! DN.isValidDN(value) ) {
373 if (isStringDnEscaped(value)) {
374 LOG.error("{}: attribute '{}' is an invalid DN even though it was escaped: {}",
375 new Object[]{getDisplayName(), dnAttribute, value});
376 } else {
377 LOG.error("{}: attribute '{}' is an invalid DN. " +
378 "Perhaps its components need to be escaped with utils.escapeLdapRdn(rdn): {}",
379 new Object[]{getDisplayName(), dnAttribute, value});
380 }
381
382 throw new PspException("Attribute '%s' is an invalid DN in %s (utils.escapeLdapRdn is probably necessary): %s",
383 dnAttribute, ldifSource, ldifLine);
384 }
385 }
386
387 }
388 }
389 return ldif;
390 }
391
392 @Override
393 protected void populateJexlMap(Map<String, Object> variableMap, Subject subject,
394 LdapUser ldapUser, GrouperGroupInfo grouperGroupInfo, LdapGroup ldapGroup) {
395
396 super.populateJexlMap(variableMap, subject, ldapUser, grouperGroupInfo, ldapGroup);
397
398 if ( ldapGroup != null )
399 variableMap.put("ldapGroup", ldapGroup.getLdapObject());
400 if ( ldapUser != null )
401 variableMap.put("ldapUser", ldapUser.getLdapObject());
402 }
403
404
405
406
407
408
409
410
411 protected void scheduleLdapModification(ModifyRequest operation) {
412 ProvisioningWorkItem workItem = getCurrentWorkItem();
413 LOG.info("{}: Scheduling ldap modification: {}", getDisplayName(), operation);
414
415 workItem.addValueToProvisioningData(LDAP_MOD_LIST, operation);
416 }
417
418
419
420
421
422
423
424
425
426
427 @Override
428 public void finishProvisioningBatch(List<ProvisioningWorkItem> workItems) throws PspException {
429 try {
430 MDC.put("step", "coalesced");
431 makeCoalescedLdapChanges(workItems);
432
433
434 for ( ProvisioningWorkItem workItem : workItems )
435 workItem.markAsSuccess("Modification complete");
436
437 } catch (PspException e1) {
438 LOG.warn("RETRYING: Performing slower, unoptimized ldap provisioning after optimized provisioning failed");
439
440 for ( ProvisioningWorkItem workItem : workItems ) {
441 try {
442 MDC.put("step", "ldap_retry:"+workItem.getMdcLabel());
443 makeIndividualLdapChanges(workItem);
444 workItem.markAsSuccess("Modification complete");
445 } catch (PspException e2) {
446 LOG.error("Simple ldap provisioning failed for {}", workItem, e2);
447 workItem.markAsFailure("Modification failed: %s", e2.getMessage());
448 }
449 }
450 }
451 finally {
452 MDC.remove("step");
453 }
454
455 super.finishProvisioningBatch(workItems);
456 }
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478 private void makeCoalescedLdapChanges(List<ProvisioningWorkItem> workItems) throws PspException {
479 LOG.debug("{}: Making coalescedLdapChanges", getDisplayName());
480
481
482 MultiMap dn2Mods = new MultiValueMap();
483
484
485 for ( ProvisioningWorkItem workItem : workItems ) {
486 List<ModifyRequest> mods = (List) workItem.getProvisioningDataValues(LDAP_MOD_LIST);
487
488
489 if ( mods == null )
490 continue;
491
492 LOG.info("{}: WorkItem {} needs {} ldap modifications",
493 new Object[]{getDisplayName(), workItem, mods.size()} );
494 for ( ModifyRequest mod : mods ) {
495 LOG.debug("{}: Mod for WorkItem: {}", getDisplayName(), getLoggingSummary(mod));
496 dn2Mods.put(mod.getDn(), mod);
497 }
498 }
499
500
501 for ( String dn : (Collection<String>) dn2Mods.keySet() ) {
502
503 Collection<ModifyRequest> modsForDn = (Collection<ModifyRequest>) dn2Mods.get(dn);
504
505
506
507
508
509
510 List<List<AttributeModification>> coalescedOperations = new ArrayList<List<AttributeModification>>();
511
512
513
514 coalescedOperations.add(new ArrayList<AttributeModification>());
515
516
517
518 MultiMap attribute2ValuesToAdd = new MultiValueMap();
519 MultiMap attribute2ValuesToDel = new MultiValueMap();
520
521 for ( ModifyRequest mod : modsForDn ) {
522 for ( AttributeModification attributeMod : mod.getAttributeModifications() ) {
523 LdapAttribute attribute = attributeMod.getAttribute();
524
525 switch (attributeMod.getAttributeModificationType() ) {
526 case ADD:
527 for ( String value : attribute.getStringValues() )
528 attribute2ValuesToAdd.put(attribute.getName(), value);
529 break;
530 case REMOVE:
531 for ( String value : attribute.getStringValues() )
532 attribute2ValuesToDel.put(attribute.getName(), value);
533 break;
534 case REPLACE:
535 default:
536
537
538 coalescedOperations.get(0).add(attributeMod);
539 }
540 }
541 }
542
543 int maxValues = config.getMaxValuesToChangePerOperation();
544
545
546
547
548
549
550
551
552
553
554
555
556 for ( String attributeName : (Collection<String>) attribute2ValuesToDel.keySet() ) {
557 Collection<String> valuesToRemove = (Collection<String>) attribute2ValuesToDel.get(attributeName);
558 if (valuesToRemove == null ) {
559 valuesToRemove = Collections.EMPTY_LIST;
560 }
561
562 Collection<String> valuesToAdd = (Collection<String>) attribute2ValuesToAdd.get(attributeName);
563 if ( valuesToAdd == null ) {
564 valuesToAdd = Collections.EMPTY_LIST;
565 }
566
567
568 Set<String> valuesWithConflictingOperations = new HashSet<String>(valuesToRemove);
569 valuesWithConflictingOperations.retainAll(valuesToAdd);
570
571 if ( valuesWithConflictingOperations.size() > 0 ) {
572 LOG.warn("Found {} conflicting ldap operations in event batch. Scheduling a full sync on affected groups", valuesWithConflictingOperations.size());
573
574 Set<GrouperGroupInfo> groupsNeedingFullSync = new HashSet<>();
575
576
577 for ( String conflictingProvisioningAttributeValue : valuesWithConflictingOperations ) {
578
579 for ( ProvisioningWorkItem workItem : workItems ) {
580 if ( isWorkItemMakingChange(workItem, dn, attributeName, conflictingProvisioningAttributeValue) ) {
581 groupsNeedingFullSync.add(workItem.getGroupInfo(this));
582 }
583 }
584 }
585 }
586 }
587
588
589 for ( String attributeName : (Collection<String>) attribute2ValuesToDel.keySet() ) {
590 Collection<String> values = (Collection<String>) attribute2ValuesToDel.get(attributeName);
591 List<List<String>> valueChunks = PspUtils.chopped(values, maxValues);
592
593 for (int i=0; i<valueChunks.size(); i++) {
594 List<String> valueChunk = valueChunks.get(i);
595
596 LdapAttribute attribute = new LdapAttribute(attributeName, GrouperUtil.toArray(valueChunk, String.class));
597 AttributeModification mod = new AttributeModification(AttributeModificationType.REMOVE, attribute);
598
599
600 if ( coalescedOperations.size() <= i )
601 coalescedOperations.add(new ArrayList<AttributeModification>());
602
603 coalescedOperations.get(i).add(mod);
604 }
605 }
606
607
608
609
610
611 for ( String attributeName : (Collection<String>) attribute2ValuesToAdd.keySet() ) {
612 Collection<String> values = (Collection<String>) attribute2ValuesToAdd.get(attributeName);
613 List<List<String>> valueChunks = PspUtils.chopped(values, maxValues);
614
615 for (int i=0; i<valueChunks.size(); i++) {
616 List<String> valueChunk = valueChunks.get(i);
617
618 LdapAttribute attribute = new LdapAttribute(attributeName, GrouperUtil.toArray(valueChunk, String.class));
619 AttributeModification mod = new AttributeModification(AttributeModificationType.ADD, attribute);
620
621
622 if ( coalescedOperations.size() <= i )
623 coalescedOperations.add(new ArrayList<AttributeModification>());
624
625 coalescedOperations.get(i).add(mod);
626 }
627 }
628
629 Connection conn = getLdapSystem().getLdapConnection();
630 try {
631 for ( List<AttributeModification> operation : coalescedOperations ) {
632 ModifyRequest mod = new ModifyRequest(dn, GrouperUtil.toArray(operation, AttributeModification.class));
633 try {
634 conn.open();
635
636 LOG.info("Performing LDAP modification: {}", getLoggingSummary(mod) );
637 conn.getProviderConnection().modify(mod);
638 } catch (LdapException e) {
639 LOG.info("(THIS WILL BE RETRIED) Problem doing coalesced ldap modification: {} / {}: {}",
640 new Object[]{dn, mod, e.getMessage()});
641 throw new PspException("Coalesced LDAP Modification failed: %s",e.getMessage());
642 }
643 }
644 } finally {
645 conn.close();
646 }
647 }
648 }
649
650
651 protected boolean isWorkItemMakingChange(
652 ProvisioningWorkItem workItem,
653 String dn, String attributeName, String provisioningAttributeValue) {
654
655 @SuppressWarnings("unchecked")
656 List<ModifyRequest> modRequests = (List) workItem.getProvisioningDataValues(LDAP_MOD_LIST);
657
658
659
660 for ( ModifyRequest modRequest : modRequests ) {
661
662 if ( dn.equalsIgnoreCase(modRequest.getDn()) ) {
663
664 for ( AttributeModification attributeMod : modRequest.getAttributeModifications()) {
665 if ( attributeMod.getAttribute().getName().equalsIgnoreCase(attributeName) ) {
666 for ( String modValue : attributeMod.getAttribute().getStringValues() ) {
667 if ( modValue.equalsIgnoreCase(provisioningAttributeValue) ) {
668
669
670
671 return true;
672 }
673 }
674 }
675 }
676 }
677 }
678
679 return false;
680 }
681
682
683
684
685
686
687
688
689
690 private void makeIndividualLdapChanges(ProvisioningWorkItem workItem) throws PspException {
691 List<ModifyRequest> mods = (List) workItem.getProvisioningDataValues(LDAP_MOD_LIST);
692
693 if ( mods == null ) {
694 LOG.debug("{}: No ldap changes are necessary for work item {}", getDisplayName(), workItem);
695 return;
696 }
697
698 LOG.debug("{}: Implementing changes for work item {}", getDisplayName(), workItem);
699 for ( ModifyRequest mod : mods ) {
700 try {
701 getLdapSystem().performLdapModify(mod, false);
702 } catch (PspException e) {
703 LOG.error("{}: Ldap provisioning failed for {} / {}", new Object[]{getDisplayName(), workItem, mod, e});
704
705 throw e;
706 }
707 }
708 }
709
710 protected LdapSystem getLdapSystem() throws PspException {
711 if ( ldapSystem != null )
712 return ldapSystem;
713
714
715 synchronized (this) {
716
717
718 if ( ldapSystem != null )
719 return ldapSystem;
720
721 ldapSystem = new LdapSystem(config.getLdapPoolName(), config.isActiveDirectory());
722 return ldapSystem;
723 }
724 }
725
726 private String getLoggingSummary(ModifyRequest modForDn) {
727 if ( modForDn == null )
728 return "no changes";
729
730 StringBuilder sb = new StringBuilder();
731
732 sb.append(LdapObject.getDnSummary(modForDn.getDn(), 2));
733
734 for ( AttributeModification attribute : modForDn.getAttributeModifications()) {
735 switch (attribute.getAttributeModificationType()) {
736 case ADD: sb.append(String.format("[%s: +%d value(s)]",
737 attribute.getAttribute().getName(),
738 attribute.getAttribute().getStringValues().size()));
739 break;
740
741 case REMOVE: sb.append(String.format("[%s: -%d value(s)]",
742 attribute.getAttribute().getName(),
743 attribute.getAttribute().getStringValues().size()));
744 break;
745
746 case REPLACE: sb.append(String.format("[%s: =%d value(s)]",
747 attribute.getAttribute().getName(),
748 attribute.getAttribute().getStringValues().size()));
749 break;
750 }
751 }
752
753 return sb.toString();
754 }
755
756
757
758
759
760
761
762
763
764
765 public void ensureLdapOusExist(String dnString, boolean wholeDnIsTheOu) throws PspException {
766 LOG.info("{}: Checking for (and creating) missing OUs in DN: {} (wholeDnIsOu={})",
767 new Object[]{getDisplayName(), dnString, wholeDnIsTheOu});
768
769 DN startingDn;
770 try {
771 startingDn = new DN(dnString);
772
773 if ( wholeDnIsTheOu ) {
774 ensureLdapOusExist(startingDn);
775 } else {
776 ensureLdapOusExist(startingDn.getParent());
777 }
778 } catch (LDAPException e) {
779 LOG.error("Problem parsing DN {}", dnString, e);
780 throw new PspException("Problem parsing DN: %s", dnString);
781 }
782
783 }
784
785
786
787
788
789
790
791
792
793
794
795
796 protected void ensureLdapOusExist(DN dn) throws PspException {
797 if ( dn.isNullDN() ) {
798 throw new PspException("Never found an existing DN component when creating OUs");
799 }
800
801
802 if ( existingOUs.contains(dn) ) {
803 LOG.debug("{}: OU is known to exist: {}", getDisplayName(), dn.toMinimallyEncodedString());
804 return;
805 }
806
807 LOG.debug("{}: Checking to see if ou exists: {}", getDisplayName(), dn);
808 try {
809 if ( getLdapSystem().performLdapRead(dn) != null ) {
810
811 existingOUs.add(dn);
812 return;
813 } else {
814
815 ensureLdapOusExist(dn.getParent());
816 createOuInExistingLocation(dn);
817 existingOUs.add(dn);
818 }
819 }
820 catch (PspException e) {
821 LOG.error("{}: Creating OU failed: {}", new Object[]{getDisplayName(), dn, e});
822 throw new PspException("Unable to find existing OU nor create new one (%s)", e.getMessage());
823 }
824 }
825
826
827
828
829
830
831
832
833
834
835
836 protected void createOuInExistingLocation(DN ouDn) throws PspException {
837 String ouDnString = ouDn.toMinimallyEncodedString();
838
839 LOG.info("{}: Creating OU: {}", getDisplayName(), ouDnString);
840
841 RDN topRDN = ouDn.getRDN();
842
843
844 LdapAttribute topRdnAttribute = new LdapAttribute(topRDN.getAttributeNames()[0]);
845 topRdnAttribute.addStringValue( topRDN.getAttributeValues());
846
847 String ldif = evaluateJexlExpression("OuTemplate", config.getOuCreationLdifTemplate(),
848 null, null,
849 null, null,
850 "dn", ouDn.toMinimallyEncodedString(),
851 "ou", topRdnAttribute.getStringValue());
852 ldif = ldif.replaceAll("\\|\\|", "\n");
853
854 try {
855 Reader reader = new StringReader(ldif);
856 LdifReader ldifReader = new LdifReader(reader);
857 SearchResult ldifResult = ldifReader.read();
858 LdapEntry ldifEntry = ldifResult.getEntry();
859
860
861 if ( ldifEntry.getAttribute( topRdnAttribute.getName() ) == null ) {
862 ldifEntry.addAttribute(topRdnAttribute);
863 }
864
865 performLdapAdd(ldifEntry);
866 } catch ( IOException e ) {
867 LOG.error("{}: Problem while processing ldif to create new OU: {}", new Object[] {getDisplayName(), ldif, e});
868 throw new PspException("LDIF problem creating OU: %s", e.getMessage());
869 }
870 }
871
872
873
874
875
876
877 protected void performLdapAdd(LdapEntry entryToAdd) throws PspException {
878 LOG.info("{}: Creating LDAP object: {}", getDisplayName(), entryToAdd.getDn());
879
880 ensureLdapOusExist(entryToAdd.getDn(), false);
881 ldapSystem.performLdapAdd(entryToAdd);
882 }
883
884 }