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