View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.provisioning.java.data;
20  
21  import java.text.ParseException;
22  import java.util.Collections;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Optional;
27  import java.util.stream.Collectors;
28  import java.util.stream.Stream;
29  import org.apache.commons.lang3.StringUtils;
30  import org.apache.syncope.common.lib.SyncopeClientCompositeException;
31  import org.apache.syncope.common.lib.SyncopeClientException;
32  import org.apache.syncope.common.lib.to.AnyTypeClassTO;
33  import org.apache.syncope.common.lib.to.Item;
34  import org.apache.syncope.common.lib.to.ItemContainer;
35  import org.apache.syncope.common.lib.to.Mapping;
36  import org.apache.syncope.common.lib.to.OrgUnit;
37  import org.apache.syncope.common.lib.to.Provision;
38  import org.apache.syncope.common.lib.to.ResourceTO;
39  import org.apache.syncope.common.lib.types.AnyTypeKind;
40  import org.apache.syncope.common.lib.types.ClientExceptionType;
41  import org.apache.syncope.common.lib.types.MappingPurpose;
42  import org.apache.syncope.common.lib.types.SchemaType;
43  import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
44  import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
45  import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
46  import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
47  import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
48  import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
49  import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
50  import org.apache.syncope.core.persistence.api.entity.AnyType;
51  import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
52  import org.apache.syncope.core.persistence.api.entity.ConnInstance;
53  import org.apache.syncope.core.persistence.api.entity.DerSchema;
54  import org.apache.syncope.core.persistence.api.entity.EntityFactory;
55  import org.apache.syncope.core.persistence.api.entity.ExternalResource;
56  import org.apache.syncope.core.persistence.api.entity.Implementation;
57  import org.apache.syncope.core.persistence.api.entity.PlainSchema;
58  import org.apache.syncope.core.persistence.api.entity.VirSchema;
59  import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
60  import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
61  import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy;
62  import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
63  import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
64  import org.apache.syncope.core.provisioning.api.IntAttrName;
65  import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
66  import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
67  import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
68  import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
69  import org.identityconnectors.framework.common.objects.ObjectClass;
70  import org.slf4j.Logger;
71  import org.slf4j.LoggerFactory;
72  
73  public class ResourceDataBinderImpl implements ResourceDataBinder {
74  
75      protected static final Logger LOG = LoggerFactory.getLogger(ResourceDataBinder.class);
76  
77      protected final AnyTypeDAO anyTypeDAO;
78  
79      protected final ConnInstanceDAO connInstanceDAO;
80  
81      protected final PolicyDAO policyDAO;
82  
83      protected final VirSchemaDAO virSchemaDAO;
84  
85      protected final AnyTypeClassDAO anyTypeClassDAO;
86  
87      protected final ImplementationDAO implementationDAO;
88  
89      protected final PlainSchemaDAO plainSchemaDAO;
90  
91      protected final EntityFactory entityFactory;
92  
93      protected final IntAttrNameParser intAttrNameParser;
94  
95      protected final PropagationTaskExecutor propagationTaskExecutor;
96  
97      public ResourceDataBinderImpl(
98              final AnyTypeDAO anyTypeDAO,
99              final ConnInstanceDAO connInstanceDAO,
100             final PolicyDAO policyDAO,
101             final VirSchemaDAO virSchemaDAO,
102             final AnyTypeClassDAO anyTypeClassDAO,
103             final ImplementationDAO implementationDAO,
104             final PlainSchemaDAO plainSchemaDAO,
105             final EntityFactory entityFactory,
106             final IntAttrNameParser intAttrNameParser,
107             final PropagationTaskExecutor propagationTaskExecutor) {
108 
109         this.anyTypeDAO = anyTypeDAO;
110         this.connInstanceDAO = connInstanceDAO;
111         this.policyDAO = policyDAO;
112         this.virSchemaDAO = virSchemaDAO;
113         this.anyTypeClassDAO = anyTypeClassDAO;
114         this.implementationDAO = implementationDAO;
115         this.plainSchemaDAO = plainSchemaDAO;
116         this.entityFactory = entityFactory;
117         this.intAttrNameParser = intAttrNameParser;
118         this.propagationTaskExecutor = propagationTaskExecutor;
119     }
120 
121     @Override
122     public ExternalResource create(final ResourceTO resourceTO) {
123         return update(entityFactory.newEntity(ExternalResource.class), resourceTO);
124     }
125 
126     @Override
127     public ExternalResource update(final ExternalResource resource, final ResourceTO resourceTO) {
128         resource.setKey(resourceTO.getKey());
129 
130         if (resourceTO.getConnector() != null) {
131             ConnInstance connector = connInstanceDAO.find(resourceTO.getConnector());
132             resource.setConnector(connector);
133 
134             if (!connector.getResources().contains(resource)) {
135                 connector.add(resource);
136             }
137         }
138 
139         resource.setEnforceMandatoryCondition(resourceTO.isEnforceMandatoryCondition());
140 
141         resource.setPropagationPriority(resourceTO.getPropagationPriority());
142 
143         // 1. add or update all (valid) provisions from TO
144         resourceTO.getProvisions().forEach(provisionTO -> {
145             AnyType anyType = anyTypeDAO.find(provisionTO.getAnyType());
146             if (anyType == null) {
147                 LOG.debug("Invalid {} specified {}, ignoring...",
148                         AnyType.class.getSimpleName(), provisionTO.getAnyType());
149             } else {
150                 Provision provision = resource.getProvisionByAnyType(anyType.getKey()).orElse(null);
151                 if (provision == null) {
152                     provision = new Provision();
153                     provision.setAnyType(anyType.getKey());
154                     resource.getProvisions().add(provision);
155                 }
156 
157                 if (provisionTO.getObjectClass() == null) {
158                     SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidProvision);
159                     sce.getElements().add("Null " + ObjectClass.class.getSimpleName());
160                     throw sce;
161                 }
162                 provision.setObjectClass(provisionTO.getObjectClass());
163 
164                 // add all classes contained in the TO
165                 for (String name : provisionTO.getAuxClasses()) {
166                     AnyTypeClass anyTypeClass = anyTypeClassDAO.find(name);
167                     if (anyTypeClass == null || provision.getAuxClasses().contains(name)) {
168                         LOG.warn("Ignoring invalid or already present {}: {}",
169                                 AnyTypeClass.class.getSimpleName(), name);
170                     } else {
171                         provision.getAuxClasses().add(anyTypeClass.getKey());
172                     }
173                 }
174                 // remove all classes not contained in the TO
175                 provision.getAuxClasses().
176                         removeIf(anyTypeClass -> !provisionTO.getAuxClasses().contains(anyTypeClass));
177 
178                 provision.setIgnoreCaseMatch(provisionTO.isIgnoreCaseMatch());
179 
180                 if (StringUtils.isBlank(provisionTO.getUidOnCreate())) {
181                     provision.setUidOnCreate(null);
182                 } else {
183                     PlainSchema uidOnCreate = plainSchemaDAO.find(provisionTO.getUidOnCreate());
184                     if (uidOnCreate == null) {
185                         LOG.warn("Ignoring invalid schema for uidOnCreate: {}", provisionTO.getUidOnCreate());
186                         provision.setUidOnCreate(null);
187                     } else {
188                         provision.setUidOnCreate(uidOnCreate.getKey());
189                     }
190                 }
191 
192                 if (provisionTO.getMapping() == null) {
193                     provision.setMapping(null);
194                 } else {
195                     Mapping mapping = provision.getMapping();
196                     if (mapping == null) {
197                         mapping = new Mapping();
198                         provision.setMapping(mapping);
199                     } else {
200                         mapping.getItems().clear();
201                     }
202 
203                     AnyTypeClassTO allowedSchemas = new AnyTypeClassTO();
204                     Stream.concat(
205                             anyType.getClasses().stream(),
206                             provision.getAuxClasses().stream().map(anyTypeClassDAO::find)).forEach(anyTypeClass -> {
207 
208                         allowedSchemas.getPlainSchemas().addAll(anyTypeClass.getPlainSchemas().stream().
209                                 map(PlainSchema::getKey).collect(Collectors.toList()));
210                         allowedSchemas.getDerSchemas().addAll(anyTypeClass.getDerSchemas().stream().
211                                 map(DerSchema::getKey).collect(Collectors.toList()));
212                         allowedSchemas.getVirSchemas().addAll(anyTypeClass.getVirSchemas().stream().
213                                 map(VirSchema::getKey).collect(Collectors.toList()));
214                     });
215 
216                     populateMapping(
217                             resource,
218                             provisionTO.getMapping(),
219                             mapping,
220                             anyType.getKind(),
221                             allowedSchemas);
222                 }
223 
224                 if (provisionTO.getVirSchemas().isEmpty()) {
225                     for (VirSchema schema : virSchemaDAO.find(resource.getKey(), anyType.getKey())) {
226                         virSchemaDAO.delete(schema.getKey());
227                     }
228                 } else {
229                     for (String schemaName : provisionTO.getVirSchemas()) {
230                         VirSchema schema = virSchemaDAO.find(schemaName);
231                         if (schema == null) {
232                             LOG.debug("Invalid {} specified: {}, ignoring...",
233                                     VirSchema.class.getSimpleName(), schemaName);
234                         } else {
235                             schema.setResource(resource);
236                             schema.setAnyType(anyTypeDAO.find(provision.getAnyType()));
237                         }
238                     }
239                 }
240             }
241         });
242 
243         // 2. remove all provisions not contained in the TO
244         for (Iterator<Provision> itor = resource.getProvisions().iterator(); itor.hasNext();) {
245             Provision provision = itor.next();
246             if (resourceTO.getProvision(provision.getAnyType()).isEmpty()) {
247                 virSchemaDAO.find(resource.getKey(), provision.getAnyType()).
248                         forEach(schema -> virSchemaDAO.delete(schema.getKey()));
249 
250                 itor.remove();
251             }
252         }
253 
254         // 3. orgUnit
255         if (resourceTO.getOrgUnit() == null && resource.getOrgUnit() != null) {
256             resource.setOrgUnit(null);
257         } else if (resourceTO.getOrgUnit() != null) {
258             OrgUnit orgUnitTO = resourceTO.getOrgUnit();
259 
260             OrgUnit orgUnit = Optional.ofNullable(resource.getOrgUnit()).orElseGet(() -> new OrgUnit());
261 
262             if (orgUnitTO.getObjectClass() == null) {
263                 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidOrgUnit);
264                 sce.getElements().add("Null " + ObjectClass.class.getSimpleName());
265                 throw sce;
266             }
267             orgUnit.setObjectClass(orgUnitTO.getObjectClass());
268 
269             orgUnit.setIgnoreCaseMatch(orgUnitTO.isIgnoreCaseMatch());
270 
271             if (orgUnitTO.getConnObjectLink() == null) {
272                 SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidOrgUnit);
273                 sce.getElements().add("Null connObjectLink");
274                 throw sce;
275             }
276             orgUnit.setConnObjectLink(orgUnitTO.getConnObjectLink());
277 
278             SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
279             SyncopeClientException invalidMapping = SyncopeClientException.build(
280                     ClientExceptionType.InvalidMapping);
281             SyncopeClientException requiredValuesMissing = SyncopeClientException.build(
282                     ClientExceptionType.RequiredValuesMissing);
283 
284             orgUnit.getItems().clear();
285             for (Item itemTO : orgUnitTO.getItems()) {
286                 if (itemTO == null) {
287                     LOG.error("Null {}", Item.class.getSimpleName());
288                     invalidMapping.getElements().add("Null " + Item.class.getSimpleName());
289                 } else if (itemTO.getIntAttrName() == null) {
290                     requiredValuesMissing.getElements().add("intAttrName");
291                     scce.addException(requiredValuesMissing);
292                 } else {
293                     if (!"name".equals(itemTO.getIntAttrName()) && !"fullpath".equals(itemTO.getIntAttrName())) {
294                         LOG.error("Only 'name' and 'fullpath' are supported for Realms");
295                         invalidMapping.getElements().add("Only 'name' and 'fullpath' are supported for Realms");
296                     } else {
297                         // no mandatory condition implies mandatory condition false
298                         if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
299                                 ? "false" : itemTO.getMandatoryCondition())) {
300 
301                             SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
302                                     ClientExceptionType.InvalidValues);
303                             invalidMandatoryCondition.getElements().add(itemTO.getMandatoryCondition());
304                             scce.addException(invalidMandatoryCondition);
305                         }
306 
307                         Item item = new Item();
308                         item.setIntAttrName(itemTO.getIntAttrName());
309                         item.setExtAttrName(itemTO.getExtAttrName());
310                         item.setPurpose(itemTO.getPurpose());
311                         item.setMandatoryCondition(itemTO.getMandatoryCondition());
312                         item.setConnObjectKey(itemTO.isConnObjectKey());
313                         item.setPassword(itemTO.isPassword());
314                         item.setPropagationJEXLTransformer(itemTO.getPropagationJEXLTransformer());
315                         item.setPullJEXLTransformer(itemTO.getPullJEXLTransformer());
316 
317                         itemTO.getTransformers().forEach(transformerKey -> {
318                             Implementation transformer = implementationDAO.find(transformerKey);
319                             if (transformer == null) {
320                                 LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...",
321                                         transformerKey);
322                             } else {
323                                 item.getTransformers().add(transformer.getKey());
324                             }
325                         });
326                         // remove all implementations not contained in the TO
327                         item.getTransformers().
328                                 removeIf(implementation -> !itemTO.getTransformers().contains(implementation));
329 
330                         if (item.isConnObjectKey()) {
331                             orgUnit.setConnObjectKeyItem(item);
332                         } else {
333                             orgUnit.add(item);
334                         }
335                     }
336                 }
337             }
338             if (!invalidMapping.getElements().isEmpty()) {
339                 scce.addException(invalidMapping);
340             }
341             if (scce.hasExceptions()) {
342                 throw scce;
343             }
344 
345             resource.setOrgUnit(orgUnit);
346         }
347 
348         resource.setCreateTraceLevel(resourceTO.getCreateTraceLevel());
349         resource.setUpdateTraceLevel(resourceTO.getUpdateTraceLevel());
350         resource.setDeleteTraceLevel(resourceTO.getDeleteTraceLevel());
351         resource.setProvisioningTraceLevel(resourceTO.getProvisioningTraceLevel());
352 
353         resource.setPasswordPolicy(resourceTO.getPasswordPolicy() == null
354                 ? null : policyDAO.<PasswordPolicy>find(resourceTO.getPasswordPolicy()));
355 
356         resource.setAccountPolicy(resourceTO.getAccountPolicy() == null
357                 ? null : policyDAO.<AccountPolicy>find(resourceTO.getAccountPolicy()));
358 
359         if (resource.getPropagationPolicy() != null
360                 && !resource.getPropagationPolicy().getKey().equals(resourceTO.getPropagationPolicy())) {
361 
362             propagationTaskExecutor.expireRetryTemplate(resource.getKey());
363         }
364         resource.setPropagationPolicy(resourceTO.getPropagationPolicy() == null
365                 ? null : policyDAO.<PropagationPolicy>find(resourceTO.getPropagationPolicy()));
366 
367         resource.setPullPolicy(resourceTO.getPullPolicy() == null
368                 ? null : policyDAO.<PullPolicy>find(resourceTO.getPullPolicy()));
369 
370         resource.setPushPolicy(resourceTO.getPushPolicy() == null
371                 ? null : policyDAO.<PushPolicy>find(resourceTO.getPushPolicy()));
372 
373         if (resourceTO.getProvisionSorter() == null) {
374             resource.setProvisionSorter(null);
375         } else {
376             Implementation provisionSorter = implementationDAO.find(resourceTO.getProvisionSorter());
377             if (provisionSorter == null) {
378                 LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...",
379                         resourceTO.getProvisionSorter());
380             } else {
381                 resource.setProvisionSorter(provisionSorter);
382             }
383         }
384 
385         resource.setConfOverride(new HashSet<>(resourceTO.getConfOverride()));
386 
387         resource.setOverrideCapabilities(resourceTO.isOverrideCapabilities());
388         resource.getCapabilitiesOverride().clear();
389         resource.getCapabilitiesOverride().addAll(resourceTO.getCapabilitiesOverride());
390 
391         resourceTO.getPropagationActions().forEach(propagationActionKey -> {
392             Implementation propagationAction = implementationDAO.find(propagationActionKey);
393             if (propagationAction == null) {
394                 LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", propagationActionKey);
395             } else {
396                 resource.add(propagationAction);
397             }
398         });
399         // remove all implementations not contained in the TO
400         resource.getPropagationActions().
401                 removeIf(propActions -> !resourceTO.getPropagationActions().contains(propActions.getKey()));
402 
403         return resource;
404     }
405 
406     protected void populateMapping(
407             final ExternalResource resource,
408             final Mapping mappingTO,
409             final Mapping mapping,
410             final AnyTypeKind anyTypeKind,
411             final AnyTypeClassTO allowedSchemas) {
412 
413         mapping.setConnObjectLink(mappingTO.getConnObjectLink());
414 
415         SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
416         SyncopeClientException invalidMapping = SyncopeClientException.build(ClientExceptionType.InvalidMapping);
417         SyncopeClientException requiredValuesMissing = SyncopeClientException.build(
418                 ClientExceptionType.RequiredValuesMissing);
419 
420         for (Item itemTO : mappingTO.getItems()) {
421             if (itemTO == null) {
422                 LOG.error("Null {}", Item.class.getSimpleName());
423                 invalidMapping.getElements().add("Null " + Item.class.getSimpleName());
424             } else if (itemTO.getIntAttrName() == null) {
425                 requiredValuesMissing.getElements().add("intAttrName");
426                 scce.addException(requiredValuesMissing);
427             } else {
428                 IntAttrName intAttrName = null;
429                 try {
430                     intAttrName = intAttrNameParser.parse(itemTO.getIntAttrName(), anyTypeKind);
431                 } catch (ParseException e) {
432                     LOG.error("Invalid intAttrName '{}'", itemTO.getIntAttrName(), e);
433                 }
434 
435                 if (intAttrName == null
436                         || intAttrName.getSchemaType() == null && intAttrName.getField() == null
437                         && intAttrName.getPrivilegesOfApplication() == null) {
438 
439                     LOG.error("'{}' not existing", itemTO.getIntAttrName());
440                     invalidMapping.getElements().add('\'' + itemTO.getIntAttrName() + "' not existing");
441                 } else {
442                     boolean allowed = true;
443                     if (intAttrName.getSchemaType() != null
444                             && intAttrName.getEnclosingGroup() == null
445                             && intAttrName.getRelatedAnyObject() == null
446                             && intAttrName.getRelationshipType() == null
447                             && intAttrName.getPrivilegesOfApplication() == null) {
448 
449                         switch (intAttrName.getSchemaType()) {
450                             case PLAIN:
451                                 allowed = allowedSchemas.getPlainSchemas().contains(intAttrName.getSchema().getKey());
452                                 break;
453 
454                             case DERIVED:
455                                 allowed = allowedSchemas.getDerSchemas().contains(intAttrName.getSchema().getKey());
456                                 break;
457 
458                             case VIRTUAL:
459                                 allowed = allowedSchemas.getVirSchemas().contains(intAttrName.getSchema().getKey());
460                                 break;
461 
462                             default:
463                         }
464                     }
465 
466                     if (allowed) {
467                         // no mandatory condition implies mandatory condition false
468                         if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
469                                 ? "false" : itemTO.getMandatoryCondition())) {
470 
471                             SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
472                                     ClientExceptionType.InvalidValues);
473                             invalidMandatoryCondition.getElements().add(itemTO.getMandatoryCondition());
474                             scce.addException(invalidMandatoryCondition);
475                         }
476 
477                         Item item = new Item();
478                         item.setIntAttrName(itemTO.getIntAttrName());
479                         item.setExtAttrName(itemTO.getExtAttrName());
480                         item.setPurpose(itemTO.getPurpose());
481                         item.setMandatoryCondition(itemTO.getMandatoryCondition());
482                         item.setConnObjectKey(itemTO.isConnObjectKey());
483                         item.setPassword(itemTO.isPassword());
484                         item.setPropagationJEXLTransformer(itemTO.getPropagationJEXLTransformer());
485                         item.setPullJEXLTransformer(itemTO.getPullJEXLTransformer());
486 
487                         itemTO.getTransformers().forEach(transformerKey -> {
488                             Implementation transformer = implementationDAO.find(transformerKey);
489                             if (transformer == null) {
490                                 LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...",
491                                         transformerKey);
492                             } else {
493                                 item.getTransformers().add(transformer.getKey());
494                             }
495                         });
496                         // remove all implementations not contained in the TO
497                         item.getTransformers().
498                                 removeIf(implementation -> !itemTO.getTransformers().contains(implementation));
499 
500                         if (item.isConnObjectKey()) {
501                             if (intAttrName.getSchemaType() == SchemaType.VIRTUAL) {
502                                 invalidMapping.getElements().
503                                         add("Virtual attributes cannot be set as ConnObjectKey");
504                             }
505                             if ("password".equals(intAttrName.getField())) {
506                                 invalidMapping.getElements().add(
507                                         "Password attributes cannot be set as ConnObjectKey");
508                             }
509 
510                             mapping.setConnObjectKeyItem(item);
511                         } else {
512                             mapping.add(item);
513                         }
514 
515                         if (intAttrName.getEnclosingGroup() != null
516                                 && item.getPurpose() != MappingPurpose.PROPAGATION) {
517 
518                             invalidMapping.getElements().add(
519                                     "Only " + MappingPurpose.PROPAGATION.name()
520                                     + " allowed when referring to groups");
521                         }
522                         if (intAttrName.getRelatedAnyObject() != null
523                                 && item.getPurpose() != MappingPurpose.PROPAGATION) {
524 
525                             invalidMapping.getElements().add(
526                                     "Only " + MappingPurpose.PROPAGATION.name()
527                                     + " allowed when referring to any objects");
528                         }
529                         if (intAttrName.getPrivilegesOfApplication() != null
530                                 && item.getPurpose() != MappingPurpose.PROPAGATION) {
531 
532                             invalidMapping.getElements().add(
533                                     "Only " + MappingPurpose.PROPAGATION.name()
534                                     + " allowed when referring to privileges");
535                         }
536                         if (intAttrName.getSchemaType() == SchemaType.DERIVED
537                                 && item.getPurpose() != MappingPurpose.PROPAGATION) {
538 
539                             invalidMapping.getElements().add(
540                                     "Only " + MappingPurpose.PROPAGATION.name() + " allowed for derived");
541                         }
542                         if (intAttrName.getSchemaType() == SchemaType.VIRTUAL) {
543                             if (item.getPurpose() != MappingPurpose.PROPAGATION) {
544                                 invalidMapping.getElements().add(
545                                         "Only " + MappingPurpose.PROPAGATION.name() + " allowed for virtual");
546                             }
547 
548                             VirSchema schema = virSchemaDAO.find(item.getIntAttrName());
549                             if (schema != null && schema.getResource().equals(resource)) {
550                                 invalidMapping.getElements().add(
551                                         "No need to map virtual schema on linking resource");
552                             }
553                         }
554                         if (intAttrName.getRelatedUser() != null
555                                 && item.getPurpose() != MappingPurpose.PROPAGATION) {
556 
557                             invalidMapping.getElements().add(
558                                     "Only " + MappingPurpose.PROPAGATION.name()
559                                     + " allowed when referring to users");
560                         }
561                         if ((intAttrName.getRelationshipType() != null
562                                 || intAttrName.getRelationshipAnyType() != null)
563                                 && item.getPurpose() != MappingPurpose.PROPAGATION) {
564 
565                             invalidMapping.getElements().add(
566                                     "Only " + MappingPurpose.PROPAGATION.name()
567                                     + " allowed when referring to relationships");
568                         }
569                     } else {
570                         LOG.error("'{}' not allowed", itemTO.getIntAttrName());
571                         invalidMapping.getElements().add('\'' + itemTO.getIntAttrName() + "' not allowed");
572                     }
573                 }
574             }
575         }
576 
577         if (!invalidMapping.getElements().isEmpty()) {
578             scce.addException(invalidMapping);
579         }
580         if (scce.hasExceptions()) {
581             throw scce;
582         }
583     }
584 
585     protected void populateItems(final List<Item> items, final ItemContainer containerTO) {
586         items.forEach(item -> {
587             Item itemTO = new Item();
588             itemTO.setIntAttrName(item.getIntAttrName());
589             itemTO.setExtAttrName(item.getExtAttrName());
590             itemTO.setPurpose(item.getPurpose());
591             itemTO.setMandatoryCondition(item.getMandatoryCondition());
592             itemTO.setConnObjectKey(item.isConnObjectKey());
593             itemTO.setPassword(item.isPassword());
594             itemTO.setPropagationJEXLTransformer(item.getPropagationJEXLTransformer());
595             itemTO.setPullJEXLTransformer(item.getPullJEXLTransformer());
596             itemTO.getTransformers().addAll(item.getTransformers());
597 
598             if (itemTO.isConnObjectKey()) {
599                 containerTO.setConnObjectKeyItem(itemTO);
600             } else {
601                 containerTO.add(itemTO);
602             }
603         });
604     }
605 
606     @Override
607     public ResourceTO getResourceTO(final ExternalResource resource) {
608         ResourceTO resourceTO = new ResourceTO();
609 
610         // set the resource name
611         resourceTO.setKey(resource.getKey());
612 
613         // set the connector instance
614         ConnInstance connector = resource.getConnector();
615 
616         resourceTO.setConnector(Optional.ofNullable(connector).map(ConnInstance::getKey).orElse(null));
617         resourceTO.setConnectorDisplayName(Optional.ofNullable(connector).
618                 map(ConnInstance::getDisplayName).orElse(null));
619 
620         // set the provision information
621         resource.getProvisions().forEach(provision -> {
622             Provision provisionTO = new Provision();
623             provisionTO.setAnyType(provision.getAnyType());
624             provisionTO.setObjectClass(provision.getObjectClass());
625             provisionTO.getAuxClasses().addAll(provision.getAuxClasses());
626             provisionTO.setSyncToken(provision.getSyncToken());
627             provisionTO.setIgnoreCaseMatch(provision.isIgnoreCaseMatch());
628             provisionTO.setUidOnCreate(provision.getUidOnCreate());
629 
630             if (provision.getMapping() != null) {
631                 Mapping mappingTO = new Mapping();
632                 provisionTO.setMapping(mappingTO);
633                 mappingTO.setConnObjectLink(provision.getMapping().getConnObjectLink());
634                 populateItems(provision.getMapping().getItems(), mappingTO);
635             }
636 
637             resourceTO.getProvisions().add(provisionTO);
638         });
639         resourceTO.getProvisions().
640                 forEach(provisionTO -> virSchemaDAO.find(resource.getKey(), provisionTO.getAnyType()).
641                 forEach(virSchema -> {
642                     provisionTO.getVirSchemas().add(virSchema.getKey());
643                     provisionTO.getMapping().getLinkingItems().add(virSchema.asLinkingMappingItem());
644                 }));
645 
646         if (resource.getOrgUnit() != null) {
647             OrgUnit orgUnit = resource.getOrgUnit();
648 
649             OrgUnit orgUnitTO = new OrgUnit();
650             orgUnitTO.setObjectClass(orgUnit.getObjectClass());
651             orgUnitTO.setSyncToken(orgUnit.getSyncToken());
652             orgUnitTO.setIgnoreCaseMatch(orgUnit.isIgnoreCaseMatch());
653             orgUnitTO.setConnObjectLink(orgUnit.getConnObjectLink());
654             populateItems(orgUnit.getItems(), orgUnitTO);
655 
656             resourceTO.setOrgUnit(orgUnitTO);
657         }
658 
659         resourceTO.setEnforceMandatoryCondition(resource.isEnforceMandatoryCondition());
660 
661         resourceTO.setPropagationPriority(resource.getPropagationPriority());
662 
663         resourceTO.setCreateTraceLevel(resource.getCreateTraceLevel());
664         resourceTO.setUpdateTraceLevel(resource.getUpdateTraceLevel());
665         resourceTO.setDeleteTraceLevel(resource.getDeleteTraceLevel());
666         resourceTO.setProvisioningTraceLevel(resource.getProvisioningTraceLevel());
667 
668         resourceTO.setPasswordPolicy(resource.getPasswordPolicy() == null
669                 ? null : resource.getPasswordPolicy().getKey());
670 
671         resourceTO.setAccountPolicy(resource.getAccountPolicy() == null
672                 ? null : resource.getAccountPolicy().getKey());
673 
674         resourceTO.setPropagationPolicy(resource.getPropagationPolicy() == null
675                 ? null : resource.getPropagationPolicy().getKey());
676 
677         resourceTO.setPullPolicy(resource.getPullPolicy() == null
678                 ? null : resource.getPullPolicy().getKey());
679 
680         resourceTO.setPushPolicy(resource.getPushPolicy() == null
681                 ? null : resource.getPushPolicy().getKey());
682 
683         resourceTO.setProvisionSorter(resource.getProvisionSorter() == null
684                 ? null : resource.getProvisionSorter().getKey());
685 
686         resourceTO.getConfOverride().addAll(resource.getConfOverride());
687         Collections.sort(resourceTO.getConfOverride());
688 
689         resourceTO.setOverrideCapabilities(resource.isOverrideCapabilities());
690         resourceTO.getCapabilitiesOverride().addAll(resource.getCapabilitiesOverride());
691 
692         resourceTO.getPropagationActions().addAll(
693                 resource.getPropagationActions().stream().map(Implementation::getKey).collect(Collectors.toList()));
694 
695         return resourceTO;
696     }
697 }