syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [4/8] syncope git commit: [SYNCOPE-663] Throwing new IgnoreException from SyncActions' or PushActions's methods will make the related user / role to get ignored by the sync / push process
Date Thu, 30 Apr 2015 10:54:35 GMT
http://git-wip-us.apache.org/repos/asf/syncope/blob/1a133b77/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
index 5f006d0,0000000..8241937
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncResultHandler.java
@@@ -1,651 -1,0 +1,659 @@@
 +/*
 + * 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.sync;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import org.apache.commons.lang3.exception.ExceptionUtils;
 +import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
 +import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 +import org.apache.syncope.common.lib.types.AuditElements;
 +import org.apache.syncope.common.lib.types.AuditElements.Result;
 +import org.apache.syncope.common.lib.types.MatchingRule;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.common.lib.types.UnmatchingRule;
 +import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 +import org.apache.syncope.core.persistence.api.entity.AttributableUtils;
 +import org.apache.syncope.core.persistence.api.entity.task.SyncTask;
 +import org.apache.syncope.core.provisioning.api.AttributableTransformer;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 +import org.apache.syncope.core.provisioning.api.sync.SyncActions;
 +import org.apache.syncope.core.misc.security.UnauthorizedException;
++import org.apache.syncope.core.provisioning.api.sync.IgnoreProvisionException;
 +import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult;
 +import org.apache.syncope.core.provisioning.api.sync.SyncopeSyncResultHandler;
 +import org.identityconnectors.framework.common.objects.SyncDelta;
 +import org.identityconnectors.framework.common.objects.SyncDeltaType;
 +import org.quartz.JobExecutionException;
 +import org.springframework.beans.factory.annotation.Autowired;
 +
 +public abstract class AbstractSyncResultHandler extends AbstractSyncopeResultHandler<SyncTask, SyncActions>
 +        implements SyncopeSyncResultHandler {
 +
 +    @Autowired
 +    protected SyncUtils syncUtilities;
 +
 +    @Autowired
 +    protected AttributableTransformer attrTransformer;
 +
-     protected abstract AttributableUtils getAttributableUtils();
- 
 +    protected abstract String getName(AbstractSubjectTO subjectTO);
 +
-     protected abstract AbstractSubjectTO getSubjectTO(long key);
- 
 +    protected abstract AbstractSubjectMod getSubjectMod(AbstractSubjectTO subjectTO, SyncDelta delta);
 +
-     protected abstract AbstractSubjectTO create(
++    protected abstract AbstractSubjectTO doCreate(
 +            AbstractSubjectTO subjectTO, SyncDelta _delta, ProvisioningResult result);
 +
-     protected abstract AbstractSubjectTO link(AbstractSubjectTO before, ProvisioningResult result, boolean unlink);
++    protected abstract AbstractSubjectTO doLink(AbstractSubjectTO before, ProvisioningResult result, boolean unlink);
 +
-     protected abstract AbstractSubjectTO update(AbstractSubjectTO before, AbstractSubjectMod subjectMod,
++    protected abstract AbstractSubjectTO doUpdate(AbstractSubjectTO before, AbstractSubjectMod subjectMod,
 +            SyncDelta delta, ProvisioningResult result);
 +
-     protected abstract void deprovision(Long key, boolean unlink);
++    protected abstract void doDeprovision(Long key, boolean unlink);
 +
-     protected abstract void delete(Long key);
++    protected abstract void doDelete(Long key);
 +
 +    @Override
 +    public boolean handle(final SyncDelta delta) {
 +        try {
 +            doHandle(delta);
 +            return true;
++        } catch (IgnoreProvisionException e) {
++            ProvisioningResult result = new ProvisioningResult();
++            result.setOperation(ResourceOperation.NONE);
++            result.setSubjectType(getAttributableUtils().getType());
++            result.setStatus(ProvisioningResult.Status.IGNORE);
++            result.setKey(0L);
++            result.setName(delta.getObject().getName().getNameValue());
++
++            LOG.warn("Ignoring during synchronization", e);
++            return true;
 +        } catch (JobExecutionException e) {
 +            LOG.error("Synchronization failed", e);
 +            return false;
 +        }
 +    }
 +
 +    protected List<ProvisioningResult> assign(final SyncDelta delta, final AttributableUtils attrUtils)
 +            throws JobExecutionException {
++
 +        if (!profile.getTask().isPerformCreate()) {
 +            LOG.debug("SyncTask not configured for create");
 +            return Collections.<ProvisioningResult>emptyList();
 +        }
 +
 +        final AbstractSubjectTO subjectTO =
 +                connObjectUtils.getSubjectTO(delta.getObject(), profile.getTask(), attrUtils);
 +
 +        subjectTO.getResources().add(profile.getTask().getResource().getKey());
 +
 +        final ProvisioningResult result = new ProvisioningResult();
 +        result.setOperation(ResourceOperation.CREATE);
 +        result.setSubjectType(attrUtils.getType());
 +        result.setStatus(ProvisioningResult.Status.SUCCESS);
 +
 +        // Attributable transformation (if configured)
 +        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
 +        LOG.debug("Transformed: {}", transformed);
 +
 +        result.setName(getName(transformed));
 +
 +        if (profile.isDryRun()) {
-             result.setId(0L);
++            result.setKey(0L);
 +        } else {
 +            SyncDelta _delta = delta;
 +            for (SyncActions action : profile.getActions()) {
 +                _delta = action.beforeAssign(this.getProfile(), _delta, transformed);
 +            }
 +
 +            create(transformed, _delta, attrUtils, UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), result);
 +        }
 +
 +        return Collections.singletonList(result);
 +    }
 +
-     protected List<ProvisioningResult> create(final SyncDelta delta, final AttributableUtils attrUtils)
++    protected List<ProvisioningResult> provision(final SyncDelta delta, final AttributableUtils attrUtils)
 +            throws JobExecutionException {
 +
 +        if (!profile.getTask().isPerformCreate()) {
 +            LOG.debug("SyncTask not configured for create");
 +            return Collections.<ProvisioningResult>emptyList();
 +        }
 +
 +        final AbstractSubjectTO subjectTO =
 +                connObjectUtils.getSubjectTO(delta.getObject(), profile.getTask(), attrUtils);
 +
 +        // Attributable transformation (if configured)
 +        AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
 +        LOG.debug("Transformed: {}", transformed);
 +
 +        final ProvisioningResult result = new ProvisioningResult();
 +        result.setOperation(ResourceOperation.CREATE);
 +        result.setSubjectType(attrUtils.getType());
 +        result.setStatus(ProvisioningResult.Status.SUCCESS);
 +
 +        result.setName(getName(transformed));
 +
 +        if (profile.isDryRun()) {
-             result.setId(0L);
++            result.setKey(0L);
 +        } else {
 +            SyncDelta _delta = delta;
 +            for (SyncActions action : profile.getActions()) {
 +                _delta = action.beforeProvision(this.getProfile(), _delta, transformed);
 +            }
 +
 +            create(transformed, _delta, attrUtils, UnmatchingRule.toEventName(UnmatchingRule.PROVISION), result);
 +        }
 +
 +        return Collections.<ProvisioningResult>singletonList(result);
 +    }
 +
 +    private void create(
 +            final AbstractSubjectTO subjectTO,
 +            final SyncDelta delta,
 +            final AttributableUtils attrUtils,
 +            final String operation,
 +            final ProvisioningResult result)
 +            throws JobExecutionException {
 +
 +        Object output;
 +        Result resultStatus;
 +
 +        try {
-             AbstractSubjectTO actual = create(subjectTO, delta, result);
++            AbstractSubjectTO actual = doCreate(subjectTO, delta, result);
 +            result.setName(getName(actual));
 +            output = actual;
 +            resultStatus = Result.SUCCESS;
 +
 +            for (SyncActions action : profile.getActions()) {
 +                action.after(this.getProfile(), delta, actual, result);
 +            }
 +        } catch (PropagationException e) {
 +            // A propagation failure doesn't imply a synchronization failure.
 +            // The propagation exception status will be reported into the propagation task execution.
 +            LOG.error("Could not propagate {} {}", attrUtils.getType(), delta.getUid().getUidValue(), e);
 +            output = e;
 +            resultStatus = Result.FAILURE;
 +        } catch (Exception e) {
 +            result.setStatus(ProvisioningResult.Status.FAILURE);
 +            result.setMessage(ExceptionUtils.getRootCauseMessage(e));
 +            LOG.error("Could not create {} {} ", attrUtils.getType(), delta.getUid().getUidValue(), e);
 +            output = e;
 +            resultStatus = Result.FAILURE;
 +        }
 +
 +        audit(operation, resultStatus, null, output, delta);
 +    }
 +
 +    protected List<ProvisioningResult> update(SyncDelta delta, final List<Long> subjects,
 +            final AttributableUtils attrUtils)
 +            throws JobExecutionException {
 +
 +        if (!profile.getTask().isPerformUpdate()) {
 +            LOG.debug("SyncTask not configured for update");
 +            return Collections.<ProvisioningResult>emptyList();
 +        }
 +
 +        LOG.debug("About to update {}", subjects);
 +
 +        List<ProvisioningResult> results = new ArrayList<>();
 +
 +        for (Long key : subjects) {
 +            LOG.debug("About to update {}", key);
 +
 +            final ProvisioningResult result = new ProvisioningResult();
 +            result.setOperation(ResourceOperation.UPDATE);
 +            result.setSubjectType(attrUtils.getType());
 +            result.setStatus(ProvisioningResult.Status.SUCCESS);
-             result.setId(key);
++            result.setKey(key);
 +
 +            AbstractSubjectTO before = getSubjectTO(key);
 +            if (before == null) {
 +                result.setStatus(ProvisioningResult.Status.FAILURE);
 +                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtils.getType().name(), key));
 +            } else {
 +                result.setName(getName(before));
 +            }
 +
 +            Result resultStatus;
 +            Object output;
 +            if (!profile.isDryRun()) {
 +                if (before == null) {
 +                    resultStatus = Result.FAILURE;
 +                    output = null;
 +                } else {
 +                    try {
 +                        final AbstractSubjectMod attributableMod = getSubjectMod(before, delta);
 +
 +                        // Attribute value transformation (if configured)
 +                        final AbstractSubjectMod actual = attrTransformer.transform(attributableMod);
 +                        LOG.debug("Transformed: {}", actual);
 +
 +                        for (SyncActions action : profile.getActions()) {
 +                            delta = action.beforeUpdate(this.getProfile(), delta, before, attributableMod);
 +                        }
 +
-                         final AbstractSubjectTO updated = update(before, attributableMod, delta, result);
++                        final AbstractSubjectTO updated = doUpdate(before, attributableMod, delta, result);
 +
 +                        for (SyncActions action : profile.getActions()) {
 +                            action.after(this.getProfile(), delta, updated, result);
 +                        }
 +
 +                        output = updated;
 +                        resultStatus = Result.SUCCESS;
 +                        result.setName(getName(updated));
 +                        LOG.debug("{} {} successfully updated", attrUtils.getType(), key);
 +                    } catch (PropagationException e) {
 +                        // A propagation failure doesn't imply a synchronization failure.
 +                        // The propagation exception status will be reported into the propagation task execution.
 +                        LOG.error("Could not propagate {} {}", attrUtils.getType(), delta.getUid().getUidValue(), e);
 +                        output = e;
 +                        resultStatus = Result.FAILURE;
 +                    } catch (Exception e) {
 +                        result.setStatus(ProvisioningResult.Status.FAILURE);
 +                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
 +                        LOG.error("Could not update {} {}", attrUtils.getType(), delta.getUid().getUidValue(), e);
 +                        output = e;
 +                        resultStatus = Result.FAILURE;
 +                    }
 +                }
 +                audit(MatchingRule.toEventName(MatchingRule.UPDATE), resultStatus, before, output, delta);
 +            }
 +            results.add(result);
 +        }
 +        return results;
 +    }
 +
 +    protected List<ProvisioningResult> deprovision(
 +            SyncDelta delta,
 +            final List<Long> subjects,
 +            final AttributableUtils attrUtils,
 +            final boolean unlink)
 +            throws JobExecutionException {
 +
 +        if (!profile.getTask().isPerformUpdate()) {
 +            LOG.debug("SyncTask not configured for update");
 +            return Collections.<ProvisioningResult>emptyList();
 +        }
 +
 +        LOG.debug("About to update {}", subjects);
 +
 +        final List<ProvisioningResult> updResults = new ArrayList<>();
 +
 +        for (Long id : subjects) {
 +            LOG.debug("About to unassign resource {}", id);
 +
 +            Object output;
 +            Result resultStatus;
 +
 +            final ProvisioningResult result = new ProvisioningResult();
 +            result.setOperation(ResourceOperation.DELETE);
 +            result.setSubjectType(attrUtils.getType());
 +            result.setStatus(ProvisioningResult.Status.SUCCESS);
-             result.setId(id);
++            result.setKey(id);
 +
 +            final AbstractSubjectTO before = getSubjectTO(id);
 +
 +            if (before == null) {
 +                result.setStatus(ProvisioningResult.Status.FAILURE);
 +                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtils.getType().name(), id));
 +            }
 +
 +            if (!profile.isDryRun()) {
 +                if (before == null) {
 +                    resultStatus = Result.FAILURE;
 +                    output = null;
 +                } else {
 +                    result.setName(getName(before));
 +
 +                    try {
 +                        if (unlink) {
 +                            for (SyncActions action : profile.getActions()) {
 +                                action.beforeUnassign(this.getProfile(), delta, before);
 +                            }
 +                        } else {
 +                            for (SyncActions action : profile.getActions()) {
 +                                action.beforeDeprovision(this.getProfile(), delta, before);
 +                            }
 +                        }
 +
-                         deprovision(id, unlink);
++                        doDeprovision(id, unlink);
 +                        output = getSubjectTO(id);
 +
 +                        for (SyncActions action : profile.getActions()) {
 +                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
 +                        }
 +
 +                        resultStatus = Result.SUCCESS;
 +                        LOG.debug("{} {} successfully updated", attrUtils.getType(), id);
 +                    } catch (PropagationException e) {
 +                        // A propagation failure doesn't imply a synchronization failure.
 +                        // The propagation exception status will be reported into the propagation task execution.
 +                        LOG.error("Could not propagate {} {}", attrUtils.getType(), delta.getUid().getUidValue(), e);
 +                        output = e;
 +                        resultStatus = Result.FAILURE;
 +                    } catch (Exception e) {
 +                        result.setStatus(ProvisioningResult.Status.FAILURE);
 +                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
 +                        LOG.error("Could not update {} {}", attrUtils.getType(), delta.getUid().getUidValue(), e);
 +                        output = e;
 +                        resultStatus = Result.FAILURE;
 +                    }
 +                }
 +                audit(unlink
 +                        ? MatchingRule.toEventName(MatchingRule.UNASSIGN)
 +                        : MatchingRule.toEventName(MatchingRule.DEPROVISION), resultStatus, before, output, delta);
 +            }
 +            updResults.add(result);
 +        }
 +
 +        return updResults;
 +    }
 +
 +    protected List<ProvisioningResult> link(
 +            SyncDelta delta,
 +            final List<Long> subjects,
 +            final AttributableUtils attrUtils,
 +            final boolean unlink)
 +            throws JobExecutionException {
 +
 +        if (!profile.getTask().isPerformUpdate()) {
 +            LOG.debug("SyncTask not configured for update");
 +            return Collections.<ProvisioningResult>emptyList();
 +        }
 +
 +        LOG.debug("About to update {}", subjects);
 +
 +        final List<ProvisioningResult> updResults = new ArrayList<>();
 +
 +        for (Long id : subjects) {
 +            LOG.debug("About to unassign resource {}", id);
 +
 +            Object output;
 +            Result resultStatus;
 +
 +            final ProvisioningResult result = new ProvisioningResult();
 +            result.setOperation(ResourceOperation.NONE);
 +            result.setSubjectType(attrUtils.getType());
 +            result.setStatus(ProvisioningResult.Status.SUCCESS);
-             result.setId(id);
++            result.setKey(id);
 +
 +            final AbstractSubjectTO before = getSubjectTO(id);
 +
 +            if (before == null) {
 +                result.setStatus(ProvisioningResult.Status.FAILURE);
 +                result.setMessage(String.format("Subject '%s(%d)' not found", attrUtils.getType().name(), id));
 +            }
 +
 +            if (!profile.isDryRun()) {
 +                if (before == null) {
 +                    resultStatus = Result.FAILURE;
 +                    output = null;
 +                } else {
 +                    result.setName(getName(before));
 +
 +                    try {
 +                        if (unlink) {
 +                            for (SyncActions action : profile.getActions()) {
 +                                action.beforeUnlink(this.getProfile(), delta, before);
 +                            }
 +                        } else {
 +                            for (SyncActions action : profile.getActions()) {
 +                                action.beforeLink(this.getProfile(), delta, before);
 +                            }
 +                        }
 +
-                         output = link(before, result, unlink);
++                        output = doLink(before, result, unlink);
 +
 +                        for (SyncActions action : profile.getActions()) {
 +                            action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
 +                        }
 +
 +                        resultStatus = Result.SUCCESS;
 +                        LOG.debug("{} {} successfully updated", attrUtils.getType(), id);
 +                    } catch (PropagationException e) {
 +                        // A propagation failure doesn't imply a synchronization failure.
 +                        // The propagation exception status will be reported into the propagation task execution.
 +                        LOG.error("Could not propagate {} {}", attrUtils.getType(), delta.getUid().getUidValue(), e);
 +                        output = e;
 +                        resultStatus = Result.FAILURE;
 +                    } catch (Exception e) {
 +                        result.setStatus(ProvisioningResult.Status.FAILURE);
 +                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
 +                        LOG.error("Could not update {} {}", attrUtils.getType(), delta.getUid().getUidValue(), e);
 +                        output = e;
 +                        resultStatus = Result.FAILURE;
 +                    }
 +                }
 +                audit(unlink ? MatchingRule.toEventName(MatchingRule.UNLINK)
 +                        : MatchingRule.toEventName(MatchingRule.LINK), resultStatus, before, output, delta);
 +            }
 +            updResults.add(result);
 +        }
 +
 +        return updResults;
 +    }
 +
 +    protected List<ProvisioningResult> delete(
 +            SyncDelta delta, final List<Long> subjects, final AttributableUtils attrUtils)
 +            throws JobExecutionException {
 +
 +        if (!profile.getTask().isPerformDelete()) {
 +            LOG.debug("SyncTask not configured for delete");
 +            return Collections.<ProvisioningResult>emptyList();
 +        }
 +
 +        LOG.debug("About to delete {}", subjects);
 +
 +        List<ProvisioningResult> delResults = new ArrayList<>();
 +
 +        for (Long id : subjects) {
 +            Object output;
 +            Result resultStatus = Result.FAILURE;
 +
 +            AbstractSubjectTO before = null;
 +            final ProvisioningResult result = new ProvisioningResult();
 +
 +            try {
 +                before = getSubjectTO(id);
 +
-                 result.setId(id);
++                result.setKey(id);
 +                result.setName(getName(before));
 +                result.setOperation(ResourceOperation.DELETE);
 +                result.setSubjectType(attrUtils.getType());
 +                result.setStatus(ProvisioningResult.Status.SUCCESS);
 +
 +                if (!profile.isDryRun()) {
 +                    for (SyncActions action : profile.getActions()) {
 +                        delta = action.beforeDelete(this.getProfile(), delta, before);
 +                    }
 +
 +                    try {
-                         delete(id);
++                        doDelete(id);
 +                        output = null;
 +                        resultStatus = Result.SUCCESS;
 +                    } catch (Exception e) {
 +                        result.setStatus(ProvisioningResult.Status.FAILURE);
 +                        result.setMessage(ExceptionUtils.getRootCauseMessage(e));
 +                        LOG.error("Could not delete {} {}", attrUtils.getType(), id, e);
 +                        output = e;
 +                    }
 +
 +                    for (SyncActions action : profile.getActions()) {
 +                        action.after(this.getProfile(), delta, before, result);
 +                    }
 +
 +                    audit(ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, output, delta);
 +                }
 +
 +                delResults.add(result);
 +
 +            } catch (NotFoundException e) {
 +                LOG.error("Could not find {} {}", attrUtils.getType(), id, e);
 +            } catch (UnauthorizedException e) {
 +                LOG.error("Not allowed to read {} {}", attrUtils.getType(), id, e);
 +            } catch (Exception e) {
 +                LOG.error("Could not delete {} {}", attrUtils.getType(), id, e);
 +            }
 +        }
 +
 +        return delResults;
 +    }
 +
 +    private List<ProvisioningResult> ignore(SyncDelta delta, final AttributableUtils attrUtils, final boolean matching)
 +            throws JobExecutionException {
 +
 +        LOG.debug("Subject to ignore {}", delta.getObject().getUid().getUidValue());
 +
 +        final List<ProvisioningResult> ignoreResults = new ArrayList<>();
 +        final ProvisioningResult result = new ProvisioningResult();
 +
-         result.setId(null);
++        result.setKey(null);
 +        result.setName(delta.getObject().getUid().getUidValue());
 +        result.setOperation(ResourceOperation.NONE);
 +        result.setSubjectType(attrUtils.getType());
 +        result.setStatus(ProvisioningResult.Status.SUCCESS);
 +        ignoreResults.add(result);
 +
 +        if (!profile.isDryRun()) {
 +            audit(matching
 +                    ? MatchingRule.toEventName(MatchingRule.IGNORE)
 +                    : UnmatchingRule.toEventName(UnmatchingRule.IGNORE), Result.SUCCESS, null, null, delta);
 +        }
 +
 +        return ignoreResults;
 +    }
 +
 +    /**
 +     * Look into SyncDelta and take necessary profile.getActions() (create / update / delete) on user(s)/group(s).
 +     *
 +     * @param delta returned by the underlying profile.getConnector()
 +     * @throws JobExecutionException in case of synchronization failure.
 +     */
