[SYNCOPE-1311] Feature provided
authorFrancesco Chicchiriccò <ilgrosso@apache.org>
Tue, 15 May 2018 13:51:38 +0000 (15:51 +0200)
committerFrancesco Chicchiriccò <ilgrosso@apache.org>
Tue, 15 May 2018 13:51:38 +0000 (15:51 +0200)
28 files changed:
client/console/src/main/java/org/apache/syncope/client/console/panels/ProvisionAuxClassesPanel.java
client/console/src/main/resources/org/apache/syncope/client/console/panels/ProvisionAuxClassesPanel.html
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$AuxClasses.properties
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$AuxClasses_it.properties
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$AuxClasses_ja.properties
client/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ProvisionWizardBuilder$AuxClasses_pt_BR.properties
common/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisionTO.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResource.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/Provision.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAProvision.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DerAttrHandlerImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/VirAttrHandlerImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AzurePropagationActions.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/GoogleAppsPropagationActions.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/SCIMv11PropagationActions.java [deleted file]
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/GoogleAppsPullActions.java
fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
src/main/asciidoc/reference-guide/concepts/externalresources.adoc

index 0ce0ab2..02cc13d 100644 (file)
@@ -21,15 +21,22 @@ package org.apache.syncope.client.console.panels;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.common.lib.to.AnyTypeClassTO;
 import org.apache.syncope.common.lib.to.AnyTypeTO;