-     protected final void doHandle(final SyncDelta delta)
++    protected void doHandle(final SyncDelta delta)
 +            throws JobExecutionException {
 +
 +        final AttributableUtils attrUtils = getAttributableUtils();
 +
 +        LOG.debug("Process {} for {} as {}",
 +                delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass());
 +
 +        final String uid = delta.getPreviousUid() == null
 +                ? delta.getUid().getUidValue()
 +                : delta.getPreviousUid().getUidValue();
 +
 +        try {
 +            List<Long> subjectKeys = syncUtilities.findExisting(
 +                    uid, delta.getObject(), profile.getTask().getResource(), attrUtils);
 +
 +            if (subjectKeys.size() > 1) {
 +                switch (profile.getResAct()) {
 +                    case IGNORE:
 +                        throw new IllegalStateException("More than one match " + subjectKeys);
 +
 +                    case FIRSTMATCH:
 +                        subjectKeys = subjectKeys.subList(0, 1);
 +                        break;
 +
 +                    case LASTMATCH:
 +                        subjectKeys = subjectKeys.subList(subjectKeys.size() - 1, subjectKeys.size());
 +                        break;
 +
 +                    default:
 +                    // keep subjectIds as is
 +                }
 +            }
 +
 +            if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) {
 +                if (subjectKeys.isEmpty()) {
 +                    switch (profile.getTask().getUnmatchingRule()) {
 +                        case ASSIGN:
 +                            profile.getResults().addAll(assign(delta, attrUtils));
 +                            break;
 +                        case PROVISION:
-                             profile.getResults().addAll(create(delta, attrUtils));
++                            profile.getResults().addAll(provision(delta, attrUtils));
 +                            break;
 +                        case IGNORE:
 +                            profile.getResults().addAll(ignore(delta, attrUtils, false));
 +                            break;
 +                        default:
 +                        // do nothing
 +                    }
 +                } else {
 +                    switch (profile.getTask().getMatchingRule()) {
 +                        case UPDATE:
 +                            profile.getResults().addAll(update(delta, subjectKeys, attrUtils));
 +                            break;
 +                        case DEPROVISION:
 +                            profile.getResults().addAll(deprovision(delta, subjectKeys, attrUtils, false));
 +                            break;
 +                        case UNASSIGN:
 +                            profile.getResults().addAll(deprovision(delta, subjectKeys, attrUtils, true));
 +                            break;
 +                        case LINK:
 +                            profile.getResults().addAll(link(delta, subjectKeys, attrUtils, false));
 +                            break;
 +                        case UNLINK:
 +                            profile.getResults().addAll(link(delta, subjectKeys, attrUtils, true));
 +                            break;
 +                        case IGNORE:
 +                            profile.getResults().addAll(ignore(delta, attrUtils, true));
 +                            break;
 +                        default:
 +                        // do nothing
 +                    }
 +                }
 +            } else if (SyncDeltaType.DELETE == delta.getDeltaType()) {
 +                if (subjectKeys.isEmpty()) {
 +                    LOG.debug("No match found for deletion");
 +                } else {
 +                    profile.getResults().addAll(delete(delta, subjectKeys, attrUtils));
 +                }
 +            }
 +        } catch (IllegalStateException | IllegalArgumentException e) {
 +            LOG.warn(e.getMessage());
 +        }
 +    }
 +
 +    private void audit(
 +            final String event,
 +            final Result result,
 +            final Object before,
 +            final Object output,
 +            final Object... input) {
 +
 +        notificationManager.createTasks(AuditElements.EventCategoryType.SYNCHRONIZATION,
 +                getAttributableUtils().getType().name().toLowerCase(),
 +                profile.getTask().getResource().getKey(),
 +                event,
 +                result,
 +                before,
 +                output,
 +                input);
 +
 +        auditManager.audit(AuditElements.EventCategoryType.SYNCHRONIZATION,
 +                getAttributableUtils().getType().name().toLowerCase(),
 +                profile.getTask().getResource().getKey(),
 +                event,
 +                result,
 +                before,
 +                output,
 +                input);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1a133b77/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java
index d07ad34,0000000..28c3885
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractSyncopeResultHandler.java
@@@ -1,128 -1,0 +1,134 @@@
 +/*
 + * 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.sync;
 +
++import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.entity.AttributableUtilsFactory;
 +import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 +import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 +import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.apache.syncope.core.provisioning.api.sync.ProvisioningActions;
 +import org.apache.syncope.core.provisioning.api.sync.ProvisioningProfile;
 +import org.apache.syncope.core.provisioning.api.sync.SyncopeResultHandler;
 +import org.apache.syncope.core.misc.AuditManager;
 +import org.apache.syncope.core.misc.ConnObjectUtils;
++import org.apache.syncope.core.persistence.api.entity.AttributableUtils;
 +import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 +import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 +import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +
 +public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A extends ProvisioningActions>
 +        implements SyncopeResultHandler<T, A> {
 +
 +    /**
 +     * Logger.
 +     */
 +    protected static final Logger LOG = LoggerFactory.getLogger(AbstractSyncopeResultHandler.class);
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @Autowired
 +    protected GroupDAO groupDAO;
 +
 +    /**
 +     * ConnectorObject utils.
 +     */
 +    @Autowired
 +    protected ConnObjectUtils connObjectUtils;
 +
 +    /**
 +     * Notification Manager.
 +     */
 +    @Autowired
 +    protected NotificationManager notificationManager;
 +
 +    /**
 +     * Audit Manager.
 +     */
 +    @Autowired
 +    protected AuditManager auditManager;
 +
 +    /**
 +     * Propagation manager.
 +     */
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    /**
 +     * Task executor.
 +     */
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    /**
 +     * User workflow adapter.
 +     */
 +    @Autowired
 +    protected UserWorkflowAdapter uwfAdapter;
 +
 +    /**
 +     * Group workflow adapter.
 +     */
 +    @Autowired
 +    protected GroupWorkflowAdapter gwfAdapter;
 +
 +    @Autowired
 +    protected UserDataBinder userDataBinder;
 +
 +    @Autowired
 +    protected GroupDataBinder groupDataBinder;
 +
 +    @Autowired
 +    protected UserProvisioningManager userProvisioningManager;
 +
 +    @Autowired
 +    protected GroupProvisioningManager groupProvisioningManager;
 +
 +    @Autowired
 +    protected AttributableUtilsFactory attrUtilsFactory;
 +
 +    /**
 +     * Sync profile.
 +     */
 +    protected ProvisioningProfile<T, A> profile;
 +
++    protected abstract AttributableUtils getAttributableUtils();
++
++    protected abstract AbstractSubjectTO getSubjectTO(long key);
++
 +    @Override
 +    public void setProfile(final ProvisioningProfile<T, A> profile) {
 +        this.profile = profile;
 +    }
 +
 +    @Override
 +    public ProvisioningProfile<T, A> getProfile() {
 +        return profile;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1a133b77/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupSyncResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupSyncResultHandlerImpl.java
index 36e3215,0000000..b9fa5ca
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupSyncResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupSyncResultHandlerImpl.java
@@@ -1,168 -1,0 +1,168 @@@
 +/*
 + * 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.sync;
 +
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
 +import org.apache.syncope.common.lib.mod.AttrMod;
 +import org.apache.syncope.common.lib.mod.GroupMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.types.AttributableType;
 +import org.apache.syncope.core.persistence.api.entity.AttributableUtils;
 +import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult;
 +import org.apache.syncope.core.provisioning.api.sync.GroupSyncResultHandler;
 +import org.identityconnectors.framework.common.objects.SyncDelta;
 +
 +public class GroupSyncResultHandlerImpl extends AbstractSyncResultHandler implements GroupSyncResultHandler {
 +
 +    protected Map<Long, String> groupOwnerMap = new HashMap<>();
 +
 +    @Override
 +    public Map<Long, String> getGroupOwnerMap() {
 +        return this.groupOwnerMap;
 +    }
 +
 +    @Override
 +    protected AttributableUtils getAttributableUtils() {
 +        return attrUtilsFactory.getInstance(AttributableType.GROUP);
 +    }
 +
 +    @Override
 +    protected String getName(final AbstractSubjectTO subjectTO) {
 +        return GroupTO.class.cast(subjectTO).getName();
 +    }
 +
 +    @Override
 +    protected AbstractSubjectTO getSubjectTO(final long key) {
 +        try {
 +            return groupDataBinder.getGroupTO(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving group {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected AbstractSubjectMod getSubjectMod(
 +            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
 +
 +        return connObjectUtils.getAttributableMod(
 +                subjectTO.getKey(),
 +                delta.getObject(),
 +                subjectTO,
 +                profile.getTask(),
 +                attrUtilsFactory.getInstance(AttributableType.GROUP));
 +    }
 +
 +    @Override
-     protected AbstractSubjectTO create(
++    protected AbstractSubjectTO doCreate(
 +            final AbstractSubjectTO subjectTO, final SyncDelta _delta, final ProvisioningResult result) {
 +
 +        GroupTO groupTO = GroupTO.class.cast(subjectTO);
 +
 +        Map.Entry<Long, List<PropagationStatus>> created = groupProvisioningManager.create(groupTO, groupOwnerMap,
 +                Collections.singleton(profile.getTask().getResource().getKey()));
 +
 +        groupTO = groupDataBinder.getGroupTO(created.getKey());
 +
-         result.setId(created.getKey());
++        result.setKey(created.getKey());
 +        result.setName(getName(subjectTO));
 +
 +        return groupTO;
 +    }
 +
 +    @Override
-     protected AbstractSubjectTO link(
++    protected AbstractSubjectTO doLink(
 +            final AbstractSubjectTO before,
 +            final ProvisioningResult result,
 +            final boolean unlink) {
 +
 +        final GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(before.getKey());
 +
 +        if (unlink) {
 +            groupMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        } else {
 +            groupMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        }
 +
 +        return groupDataBinder.getGroupTO(gwfAdapter.update(groupMod).getResult());
 +    }
 +
 +    @Override
-     protected AbstractSubjectTO update(
++    protected AbstractSubjectTO doUpdate(
 +            final AbstractSubjectTO before,
 +            final AbstractSubjectMod subjectMod,
 +            final SyncDelta delta,
 +            final ProvisioningResult result) {
 +
 +        GroupMod groupMod = GroupMod.class.cast(subjectMod);
 +
 +        Map.Entry<Long, List<PropagationStatus>> updated = groupProvisioningManager.update(groupMod);
 +
 +        // moved after group provisioning manager
 +        String groupOwner = null;
 +        for (AttrMod attrMod : groupMod.getPlainAttrsToUpdate()) {
 +            if (attrMod.getSchema().isEmpty()) {
 +                groupOwner = attrMod.getValuesToBeAdded().iterator().next();
 +            }
 +        }
 +        if (groupOwner != null) {
 +            groupOwnerMap.put(updated.getKey(), groupOwner);
 +        }
 +
 +        final GroupTO after = groupDataBinder.getGroupTO(updated.getKey());
 +
 +        result.setName(getName(after));
 +
 +        return after;
 +    }
 +
 +    @Override
-     protected void deprovision(final Long id, final boolean unlink) {
++    protected void doDeprovision(final Long id, final boolean unlink) {
 +        taskExecutor.execute(
 +                propagationManager.getGroupDeleteTasks(id, profile.getTask().getResource().getKey()));
 +
 +        if (unlink) {
 +            final UserMod userMod = new UserMod();
 +            userMod.setKey(id);
 +            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        }
 +    }
 +
 +    @Override
-     protected void delete(final Long id) {
++    protected void doDelete(final Long id) {
 +        try {
 +            taskExecutor.execute(
 +                    propagationManager.getGroupDeleteTasks(id, profile.getTask().getResource().getKey()));
 +        } catch (Exception e) {
 +            // A propagation failure doesn't imply a synchronization failure.
 +            // The propagation exception status will be reported into the propagation task execution.
 +            LOG.error("Could not propagate user " + id, e);
 +        }
 +
 +        groupProvisioningManager.delete(id);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1a133b77/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java
index 4a19ed7,0000000..d32a855
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java
@@@ -1,146 -1,0 +1,146 @@@
 +/*
 + * 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.sync;
 +
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AttributableType;
 +import org.apache.syncope.core.persistence.api.entity.AttributableUtils;
 +import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult;
 +import org.apache.syncope.core.provisioning.api.sync.UserSyncResultHandler;
 +import org.identityconnectors.framework.common.objects.SyncDelta;
 +
 +public class UserSyncResultHandlerImpl extends AbstractSyncResultHandler implements UserSyncResultHandler {
 +
 +    @Override
 +    protected AttributableUtils getAttributableUtils() {
 +        return attrUtilsFactory.getInstance(AttributableType.USER);
 +    }
 +
 +    @Override
 +    protected String getName(final AbstractSubjectTO subjectTO) {
 +        return UserTO.class.cast(subjectTO).getUsername();
 +    }
 +
 +    @Override
 +    protected AbstractSubjectTO getSubjectTO(final long key) {
 +        try {
 +            return userDataBinder.getUserTO(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving user {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected AbstractSubjectMod getSubjectMod(
 +            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
 +
 +        return connObjectUtils.getAttributableMod(subjectTO.getKey(),
 +                delta.getObject(),
 +                subjectTO,
 +                profile.getTask(),
 +                getAttributableUtils());
 +    }
 +
 +    @Override
-     protected AbstractSubjectTO create(
++    protected AbstractSubjectTO doCreate(
 +            final AbstractSubjectTO subjectTO, final SyncDelta delta, final ProvisioningResult result) {
 +
 +        UserTO userTO = UserTO.class.cast(subjectTO);
 +
 +        Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
 +        Map.Entry<Long, List<PropagationStatus>> created = userProvisioningManager.create(userTO, true, true, enabled,
 +                Collections.singleton(profile.getTask().getResource().getKey()));
 +
-         result.setId(created.getKey());
++        result.setKey(created.getKey());
 +
 +        return userDataBinder.getUserTO(created.getKey());
 +    }
 +
 +    @Override
-     protected AbstractSubjectTO link(
++    protected AbstractSubjectTO doLink(
 +            final AbstractSubjectTO before,
 +            final ProvisioningResult result,
 +            final boolean unlink) {
 +
 +        final UserMod userMod = new UserMod();
 +        userMod.setKey(before.getKey());
 +
 +        if (unlink) {
 +            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        } else {
 +            userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        }
 +
 +        return userDataBinder.getUserTO(uwfAdapter.update(userMod).getResult().getKey().getKey());
 +    }
 +
 +    @Override
-     protected AbstractSubjectTO update(
++    protected AbstractSubjectTO doUpdate(
 +            final AbstractSubjectTO before,
 +            final AbstractSubjectMod subjectMod,
 +            final SyncDelta delta,
 +            final ProvisioningResult result) {
 +
 +        final UserMod userMod = UserMod.class.cast(subjectMod);
 +        final Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
 +
 +        Map.Entry<Long, List<PropagationStatus>> updated = userProvisioningManager.update(userMod, before.getKey(),
 +                result, enabled, Collections.singleton(profile.getTask().getResource().getKey()));
 +
 +        return userDataBinder.getUserTO(updated.getKey());
 +    }
 +
 +    @Override
-     protected void deprovision(
++    protected void doDeprovision(
 +            final Long key,
 +            final boolean unlink) {
 +
 +        taskExecutor.execute(
 +                propagationManager.getUserDeleteTasks(
 +                        key, Collections.singleton(profile.getTask().getResource().getKey())));
 +
 +        if (unlink) {
 +            final UserMod userMod = new UserMod();
 +            userMod.setKey(key);
 +            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        }
 +    }
 +
 +    @Override
-     protected void delete(final Long key) {
++    protected void doDelete(final Long key) {
 +        try {
 +            userProvisioningManager.
 +                    delete(key, Collections.<String>singleton(profile.getTask().getResource().getKey()));
 +        } catch (Exception e) {
 +            // A propagation failure doesn't imply a synchronization failure.
 +            // The propagation exception status will be reported into the propagation task execution.
 +            LOG.error("Could not propagate user " + key, e);
 +        }
 +
 +        uwfAdapter.delete(key);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1a133b77/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSyncActions.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSyncActions.java
index 9cdfa5d,0000000..89a8447
mode 100644,000000..100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSyncActions.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSyncActions.java
@@@ -1,88 -1,0 +1,101 @@@
 +/*
 + * 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.fit.core.reference;
 +
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
 +import org.apache.syncope.common.lib.mod.AttrMod;
 +import org.apache.syncope.common.lib.to.AbstractSubjectTO;
 +import org.apache.syncope.common.lib.to.AttrTO;
++import org.apache.syncope.common.lib.to.UserTO;
++import org.apache.syncope.core.provisioning.api.sync.IgnoreProvisionException;
 +import org.apache.syncope.core.provisioning.api.sync.ProvisioningProfile;
 +import org.apache.syncope.core.provisioning.java.sync.DefaultSyncActions;
 +import org.identityconnectors.framework.common.objects.SyncDelta;
 +import org.quartz.JobExecutionException;
 +
 +/**
 + * Test synchronization action.
 + */
 +public class TestSyncActions extends DefaultSyncActions {
 +
 +    private int counter = 0;
 +
 +    @Override
 +    public <T extends AbstractSubjectTO> SyncDelta beforeProvision(
-             final ProvisioningProfile<?, ?> profile,
-             final SyncDelta delta,
-             final T subject) throws JobExecutionException {
++            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final T subject)
++            throws JobExecutionException {
 +
 +        AttrTO attrTO = null;
 +        for (int i = 0; i < subject.getPlainAttrs().size(); i++) {
 +            AttrTO _attrTO = CollectionUtils.get(subject.getPlainAttrs(), i);
 +            if ("fullname".equals(_attrTO.getSchema())) {
 +                attrTO = _attrTO;
 +            }
 +        }
 +        if (attrTO == null) {
 +            attrTO = new AttrTO();
 +            attrTO.setSchema("fullname");
 +            subject.getPlainAttrs().add(attrTO);
 +        }
 +        attrTO.getValues().clear();
 +        attrTO.getValues().add(String.valueOf(counter++));
 +
 +        return delta;
 +    }
 +
 +    @Override
++    public <T extends AbstractSubjectTO> SyncDelta beforeAssign(
++            final ProvisioningProfile<?, ?> profile, final SyncDelta delta, final T subject)
++            throws JobExecutionException {
++
++        if (subject instanceof UserTO && "test2".equals(UserTO.class.cast(subject).getUsername())) {
++            throw new IgnoreProvisionException();
++        }
++
++        return delta;
++    }
++
++    @Override
 +    public <T extends AbstractSubjectTO, K extends AbstractSubjectMod> SyncDelta beforeUpdate(
 +            final ProvisioningProfile<?, ?> profile,
 +            final SyncDelta delta,
 +            final T subject,
 +            final K subjectMod) throws JobExecutionException {
 +
 +        subjectMod.getPlainAttrsToRemove().add("fullname");
 +
 +        AttrMod fullnameMod = null;
 +        for (AttrMod attrMod : subjectMod.getPlainAttrsToUpdate()) {
 +            if ("fullname".equals(attrMod.getSchema())) {
 +                fullnameMod = attrMod;
 +            }
 +        }
 +        if (fullnameMod == null) {
 +            fullnameMod = new AttrMod();
 +            fullnameMod.setSchema("fullname");
 +            subjectMod.getPlainAttrsToUpdate().add(fullnameMod);
 +        }
 +
 +        fullnameMod.getValuesToBeAdded().clear();
 +        fullnameMod.getValuesToBeAdded().add(String.valueOf(counter++));
 +
 +        return delta;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1a133b77/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ResourceITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ResourceITCase.java
index 90664b0,0000000..1a014bd
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ResourceITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ResourceITCase.java
@@@ -1,578 -1,0 +1,581 @@@
 +/*
 + * 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.fit.core.reference;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertNull;
 +import static org.junit.Assert.assertTrue;
 +import static org.junit.Assert.fail;
 +
 +import java.security.AccessControlException;
 +import java.util.Arrays;
 +import java.util.Collection;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Set;
 +import javax.ws.rs.core.Response;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.to.BulkAction;
 +import org.apache.syncope.common.lib.to.MappingItemTO;
 +import org.apache.syncope.common.lib.to.MappingTO;
 +import org.apache.syncope.common.lib.to.ResourceTO;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.ConnConfPropSchema;
 +import org.apache.syncope.common.lib.types.ConnConfProperty;
 +import org.apache.syncope.common.lib.types.EntityViolationType;
 +import org.apache.syncope.common.lib.types.IntMappingType;
 +import org.apache.syncope.common.lib.types.MappingPurpose;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.junit.FixMethodOrder;
 +import org.junit.Test;
 +import org.junit.runners.MethodSorters;
 +
 +@FixMethodOrder(MethodSorters.JVM)
 +public class ResourceITCase extends AbstractITCase {
 +
 +    private ResourceTO buildResourceTO(final String resourceName) {
 +        ResourceTO resourceTO = new ResourceTO();
 +
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnectorId(102L);
 +
 +        MappingTO mapping = new MappingTO();
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setExtAttrName("userId");
 +        item.setIntAttrName("userId");
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.addItem(item);
 +
 +        item = new MappingItemTO();
 +        item.setExtAttrName("username");
 +        item.setIntAttrName("fullname");
 +        item.setIntMappingType(IntMappingType.UserId);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.setAccountIdItem(item);
 +
 +        item = new MappingItemTO();
 +        item.setExtAttrName("fullname");
 +        item.setIntAttrName("cn");
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setAccountid(false);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.addItem(item);
 +
 +        resourceTO.setUmapping(mapping);
 +        return resourceTO;
 +    }
 +
 +    @Test
 +    public void getPropagationActionsClasses() {
 +        List<String> actions = syncopeService.info().getPropagationActions();
 +        assertNotNull(actions);
 +        assertFalse(actions.isEmpty());
 +    }
 +
 +    @Test
 +    public void create() {
 +        String resourceName = RESOURCE_NAME_CREATE;
 +        ResourceTO resourceTO = buildResourceTO(resourceName);
 +
 +        Response response = resourceService.create(resourceTO);
 +        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 +        assertNotNull(actual);
 +
 +        // check for existence
 +        actual = resourceService.read(resourceName);
 +        assertNotNull(actual);
 +    }
 +
 +    @Test
 +    public void createOverridingProps() {
 +        String resourceName = "overriding-conn-conf-target-resource-create";
 +        ResourceTO resourceTO = new ResourceTO();
 +
 +        MappingTO mapping = new MappingTO();
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setExtAttrName("uid");
 +        item.setIntAttrName("userId");
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.addItem(item);
 +
 +        item = new MappingItemTO();
 +        item.setExtAttrName("username");
 +        item.setIntAttrName("fullname");
 +        item.setIntMappingType(IntMappingType.UserId);
 +        item.setAccountid(true);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.setAccountIdItem(item);
 +
 +        item = new MappingItemTO();
 +        item.setExtAttrName("fullname");
 +        item.setIntAttrName("cn");
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setAccountid(false);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.addItem(item);
 +
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnectorId(102L);
 +
 +        resourceTO.setUmapping(mapping);
 +
 +        ConnConfProperty p = new ConnConfProperty();
 +        ConnConfPropSchema schema = new ConnConfPropSchema();
 +        schema.setType("java.lang.String");
 +        schema.setName("endpoint");
 +        schema.setRequired(true);
 +        p.setSchema(schema);
 +        p.getValues().add("http://invalidurl/");
 +
 +        Set<ConnConfProperty> connectorConfigurationProperties = new HashSet<ConnConfProperty>(Arrays.asList(p));
 +        resourceTO.getConnConfProperties().addAll(connectorConfigurationProperties);
 +
 +        Response response = resourceService.create(resourceTO);
 +        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 +        assertNotNull(actual);
 +
 +        // check the existence
 +        actual = resourceService.read(resourceName);
 +        assertNotNull(actual);
 +    }
 +
 +    @Test
 +    public void createWithSingleMappingItem() {
 +        String resourceName = RESOURCE_NAME_CREATE_SINGLE;
 +        ResourceTO resourceTO = new ResourceTO();
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnectorId(102L);
 +
 +        MappingTO umapping = new MappingTO();
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.UserId);
 +        item.setExtAttrName("userId");
 +        item.setAccountid(true);
 +        item.setPurpose(MappingPurpose.PROPAGATION);
 +        umapping.setAccountIdItem(item);
 +
 +        resourceTO.setUmapping(umapping);
 +
 +        MappingTO rmapping = new MappingTO();
 +
 +        item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.GroupId);
 +        item.setExtAttrName("groupId");
 +        item.setAccountid(true);
 +        item.setPurpose(MappingPurpose.SYNCHRONIZATION);
 +        rmapping.setAccountIdItem(item);
 +
 +        resourceTO.setGmapping(rmapping);
 +
 +        Response response = resourceService.create(resourceTO);
 +        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 +
 +        assertNotNull(actual);
 +        assertNotNull(actual.getUmapping());
 +        assertNotNull(actual.getUmapping().getItems());
 +        assertNotNull(actual.getGmapping());
 +        assertNotNull(actual.getGmapping().getItems());
 +        assertEquals(MappingPurpose.SYNCHRONIZATION, actual.getGmapping().getAccountIdItem().getPurpose());
 +        assertEquals(MappingPurpose.PROPAGATION, actual.getUmapping().getAccountIdItem().getPurpose());
 +    }
 +
 +    @Test
 +    public void createWithInvalidMapping() {
 +        String resourceName = RESOURCE_NAME_CREATE_WRONG;
 +        ResourceTO resourceTO = new ResourceTO();
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnectorId(102L);
 +
 +        MappingTO mapping = new MappingTO();
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.UserId);
 +        item.setExtAttrName("userId");
 +        item.setAccountid(true);
 +        mapping.setAccountIdItem(item);
 +
 +        item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setExtAttrName("email");
 +        // missing intAttrName ...
 +        mapping.addItem(item);
 +
 +        resourceTO.setUmapping(mapping);
 +
 +        try {
 +            createResource(resourceTO);
 +            fail("Create should not have worked");
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
 +            assertEquals("intAttrName", e.getElements().iterator().next());
 +        }
 +    }
 +
 +    @Test(expected = SyncopeClientException.class)
 +    public void createWithoutExtAttr() {
 +        String resourceName = RESOURCE_NAME_CREATE_WRONG;
 +        ResourceTO resourceTO = new ResourceTO();
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnectorId(102L);
 +
 +        MappingTO mapping = new MappingTO();
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.UserId);
 +        item.setExtAttrName("userId");
 +        item.setAccountid(true);
 +        mapping.setAccountIdItem(item);
 +
 +        item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setIntAttrName("usernane");
 +        // missing extAttrName ...
 +        mapping.addItem(item);
 +
 +        resourceTO.setUmapping(mapping);
 +
 +        createResource(resourceTO);
 +    }
 +
 +    @Test
 +    public void createWithPasswordPolicy() {
 +        String resourceName = "res-with-password-policy";
 +        ResourceTO resourceTO = new ResourceTO();
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnectorId(102L);
 +        resourceTO.setPasswordPolicy(4L);
 +
 +        MappingTO mapping = new MappingTO();
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setExtAttrName("userId");
 +        item.setIntAttrName("userId");
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setAccountid(true);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.setAccountIdItem(item);
 +
 +        resourceTO.setUmapping(mapping);
 +
 +        Response response = resourceService.create(resourceTO);
 +        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 +        assertNotNull(actual);
 +
 +        // check the existence
 +        actual = resourceService.read(resourceName);
 +        assertNotNull(actual);
 +        assertNotNull(actual.getPasswordPolicy());
 +        assertEquals(4L, (long) actual.getPasswordPolicy());
 +    }
 +
 +    @Test
 +    public void updateWithException() {
 +        try {
 +            ResourceTO resourceTO = new ResourceTO();
 +            resourceTO.setKey("resourcenotfound");
- 
 +            resourceService.update(resourceTO.getKey(), resourceTO);
++
++            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
 +        }
 +    }
 +
 +    @Test
 +    public void update() {
 +        String resourceName = RESOURCE_NAME_UPDATE;
 +        ResourceTO resourceTO = new ResourceTO();
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnectorId(101L);
 +
 +        MappingTO mapping = new MappingTO();
 +
 +        // Update with an existing and already assigned mapping
 +        MappingItemTO item = new MappingItemTO();
 +        item.setKey(112L);
 +        item.setExtAttrName("test3");
 +        item.setIntAttrName("fullname");
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.addItem(item);
 +
 +        // Update defining new mappings
 +        for (int i = 4; i < 6; i++) {
 +            item = new MappingItemTO();
 +            item.setExtAttrName("test" + i);
 +            item.setIntAttrName("fullname");
 +            item.setIntMappingType(IntMappingType.UserPlainSchema);
 +            item.setPurpose(MappingPurpose.BOTH);
 +            mapping.addItem(item);
 +        }
 +        item = new MappingItemTO();
 +        item.setExtAttrName("username");
 +        item.setIntAttrName("fullname");
 +        item.setIntMappingType(IntMappingType.UserId);
 +        item.setAccountid(true);
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.setAccountIdItem(item);
 +
 +        resourceTO.setUmapping(mapping);
 +
 +        resourceService.update(resourceTO.getKey(), resourceTO);
 +        ResourceTO actual = resourceService.read(resourceTO.getKey());
 +        assertNotNull(actual);
 +
 +        // check for existence
 +        Collection<MappingItemTO> mapItems = actual.getUmapping().getItems();
 +        assertNotNull(mapItems);
 +        assertEquals(4, mapItems.size());
 +    }
 +
 +    @Test
 +    public void deleteWithException() {
 +        try {
 +            resourceService.delete("resourcenotfound");
++            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
 +        }
 +    }
 +
 +    @Test
 +    public void updateResetSyncToken() {
 +        // create resource with sync token
 +        String resourceName = RESOURCE_NAME_RESETSYNCTOKEN + getUUIDString();
 +        ResourceTO pre = buildResourceTO(resourceName);
 +        pre.setUsyncToken("test");
 +        resourceService.create(pre);
 +
 +        pre.setUsyncToken(null);
 +        resourceService.update(pre.getKey(), pre);
 +        ResourceTO actual = resourceService.read(pre.getKey());
 +        // check that the synctoken has been reset
 +        assertNull(actual.getUsyncToken());
 +    }
 +
 +    @Test
 +    public void delete() {
 +        String resourceName = "tobedeleted";
 +
 +        ResourceTO resource = buildResourceTO(resourceName);
 +        Response response = resourceService.create(resource);
 +        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 +        assertNotNull(actual);
 +
 +        resourceService.delete(resourceName);
 +
 +        try {
 +            resourceService.read(resourceName);
++            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
 +        }
 +    }
 +
 +    @Test
 +    public void list() {
 +        List<ResourceTO> actuals = resourceService.list();
 +        assertNotNull(actuals);
 +        assertFalse(actuals.isEmpty());
 +        for (ResourceTO resourceTO : actuals) {
 +            assertNotNull(resourceTO);
 +        }
 +    }
 +
 +    @Test
 +    public void read() {
 +        ResourceTO actual = resourceService.read(RESOURCE_NAME_TESTDB);
 +        assertNotNull(actual);
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE323() {
 +        ResourceTO actual = resourceService.read(RESOURCE_NAME_TESTDB);
 +        assertNotNull(actual);
 +
 +        try {
 +            createResource(actual);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(Response.Status.CONFLICT, e.getType().getResponseStatus());
 +            assertEquals(ClientExceptionType.EntityExists, e.getType());
 +        }
 +
 +        actual.setKey(null);
 +        try {
 +            createResource(actual);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(Response.Status.BAD_REQUEST, e.getType().getResponseStatus());
 +            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
 +        }
 +    }
 +
 +    @Test
 +    public void bulkAction() {
 +        resourceService.create(buildResourceTO("forBulk1"));
 +        resourceService.create(buildResourceTO("forBulk2"));
 +
 +        assertNotNull(resourceService.read("forBulk1"));
 +        assertNotNull(resourceService.read("forBulk2"));
 +
 +        final BulkAction bulkAction = new BulkAction();
 +        bulkAction.setOperation(BulkAction.Type.DELETE);
 +
 +        bulkAction.getTargets().add(String.valueOf("forBulk1"));
 +        bulkAction.getTargets().add(String.valueOf("forBulk2"));
 +
 +        resourceService.bulk(bulkAction);
 +
 +        try {
 +            resourceService.read("forBulk1");
 +            fail();
 +        } catch (SyncopeClientException e) {
 +        }
 +
 +        try {
 +            resourceService.read("forBulk2");
 +            fail();
 +        } catch (SyncopeClientException e) {
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE360() {
 +        final String name = "SYNCOPE360-" + getUUIDString();
 +        resourceService.create(buildResourceTO(name));
 +
 +        ResourceTO resource = resourceService.read(name);
 +        assertNotNull(resource);
 +        assertNotNull(resource.getUmapping());
 +
 +        resource.setUmapping(new MappingTO());
 +        resourceService.update(name, resource);
 +
 +        resource = resourceService.read(name);
 +        assertNotNull(resource);
 +        assertNull(resource.getUmapping());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE368() {
 +        final String name = "SYNCOPE368-" + getUUIDString();
 +
 +        ResourceTO resourceTO = new ResourceTO();
 +
 +        resourceTO.setKey(name);
 +        resourceTO.setConnectorId(105L);
 +
 +        MappingTO mapping = new MappingTO();
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.GroupName);
 +        item.setExtAttrName("cn");
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.setAccountIdItem(item);
 +
 +        item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.GroupOwnerSchema);
 +        item.setExtAttrName("owner");
 +        item.setPurpose(MappingPurpose.BOTH);
 +        mapping.addItem(item);
 +
 +        resourceTO.setGmapping(mapping);
 +
 +        resourceTO = createResource(resourceTO);
 +        assertNotNull(resourceTO);
 +        assertEquals(2, resourceTO.getGmapping().getItems().size());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE418() {
 +        try {
 +            resourceService.create(
 +                    buildResourceTO("http://schemas.examples.org/security/authorization/organizationUnit"));
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.InvalidExternalResource, e.getType());
 +
 +            assertTrue(e.getElements().iterator().next().toString().contains(EntityViolationType.InvalidName.name()));
 +        }
 +    }
 +
 +    @Test
 +    public void anonymous() {
 +        ResourceService unauthenticated = clientFactory.createAnonymous().getService(ResourceService.class);
 +        try {
 +            unauthenticated.list();
 +            fail();
 +        } catch (AccessControlException e) {
 +            assertNotNull(e);
 +        }
 +
 +        ResourceService anonymous = clientFactory.create(ANONYMOUS_UNAME, ANONYMOUS_KEY).
 +                getService(ResourceService.class);
 +        assertFalse(anonymous.list().isEmpty());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE493() {
 +        // create resource with attribute mapping set to NONE and check its propagation
 +        String resourceName = RESOURCE_NAME_CREATE_NONE;
 +        ResourceTO resourceTO = new ResourceTO();
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnectorId(102L);
 +
 +        MappingTO umapping = new MappingTO();
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setIntMappingType(IntMappingType.UserId);
 +        item.setExtAttrName("userId");
 +        item.setAccountid(true);
 +        item.setPurpose(MappingPurpose.PROPAGATION);
 +        umapping.setAccountIdItem(item);
 +
 +        MappingItemTO item2 = new MappingItemTO();
 +        item2.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item2.setAccountid(false);
 +        item2.setIntAttrName("gender");
 +        item2.setExtAttrName("gender");
 +        item2.setPurpose(MappingPurpose.NONE);
 +        umapping.addItem(item2);
 +
 +        resourceTO.setUmapping(umapping);
 +
 +        Response response = resourceService.create(resourceTO);
 +        ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 +
 +        assertNotNull(actual);
 +        assertNotNull(actual.getUmapping());
 +        assertNotNull(actual.getUmapping().getItems());
 +        assertEquals(MappingPurpose.PROPAGATION, actual.getUmapping().getAccountIdItem().getPurpose());
 +        for (MappingItemTO itemTO : actual.getUmapping().getItems()) {
 +            if ("gender".equals(itemTO.getIntAttrName())) {
 +                assertEquals(MappingPurpose.NONE, itemTO.getPurpose());
 +            }
 +        }
 +    }
 +}


Mime
View raw message