+import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.to.ProvisionTO;
+import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.model.util.ListModel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,6 +47,12 @@ public class ProvisionAuxClassesPanel extends Panel {
 
     private static final Logger LOG = LoggerFactory.getLogger(ProvisionAuxClassesPanel.class);
 
+    private final AnyTypeRestClient anyTypeRestClient = new AnyTypeRestClient();
+
+    private final AnyTypeClassRestClient anyTypeClassRestClient = new AnyTypeClassRestClient();
+
+    private final SchemaRestClient schemaRestClient = new SchemaRestClient();
+
     private final ProvisionTO provision;
 
     public ProvisionAuxClassesPanel(final String id, final ProvisionTO provision) {
@@ -53,6 +66,7 @@ public class ProvisionAuxClassesPanel extends Panel {
     protected void onBeforeRender() {
         super.onBeforeRender();
 
+        AnyTypeTO anyType = null;
         IModel<List<String>> model;
         List<String> choices;
         if (provision == null) {
@@ -62,25 +76,41 @@ public class ProvisionAuxClassesPanel extends Panel {
             model = new PropertyModel<>(provision, "auxClasses");
             choices = new ArrayList<>();
 
-            AnyTypeTO anyType = null;
             try {
-                anyType = new AnyTypeRestClient().read(provision.getAnyType());
+                anyType = anyTypeRestClient.read(provision.getAnyType());
             } catch (Exception e) {
                 LOG.error("Could not read AnyType {}", provision.getAnyType(), e);
             }
             if (anyType != null) {
-                for (AnyTypeClassTO aux : new AnyTypeClassRestClient().list()) {
+                for (AnyTypeClassTO aux : anyTypeClassRestClient.list()) {
                     if (!anyType.getClasses().contains(aux.getKey())) {
                         choices.add(aux.getKey());
                     }
                 }
             }
         }
+
         addOrReplace(
                 new AjaxPalettePanel.Builder<String>().build("auxClasses", model, new ListModel<>(choices)).
                         hideLabel().
                         setOutputMarkupId(true).
                         setEnabled(provision != null));
+
+        AjaxTextFieldPanel uidOnCreate = new AjaxTextFieldPanel(
+                "uidOnCreate", new ResourceModel("uidOnCreate", "uidOnCreate").getObject(),
+                new PropertyModel<>(provision, "uidOnCreate"));
+        uidOnCreate.setChoices(getSchemas(anyType, model.getObject()));
+        uidOnCreate.setOutputMarkupId(true).
+                setEnabled(provision != null);
+        addOrReplace(uidOnCreate);
     }
 
+    private List<String> getSchemas(final AnyTypeTO anyType, final List<String> anyTypeClasses) {
+        List<String> classes = new ArrayList<>(anyType.getClasses());
+        classes.addAll(anyTypeClasses);
+
+        return schemaRestClient.<PlainSchemaTO>getSchemas(
+                SchemaType.PLAIN, null, classes.toArray(new String[] {})).
+                stream().map(EntityTO::getKey).collect(Collectors.toList());
+    }
 }
index 3be0e43..eb08038 100644 (file)
@@ -19,5 +19,7 @@ under the License.
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
     <span wicket:id="auxClasses"/>
+    <hr/>
+    <span wicket:id="uidOnCreate"/>
   </wicket:panel>
 </html>
index cf9293e..12206c1 100644 (file)
@@ -45,6 +45,8 @@ public class ProvisionTO extends AbstractBaseBean implements EntityTO {
 
     private boolean ignoreCaseMatch;
 
+    private String uidOnCreate;
+
     private MappingTO mapping;
 
     private final List<String> virSchemas = new ArrayList<>();
@@ -98,6 +100,14 @@ public class ProvisionTO extends AbstractBaseBean implements EntityTO {
         this.ignoreCaseMatch = ignoreCaseMatch;
     }
 
+    public String getUidOnCreate() {
+        return uidOnCreate;
+    }
+
+    public void setUidOnCreate(final String uidOnCreate) {
+        this.uidOnCreate = uidOnCreate;
+    }
+
     public MappingTO getMapping() {
         return mapping;
     }
index e59350e..8544cd4 100644 (file)
@@ -21,7 +21,6 @@ package org.apache.syncope.core.persistence.api.entity;
 import java.util.Set;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.dao.AllowedSchemas;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 
@@ -53,5 +52,5 @@ public interface AnyUtils {
 
     Set<ExternalResource> getAllResources(Any<?> any);
 
-    <S extends Schema> AllowedSchemas<S> getAllowedSchemas(Any<?> any, Class<S> reference);
+    void addAttr(String key, PlainSchema schema, String value);
 }
index 4093bec..c817306 100644 (file)
@@ -95,6 +95,8 @@ public interface ExternalResource extends ProvidedKeyEntity {
 
     boolean add(Provision provision);
 
+    Optional<? extends Provision> getProvision(String anyType);
+
     Optional<? extends Provision> getProvision(AnyType anyType);
 
     Optional<? extends Provision> getProvision(ObjectClass objectClass);
index 1fe6023..5dde201 100644 (file)
@@ -22,6 +22,7 @@ import java.util.List;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.identityconnectors.framework.common.objects.SyncToken;
 
@@ -53,6 +54,10 @@ public interface Provision extends Entity {
 
     void setIgnoreCaseMatch(boolean ignoreCaseMatch);
 
+    PlainSchema getUidOnCreate();
+
+    void setUidOnCreate(PlainSchema uidOnCreate);
+
     Mapping getMapping();
 
     void setMapping(Mapping mapping);
index 3ad39f6..efcd24a 100644 (file)
@@ -315,14 +315,16 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource> implem
                 forEach(policy -> policy.getResources().remove(resource));
 
         resource.getProvisions().stream().
-                filter(provision -> provision.getMapping() != null).
-                peek(provision -> provision.getMapping().getItems().forEach(item -> item.setMapping(null))).
                 peek(provision -> {
-                    provision.getMapping().getItems().clear();
+                    provision.setUidOnCreate(null);
+                    if (provision.getMapping() != null) {
+                        provision.getMapping().getItems().forEach(item -> item.setMapping(null));
+                        provision.getMapping().getItems().clear();
+                    }
                     provision.setMapping(null);
                     provision.setResource(null);
                 }).
-                forEachOrdered(provision -> virSchemaDAO().findByProvision(provision).
+                forEach(provision -> virSchemaDAO().findByProvision(provision).
                 forEach(schema -> virSchemaDAO().delete(schema.getKey())));
 
         externalResourceHistoryConfDAO().deleteByEntity(resource);
index 15fa17c..cbc0e4e 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.dao.AllowedSchemas;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -41,7 +41,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
-import org.apache.syncope.core.persistence.api.entity.Schema;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
@@ -58,12 +58,16 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttr;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public class JPAAnyUtils implements AnyUtils {
 
+    private static final Logger LOG = LoggerFactory.getLogger(AnyUtils.class);
+
     private static final Set<String> USER_FIELD_NAMES = new HashSet<>();
 
     private static final Set<String> GROUP_FIELD_NAMES = new HashSet<>();
@@ -79,7 +83,7 @@ public class JPAAnyUtils implements AnyUtils {
     private static void initFieldNames(final Class<?> entityClass, final Set<String> keys) {
         List<Class<?>> classes = ClassUtils.getAllSuperclasses(entityClass);
         classes.add(entityClass);
-        for (Class<?> clazz : classes) {
+        classes.forEach(clazz -> {
             for (Field field : clazz.getDeclaredFields()) {
                 if (!Modifier.isStatic(field.getModifiers())
                         && !field.getName().startsWith("pc")
@@ -89,7 +93,7 @@ public class JPAAnyUtils implements AnyUtils {
                     keys.add("id".equals(field.getName()) ? "key" : field.getName());
                 }
             }
-        }
+        });
     }
 
     public static boolean matchesFieldName(final String candidate) {
@@ -377,19 +381,30 @@ public class JPAAnyUtils implements AnyUtils {
         return resources;
     }
 
-    @Transactional(readOnly = true)
+    @Transactional
     @Override
-    public <S extends Schema> AllowedSchemas<S> getAllowedSchemas(final Any<?> any, final Class<S> reference) {
-        AllowedSchemas<S> result = null;
-
-        if (any instanceof User) {
-            result = userDAO.findAllowedSchemas((User) any, reference);
-        } else if (any instanceof Group) {
-            result = groupDAO.findAllowedSchemas((Group) any, reference);
-        } else if (any instanceof AnyObject) {
-            result = anyObjectDAO.findAllowedSchemas((AnyObject) any, reference);
+    public void addAttr(final String key, final PlainSchema schema, final String value) {
+        Any any = dao().find(key);
+        if (!dao().findAllowedSchemas(any, PlainSchema.class).forSelfContains(schema)) {
+            LOG.warn("Schema {} not allowed for {}, ignoring", schema, any);
+            return;
         }
 
-        return result;
+        PlainAttr<?> attr = (PlainAttr<?>) any.getPlainAttr(schema.getKey()).orElse(null);
+        if (attr == null) {
+            attr = newPlainAttr();
+            attr.setSchema(schema);
+            ((PlainAttr) attr).setOwner(any);
+            any.add(attr);
+
+            try {
+                attr.add(value, this);
+                dao().save(any);
+            } catch (InvalidPlainAttrValueException e) {
+                LOG.error("Invalid value for attribute {} and {}: {}", schema.getKey(), any, value, e);
+            }
+        } else {
+            LOG.debug("{} has already {} set: {}", any, schema.getKey(), attr.getValuesAsStrings());
+        }
     }
 }
index 0dd78ea..6335bcf 100644 (file)
@@ -210,13 +210,21 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex
     }
 
     @Override
-    public Optional<? extends Provision> getProvision(final ObjectClass objectClass) {
-        return getProvisions().stream().filter(provision -> provision.getObjectClass().equals(objectClass)).findFirst();
+    public Optional<? extends Provision> getProvision(final String anyType) {
+        return getProvisions().stream().
+                filter(provision -> provision.getAnyType().getKey().equals(anyType)).findFirst();
     }
 
     @Override
     public Optional<? extends Provision> getProvision(final AnyType anyType) {
-        return getProvisions().stream().filter(provision -> provision.getAnyType().equals(anyType)).findFirst();
+        return getProvisions().stream().
+                filter(provision -> provision.getAnyType().equals(anyType)).findFirst();
+    }
+
+    @Override
+    public Optional<? extends Provision> getProvision(final ObjectClass objectClass) {
+        return getProvisions().stream().
+                filter(provision -> provision.getObjectClass().equals(objectClass)).findFirst();
     }
 
     @Override
index 16e85c8..e5fc935 100644 (file)
@@ -38,12 +38,14 @@ import javax.validation.constraints.NotNull;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyType;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass;
+import org.apache.syncope.core.persistence.jpa.entity.JPAPlainSchema;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.identityconnectors.framework.common.objects.SyncToken;
 
@@ -80,6 +82,9 @@ public class JPAProvision extends AbstractGeneratedKeyEntity implements Provisio
     @Max(1)
     private Integer ignoreCaseMatch;
 
+    @ManyToOne
+    private JPAPlainSchema uidOnCreate;
+
     @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "provision")
     private JPAMapping mapping;
 
@@ -156,6 +161,17 @@ public class JPAProvision extends AbstractGeneratedKeyEntity implements Provisio
     }
 
     @Override
+    public PlainSchema getUidOnCreate() {
+        return uidOnCreate;
+    }
+
+    @Override
+    public void setUidOnCreate(final PlainSchema uidOnCreate) {
+        checkType(uidOnCreate, JPAPlainSchema.class);
+        this.uidOnCreate = (JPAPlainSchema) uidOnCreate;
+    }
+
+    @Override
     public Mapping getMapping() {
         return mapping;
     }
index 37e227f..fba6674 100644 (file)
@@ -58,7 +58,7 @@ public class AnyValidator extends AbstractValidator<AnyCheck, Any> {
 
         if (!(any instanceof Conf)) {
             AllowedSchemas<PlainSchema> allowedPlainSchemas = new JPAAnyUtilsFactory().
-                    getInstance(any.getType().getKind()).getAllowedSchemas(any, PlainSchema.class);
+                    getInstance(any.getType().getKind()).dao().findAllowedSchemas(any, PlainSchema.class);
 
             for (PlainAttr<?> attr : ((Any<?>) any).getPlainAttrs()) {
                 if (attr != null && !allowedPlainSchemas.forSelfContains(attr.getSchema().getKey())) {
index 42990d8..1673372 100644 (file)
@@ -62,8 +62,8 @@ public class DerAttrHandlerImpl implements DerAttrHandler {
 
     @Override
     public String getValue(final Any<?> any, final DerSchema schema) {
-        if (!anyUtilsFactory.getInstance(any).
-                getAllowedSchemas(any, DerSchema.class).forSelfContains(schema)) {
+        if (!anyUtilsFactory.getInstance(any).dao().
+                findAllowedSchemas(any, DerSchema.class).forSelfContains(schema)) {
 
             LOG.debug("{} not allowed for {}", schema, any);
             return null;
@@ -74,8 +74,8 @@ public class DerAttrHandlerImpl implements DerAttrHandler {
 
     @Override
     public String getValue(final Any<?> any, final Membership<?> membership, final DerSchema schema) {
-        if (!anyUtilsFactory.getInstance(any).
-                getAllowedSchemas(any, DerSchema.class).getForMembership(membership.getRightEnd()).contains(schema)) {
+        if (!anyUtilsFactory.getInstance(any).dao().
+                findAllowedSchemas(any, DerSchema.class).getForMembership(membership.getRightEnd()).contains(schema)) {
 
             LOG.debug("{} not allowed for {}", schema, any);
             return null;
@@ -88,7 +88,7 @@ public class DerAttrHandlerImpl implements DerAttrHandler {
     public Map<DerSchema, String> getValues(final Any<?> any) {
         return getValues(
                 any,
-                anyUtilsFactory.getInstance(any).getAllowedSchemas(any, DerSchema.class).getForSelf());
+                anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any, DerSchema.class).getForSelf());
     }
 
     private Map<DerSchema, String> getValues(
@@ -114,7 +114,7 @@ public class DerAttrHandlerImpl implements DerAttrHandler {
         return getValues(
                 any,
                 membership,
-                anyUtilsFactory.getInstance(any).getAllowedSchemas(any, DerSchema.class).
+                anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any, DerSchema.class).
                         getForMembership(membership.getRightEnd()));
     }
 
index b6c29ee..37f9364 100644 (file)
@@ -148,8 +148,8 @@ public class VirAttrHandlerImpl implements VirAttrHandler {
 
     @Override
     public List<String> getValues(final Any<?> any, final VirSchema schema) {
-        if (!anyUtilsFactory.getInstance(any).
-                getAllowedSchemas(any, VirSchema.class).forSelfContains(schema)) {
+        if (!anyUtilsFactory.getInstance(any).dao().
+                findAllowedSchemas(any, VirSchema.class).forSelfContains(schema)) {
 
             LOG.debug("{} not allowed for {}", schema, any);
             return Collections.emptyList();
@@ -161,8 +161,8 @@ public class VirAttrHandlerImpl implements VirAttrHandler {
 
     @Override
     public List<String> getValues(final Any<?> any, final Membership<?> membership, final VirSchema schema) {
-        if (!anyUtilsFactory.getInstance(any).
-                getAllowedSchemas(any, VirSchema.class).getForMembership(membership.getRightEnd()).contains(schema)) {
+        if (!anyUtilsFactory.getInstance(any).dao().
+                findAllowedSchemas(any, VirSchema.class).getForMembership(membership.getRightEnd()).contains(schema)) {
 
             LOG.debug("{} not allowed for {}", schema, any);
             return Collections.emptyList();
@@ -176,14 +176,14 @@ public class VirAttrHandlerImpl implements VirAttrHandler {
     public Map<VirSchema, List<String>> getValues(final Any<?> any) {
         return getValues(
                 any,
-                anyUtilsFactory.getInstance(any).getAllowedSchemas(any, VirSchema.class).getForSelf());
+                anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any, VirSchema.class).getForSelf());
     }
 
     @Override
     public Map<VirSchema, List<String>> getValues(final Any<?> any, final Membership<?> membership) {
         return getValues(
                 any,
-                anyUtilsFactory.getInstance(any).getAllowedSchemas(any, VirSchema.class).
+                anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any, VirSchema.class).
                         getForMembership(membership.getRightEnd()));
     }
 
index 5f5d83c..2ced472 100644 (file)
@@ -261,7 +261,7 @@ abstract class AbstractAnyDataBinder {
         SyncopeClientException reqValMissing = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
 
         // Check if there is some mandatory schema defined for which no value has been provided
-        AllowedSchemas<PlainSchema> allowedPlainSchemas = anyUtils.getAllowedSchemas(any, PlainSchema.class);
+        AllowedSchemas<PlainSchema> allowedPlainSchemas = anyUtils.dao().findAllowedSchemas(any, PlainSchema.class);
         allowedPlainSchemas.getForSelf().forEach(schema -> {
             checkMandatory(schema, any.getPlainAttr(schema.getKey()).orElse(null), any, reqValMissing);
         });
index 122e153..fedc288 100644 (file)
@@ -25,6 +25,7 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.collections.IteratorChain;
 import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -55,11 +56,13 @@ import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceHistoryConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
@@ -109,6 +112,9 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
     private ImplementationDAO implementationDAO;
 
     @Autowired
+    private PlainSchemaDAO plainSchemaDAO;
+
+    @Autowired
     private EntityFactory entityFactory;
 
     @Autowired
@@ -199,6 +205,15 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
 
                 provision.setIgnoreCaseMatch(provisionTO.isIgnoreCaseMatch());
 
+                if (StringUtils.isNotBlank(provisionTO.getUidOnCreate())) {
+                    PlainSchema uidOnCreate = plainSchemaDAO.find(provisionTO.getUidOnCreate());
+                    if (uidOnCreate == null) {
+                        LOG.warn("Ignoring invalid schema for uidOnCreate(): {}", provisionTO.getUidOnCreate());
+                    } else {
+                        provision.setUidOnCreate(uidOnCreate);
+                    }
+                }
+
                 if (provisionTO.getMapping() == null) {
                     provision.setMapping(null);
                 } else {
@@ -599,6 +614,9 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
                     map(cls -> cls.getKey()).collect(Collectors.toList()));
             provisionTO.setSyncToken(provision.getSerializedSyncToken());
             provisionTO.setIgnoreCaseMatch(provision.isIgnoreCaseMatch());
+            if (provision.getUidOnCreate() != null) {
+                provisionTO.setUidOnCreate(provision.getUidOnCreate().getKey());
+            }
 
             if (provision.getMapping() != null) {
                 MappingTO mappingTO = new MappingTO();
index a3e7132..b55eacc 100644 (file)
@@ -56,6 +56,7 @@ import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
@@ -103,22 +104,22 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
     protected ConnObjectUtils connObjectUtils;
 
     /**
-     * Any object DAO.
+     * User DAO.
      */
     @Autowired
-    protected AnyObjectDAO anyObjectDAO;
+    protected UserDAO userDAO;
 
     /**
-     * User DAO.
+     * Group DAO.
      */
     @Autowired
-    protected UserDAO userDAO;
+    protected GroupDAO groupDAO;
 
     /**
-     * User DAO.
+     * Any object DAO.
      */
     @Autowired
-    protected GroupDAO groupDAO;
+    protected AnyObjectDAO anyObjectDAO;
 
     /**
      * Task DAO.
@@ -151,6 +152,9 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
     protected TaskDataBinder taskDataBinder;
 
     @Autowired
+    protected AnyUtilsFactory anyUtilsFactory;
+
+    @Autowired
     protected TaskUtilsFactory taskUtilsFactory;
 
     @Autowired
@@ -178,6 +182,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
         return result;
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     protected Uid createOrUpdate(
             final PropagationTask task,
             final ConnectorObject beforeObj,
@@ -213,6 +218,13 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
             LOG.debug("Create {} on {}", attributes, task.getResource().getKey());
             result = connector.create(
                     new ObjectClass(task.getObjectClassName()), attributes, null, propagationAttempted);
+
+            task.getResource().getProvision(task.getAnyType()).ifPresent(provision -> {
+                if (provision.getUidOnCreate() != null) {
+                    anyUtilsFactory.getInstance(task.getAnyTypeKind()).
+                            addAttr(task.getEntityKey(), provision.getUidOnCreate(), result.getUidValue());
+                }
+            });
         } else {
             // 1. check if rename is really required
             Name newName = AttributeUtil.getNameFromAttributes(attributes);
index 4aec759..5d24eba 100644 (file)
@@ -20,22 +20,8 @@ package org.apache.syncope.core.provisioning.java.propagation;
 
 import java.util.HashSet;
 import java.util.Set;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.PlainSchema;
-import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr;
-import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
-import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.AttributeUtil;
@@ -43,48 +29,20 @@ import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.identityconnectors.framework.common.objects.Name;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
 /**
  * This class is required during setup of an External Resource based on the ConnId
  * <a href="https://github.com/Tirasa/ConnIdAzureBundle">Azure connector</a>.
  *
- * It manages:
- * <ol>
- * <li>the User id provided by Azure, which will need to be used for all subsequent operations</li>
- * <li>the Group id provided by Azure, which will need to be used for all subsequent operations</li>
- * </ol>
+ * It ensures to send the configured e-mail address as <pre>__NAME__</pre>.
  */
 public class AzurePropagationActions implements PropagationActions {
 
     private static final Logger LOG = LoggerFactory.getLogger(AzurePropagationActions.class);
 
-    @Autowired
-    private PlainSchemaDAO plainSchemaDAO;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private GroupDAO groupDAO;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private AnyUtilsFactory anyUtilsFactory;
-
-    private static final String USER_MAIL_NICKNAME = "mailNickname";
-
-    private static final String GROUP_MAIL_NICKNAME = "mailNickname";
-
-    protected String getAzureIdSchema() {
-        return "AzureUserId";
-    }
-
-    protected String getAzureGroupIdSchema() {
-        return "AzureGroupId";
+    protected String getEmailAttrName() {
+        return "mailNickname";
     }
 
     @Transactional
@@ -96,121 +54,32 @@ public class AzurePropagationActions implements PropagationActions {
 
         switch (task.getAnyTypeKind()) {
             case USER:
-                setName(task, USER_MAIL_NICKNAME);
+                setName(task);
                 break;
+
             case GROUP:
-                setName(task, GROUP_MAIL_NICKNAME);
-                break;
-            default:
-                LOG.debug("Not about user, or group, not doing anything");
+                setName(task);
                 break;
-        }
-    }
-
-    @Transactional
-    @Override
-    public void after(final PropagationTask task, final TaskExec execution, final ConnectorObject afterObj) {
-        if (task.getOperation() == ResourceOperation.DELETE || task.getOperation() == ResourceOperation.NONE) {
-            return;
-        }
-
-        if (AnyTypeKind.USER.equals(task.getAnyTypeKind())) {
-
-            User user = userDAO.find(task.getEntityKey());
-            if (user == null) {
-                LOG.error("Could not find user {}, skipping", task.getEntityKey());
-            } else {
-                boolean modified = false;
-                AnyUtils anyUtils = anyUtilsFactory.getInstance(user);
-
-                // Azure User ID
-                PlainSchema azureId = plainSchemaDAO.find(getAzureIdSchema());
-                if (azureId == null) {
-                    LOG.error("Could not find schema {}, skipping", getAzureIdSchema());
-                } else {
-                    // set back the __UID__ received by Azure
-                    UPlainAttr attr = user.getPlainAttr(getAzureIdSchema()).orElse(null);
-                    if (attr == null) {
-                        attr = entityFactory.newEntity(UPlainAttr.class);
-                        attr.setSchema(azureId);
-                        attr.setOwner(user);
-                        user.add(attr);
 
-                        try {
-                            attr.add(afterObj.getUid().getUidValue(), anyUtils);
-                            modified = true;
-                        } catch (InvalidPlainAttrValueException e) {
-                            LOG.error("Invalid value for attribute {}: {}",
-                                    azureId.getKey(), afterObj.getUid().getUidValue(), e);
-                        }
-                    } else {
-                        LOG.debug("User {} has already {} assigned: {}",
-                                user, azureId.getKey(), attr.getValuesAsStrings());
-                    }
-                }
-
-                if (modified) {
-                    userDAO.save(user);
-                }
-            }
-        } else if (AnyTypeKind.GROUP.equals(task.getAnyTypeKind())) {
-
-            Group group = groupDAO.find(task.getEntityKey());
-            if (group == null) {
-                LOG.error("Could not find group {}, skipping", task.getEntityKey());
-            } else {
-                boolean modified = false;
-                AnyUtils anyUtils = anyUtilsFactory.getInstance(group);
-
-                // Azure Group ID
-                PlainSchema azureId = plainSchemaDAO.find(getAzureGroupIdSchema());
-                if (azureId == null) {
-                    LOG.error("Could not find schema {}, skipping", getAzureGroupIdSchema());
-                } else {
-                    // set back the __UID__ received by Azure
-                    GPlainAttr attr = group.getPlainAttr(getAzureGroupIdSchema()).orElse(null);
-                    if (attr == null) {
-                        attr = entityFactory.newEntity(GPlainAttr.class);
-                        attr.setSchema(azureId);
-                        attr.setOwner(group);
-                        group.add(attr);
-
-                        try {
-                            attr.add(afterObj.getUid().getUidValue(), anyUtils);
-                            modified = true;
-                        } catch (InvalidPlainAttrValueException e) {
-                            LOG.error("Invalid value for attribute {}: {}",
-                                    azureId.getKey(), afterObj.getUid().getUidValue(), e);
-                        }
-                    } else {
-                        LOG.debug("Group {} has already {} assigned: {}",
-                                group, azureId.getKey(), attr.getValuesAsStrings());
-                    }
-                }
-
-                if (modified) {
-                    groupDAO.save(group);
-                }
-            }
+            default:
+                LOG.debug("Not about user or group: not doing anything");
         }
     }
 
-    private void setName(final PropagationTask task, final String attributeName) {
-        Set<Attribute> attributes = new HashSet<>(task.getAttributes());
+    private void setName(final PropagationTask task) {
+        Set<Attribute> attrs = new HashSet<>(task.getAttributes());
 
-        if (AttributeUtil.find(attributeName, attributes) == null) {
-            LOG.warn("Can't find {} attribute to set as __NAME__ attribute value, skipping...", attributeName);
+        if (AttributeUtil.find(getEmailAttrName(), attrs) == null) {
+            LOG.warn("Can't find {} attribute to set as __NAME__ attribute value, skipping...", getEmailAttrName());
             return;
         }
 
-        Name name = AttributeUtil.getNameFromAttributes(attributes);
+        Name name = AttributeUtil.getNameFromAttributes(attrs);
         if (name != null) {
-            attributes.remove(name);
+            attrs.remove(name);
         }
-        attributes.add(
-                new Name(AttributeUtil.find(attributeName, attributes).getValue().get(0).toString()));
+        attrs.add(new Name(AttributeUtil.find(getEmailAttrName(), attrs).getValue().get(0).toString()));
 
-        task.setAttributes(attributes);
+        task.setAttributes(attrs);
     }
-
 }
index 1da3578..c4ea9a4 100644 (file)
@@ -22,18 +22,7 @@ import java.util.HashSet;
 import java.util.Set;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException;
-import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
-import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.AttributeUtil;
@@ -41,44 +30,20 @@ import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.identityconnectors.framework.common.objects.Name;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
 /**
  * This class is required during setup of an External Resource based on the ConnId
  * <a href="https://github.com/Tirasa/ConnIdGoogleAppsBundle">GoogleApps connector</a>.
  *
- * It manages:
- * <ol>
- * <li>the id provided by Google, which will need to be used for all subsequent operations</li>
- * <li>the e-mail address</li>
- * </ol>
+ * It ensures to send the configured e-mail address as <pre>__NAME__</pre>.
  */
 public class GoogleAppsPropagationActions implements PropagationActions {
 
     private static final Logger LOG = LoggerFactory.getLogger(GoogleAppsPropagationActions.class);
 
-    @Autowired
-    private PlainSchemaDAO plainSchemaDAO;
-
-    @Autowired
-    protected PlainAttrValueDAO plainAttrValueDAO;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private AnyUtilsFactory anyUtilsFactory;
-
-    protected String getEmailSchema() {
-        return "email";
-    }
-
-    protected String getGoogleAppsIdSchema() {
-        return "GoogleAppsId";
+    protected String getEmailAttrName() {
+        return "emails";
     }
 
     @Transactional
@@ -93,67 +58,17 @@ public class GoogleAppsPropagationActions implements PropagationActions {
 
         Set<Attribute> attrs = new HashSet<>(task.getAttributes());
 
-        // ensure to set __NAME__ value to user's email (e.g. primary e-mail address)
-        User user = userDAO.find(task.getEntityKey());
-        if (user == null) {
-            LOG.error("Could not find user {}, skipping", task.getEntityKey());
-        } else {
-            Name name = AttributeUtil.getNameFromAttributes(attrs);
-            if (name != null) {
-                attrs.remove(name);
-            }
-            attrs.add(new Name(user.getPlainAttr(getEmailSchema()).get().getValuesAsStrings().get(0)));
-        }
-
-        task.setAttributes(attrs);
-    }
-
-    @Transactional
-    @Override
-    public void after(final PropagationTask task, final TaskExec execution, final ConnectorObject afterObj) {
-        if (task.getOperation() == ResourceOperation.DELETE || task.getOperation() == ResourceOperation.NONE) {
-            return;
-        }
-        if (AnyTypeKind.USER != task.getAnyTypeKind()) {
+        if (AttributeUtil.find(getEmailAttrName(), attrs) == null) {
+            LOG.warn("Can't find {} attribute to set as __NAME__ attribute value, skipping...", getEmailAttrName());
             return;
         }
 
-        User user = userDAO.find(task.getEntityKey());
-        if (user == null) {
-            LOG.error("Could not find user {}, skipping", task.getEntityKey());
-        } else {
-            boolean modified = false;
-            AnyUtils anyUtils = anyUtilsFactory.getInstance(user);
-
-            PlainSchema googleAppsId = plainSchemaDAO.find(getGoogleAppsIdSchema());
-            if (googleAppsId == null) {
-                LOG.error("Could not find schema {}, skipping", getGoogleAppsIdSchema());
-            } else {
-                // set back the __UID__ received by Google
-                UPlainAttr attr = user.getPlainAttr(getGoogleAppsIdSchema()).orElse(null);
-                if (attr == null) {
-                    attr = entityFactory.newEntity(UPlainAttr.class);
-                    attr.setSchema(googleAppsId);
-                    attr.setOwner(user);
-                    user.add(attr);
-
-                    try {
-                        attr.add(afterObj.getUid().getUidValue(), anyUtils);
-                        modified = true;
-                    } catch (InvalidPlainAttrValueException e) {
-                        LOG.error("Invalid value for attribute {}: {}",
-                                googleAppsId.getKey(), afterObj.getUid().getUidValue(), e);
-                    }
-                } else {
-                    LOG.debug("User {} has already {} assigned: {}",
-                            user, googleAppsId.getKey(), attr.getValuesAsStrings());
-                }
-            }
-
-            if (modified) {
-                userDAO.save(user);
-            }
+        Name name = AttributeUtil.getNameFromAttributes(attrs);
+        if (name != null) {
+            attrs.remove(name);
         }
-    }
+        attrs.add(new Name(AttributeUtil.find(getEmailAttrName(), attrs).getValue().get(0).toString()));
 
+        task.setAttributes(attrs);
+    }
 }
index 2b8f314..7f4e06c 100644 (file)
@@ -340,7 +340,8 @@ public class PropagationManagerImpl implements PropagationManager {
                     LOG.warn("Ignoring invalid {} {}", VirSchema.class.getSimpleName(), vAttr.getSchema());
                 } else if (schema.isReadonly()) {
                     LOG.warn("Ignoring read-only {} {}", VirSchema.class.getSimpleName(), vAttr.getSchema());
-                } else if (anyUtilsFactory.getInstance(any).getAllowedSchemas(any, VirSchema.class).contains(schema)
+                } else if (anyUtilsFactory.getInstance(any).dao().
+                        findAllowedSchemas(any, VirSchema.class).contains(schema)
                         && virtualResources.contains(schema.getProvision().getResource().getKey())) {
 
                     Set<Attribute> values = vAttrMap.get(schema.getProvision().getResource().getKey());
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/SCIMv11PropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/SCIMv11PropagationActions.java
deleted file mode 100644 (file)
index 3aee4aa..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.java.propagation;
-
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException;
-import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.PlainSchema;
-import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
-import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * This class is required during setup of an External Resource based on the ConnId
- * <a href="https://github.com/Tirasa/ConnIdSCIMv11Bundle">SCIM connector</a>.
- *
- * It manages:
- * <ol>
- * <li>the User id provided by SCIM, which will need to be used for all subsequent operations</li>
- * </ol>
- */
-public class SCIMv11PropagationActions implements PropagationActions {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SCIMv11PropagationActions.class);
-
-    @Autowired
-    private PlainSchemaDAO plainSchemaDAO;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private AnyUtilsFactory anyUtilsFactory;
-
-    protected String getSCIMIdSchema() {
-        return "SCIMUserId";
-    }
-
-    @Transactional
-    @Override
-    public void after(final PropagationTask task, final TaskExec execution, final ConnectorObject afterObj) {
-        if (task.getOperation() == ResourceOperation.DELETE || task.getOperation() == ResourceOperation.NONE) {
-            return;
-        }
-
-        if (AnyTypeKind.USER.equals(task.getAnyTypeKind())) {
-
-            User user = userDAO.find(task.getEntityKey());
-            if (user == null) {
-                LOG.error("Could not find user {}, skipping", task.getEntityKey());
-            } else {
-                boolean modified = false;
-                AnyUtils anyUtils = anyUtilsFactory.getInstance(user);
-
-                // SCIM v1.1 User ID
-                PlainSchema userId = plainSchemaDAO.find(getSCIMIdSchema());
-                if (userId == null) {
-                    LOG.error("Could not find schema {}, skipping", getSCIMIdSchema());
-                } else {
-                    // set back the __UID__ received by SCIM service
-                    UPlainAttr attr = user.getPlainAttr(getSCIMIdSchema()).orElse(null);
-                    if (attr == null) {
-                        attr = entityFactory.newEntity(UPlainAttr.class);
-                        attr.setSchema(userId);
-                        attr.setOwner(user);
-                        user.add(attr);
-
-                        try {
-                            attr.add(afterObj.getUid().getUidValue(), anyUtils);
-                            modified = true;
-                        } catch (InvalidPlainAttrValueException e) {
-                            LOG.error("Invalid value for attribute {}: {}",
-                                    userId.getKey(), afterObj.getUid().getUidValue(), e);
-                        }
-                    } else {
-                        LOG.debug("User {} has already {} assigned: {}",
-                                user, userId.getKey(), attr.getValuesAsStrings());
-                    }
-                }
-
-                if (modified) {
-                    userDAO.save(user);
-                }
-            }
-        }
-    }
-
-}
index 35e0356..c931e1d 100644 (file)
@@ -821,13 +821,16 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
 
             if (SyncDeltaType.CREATE_OR_UPDATE == processed.getDeltaType()) {
                 if (anyKeys.isEmpty()) {
+                    List<ProvisioningReport> forUidOnCreate = null;
                     switch (profile.getTask().getUnmatchingRule()) {
                         case ASSIGN:
-                            profile.getResults().addAll(assign(processed, provision, anyUtils));
+                            forUidOnCreate = assign(processed, provision, anyUtils);
+                            profile.getResults().addAll(forUidOnCreate);
                             break;
 
                         case PROVISION:
-                            profile.getResults().addAll(provision(processed, provision, anyUtils));
+                            forUidOnCreate = provision(processed, provision, anyUtils);
+                            profile.getResults().addAll(forUidOnCreate);
                             break;
 
                         case IGNORE:
@@ -837,6 +840,12 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
                         default:
                         // do nothing
                     }
+
+                    if (forUidOnCreate != null && provision.getUidOnCreate() != null) {
+                        forUidOnCreate.forEach(report -> {
+                            anyUtils.addAttr(report.getKey(), provision.getUidOnCreate(), delta.getUid().getUidValue());
+                        });
+                    }
                 } else {
                     // update VirAttrCache
                     for (VirSchema virSchema : virSchemaDAO.findByProvision(provision)) {
index 6418f9c..446997e 100644 (file)
@@ -72,10 +72,6 @@ public class GoogleAppsPullActions implements PullActions {
 
     private final Map<String, String> googleAppsIds = new HashMap<>();
 
-    protected String getEmailSchema() {
-        return "email";
-    }
-
     protected String getGoogleAppsIdSchema() {
         return "GoogleAppsId";
     }
index d3a0f6e..352692e 100644 (file)
@@ -70,7 +70,6 @@ import org.apache.syncope.core.provisioning.java.propagation.DBPasswordPropagati
 import org.apache.syncope.core.provisioning.java.propagation.GoogleAppsPropagationActions;
 import org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions;
 import org.apache.syncope.core.provisioning.java.propagation.LDAPPasswordPropagationActions;
-import org.apache.syncope.core.provisioning.java.propagation.SCIMv11PropagationActions;
 import org.apache.syncope.core.provisioning.java.pushpull.DBPasswordPullActions;
 import org.apache.syncope.core.provisioning.java.pushpull.LDAPMembershipPullActions;
 import org.apache.syncope.core.provisioning.java.pushpull.LDAPPasswordPullActions;
@@ -184,7 +183,6 @@ public class ITImplementationLookup implements ImplementationLookup {
             classNames.add(LDAPPasswordPropagationActions.class.getName());
             classNames.add(DBPasswordPropagationActions.class.getName());
             classNames.add(AzurePropagationActions.class.getName());
-            classNames.add(SCIMv11PropagationActions.class.getName());
             classNames.add(GoogleAppsPropagationActions.class.getName());
             put(ImplementationType.PROPAGATION_ACTIONS, classNames);
 
index b97339a..8bc3e92 100644 (file)
@@ -153,16 +153,17 @@ complex statements to be expressed such as 'be mandatory only if this other attr
 
 Besides the items documented above, some more data needs to be specified for a complete mapping:
 
-* ConnId `objectClass` - which
+* which
 http://connid.tirasa.net/apidocs/1.4/org/identityconnectors/framework/common/objects/ObjectClass.html[object class^]
 shall be used during communication with the Identity Store; predefined are `\\__ACCOUNT__` for Users and 
 `\\__GROUP__` for Groups
 * whether matches between user / group / any object's attribute values and their counterparts on the Identity Store
 should be performed in a case-sensitive fashion or not
-* Object link - only required by some connector bundles as
+* which schema shall be used to hold values for identifiers generated upon create by the Identity Store - required by
+some cloud providers not accepting provided values as unique references
+* the model for generating the DN (distinguished name) values - only required by some connector bundles as
 https://connid.atlassian.net/wiki/display/BASE/LDAP[LDAP^] and
-https://connid.atlassian.net/wiki/pages/viewpage.action?pageId=360482[Active Directory^], generally specifies the model
-for generating the DN (distinguished name) values
+https://connid.atlassian.net/wiki/pages/viewpage.action?pageId=360482[Active Directory^]
 
 .Mapping items
 ====