You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by md...@apache.org on 2015/03/18 09:49:12 UTC
[1/5] syncope git commit: [SYNCOPE-648] Add matching/unmatching
events to notification and audit
Repository: syncope
Updated Branches:
refs/heads/master 28aef6c78 -> b606a4b87
[SYNCOPE-648] Add matching/unmatching events to notification and audit
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/89d5ad43
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/89d5ad43
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/89d5ad43
Branch: refs/heads/master
Commit: 89d5ad43cf26454092dbd04446a0d9a6f5192f4a
Parents: b630219
Author: Marco Di Sabatino Di Diodoro <md...@apache.org>
Authored: Tue Mar 17 16:09:35 2015 +0100
Committer: Marco Di Sabatino Di Diodoro <md...@apache.org>
Committed: Tue Mar 17 16:09:35 2015 +0100
----------------------------------------------------------------------
.../syncope/common/types/MatchingRule.java | 7 ++-
.../syncope/common/types/UnmatchingRule.java | 7 ++-
.../core/rest/controller/LoggerController.java | 18 +++++++-
.../impl/AbstractSubjectPushResultHandler.java | 12 +++--
.../impl/AbstractSubjectSyncResultHandler.java | 47 +++++++++++++++++---
.../syncope/core/sync/impl/AbstractSyncJob.java | 33 +++++++++++---
.../syncope/core/rest/TaskTestITCase.java | 44 ++++++++++++++++++
7 files changed, 150 insertions(+), 18 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/89d5ad43/common/src/main/java/org/apache/syncope/common/types/MatchingRule.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/syncope/common/types/MatchingRule.java b/common/src/main/java/org/apache/syncope/common/types/MatchingRule.java
index 51933fc..ff36dec 100644
--- a/common/src/main/java/org/apache/syncope/common/types/MatchingRule.java
+++ b/common/src/main/java/org/apache/syncope/common/types/MatchingRule.java
@@ -49,6 +49,11 @@ public enum MatchingRule {
/**
* Just link resource without performing any (de-)provisioning operation.
*/
- LINK
+ LINK;
+
+ public static String toEventName(final MatchingRule rule) {
+ return new StringBuilder(MatchingRule.class.getSimpleName()).
+ append("_").append(rule.name()).toString().toLowerCase();
+ }
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/89d5ad43/common/src/main/java/org/apache/syncope/common/types/UnmatchingRule.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/syncope/common/types/UnmatchingRule.java b/common/src/main/java/org/apache/syncope/common/types/UnmatchingRule.java
index d57aacf..224cf63 100644
--- a/common/src/main/java/org/apache/syncope/common/types/UnmatchingRule.java
+++ b/common/src/main/java/org/apache/syncope/common/types/UnmatchingRule.java
@@ -42,6 +42,11 @@ public enum UnmatchingRule {
* Just unlink resource without performing any (de-)provisioning operation.
* In case of sync task UNLINK and IGNORE will coincide.
*/
- UNLINK
+ UNLINK;
+
+ public static String toEventName(final UnmatchingRule rule) {
+ return new StringBuilder(UnmatchingRule.class.getSimpleName()).
+ append("_").append(rule.name()).toString().toLowerCase();
+ }
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/89d5ad43/core/src/main/java/org/apache/syncope/core/rest/controller/LoggerController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/LoggerController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/LoggerController.java
index a4e53b8..f38d0b2 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/LoggerController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/LoggerController.java
@@ -41,6 +41,8 @@ import org.apache.syncope.common.types.LoggerType;
import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.common.util.BeanUtils;
import org.apache.syncope.common.SyncopeClientException;
+import org.apache.syncope.common.types.MatchingRule;
+import org.apache.syncope.common.types.UnmatchingRule;
import org.apache.syncope.core.persistence.beans.ExternalResource;
import org.apache.syncope.core.persistence.beans.SchedTask;
import org.apache.syncope.core.persistence.beans.SyncTask;
@@ -217,7 +219,7 @@ public class LoggerController extends AbstractTransactionalController<LoggerTO>
final String packageSearchPath =
ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(
- SystemPropertyUtils.resolvePlaceholders(this.getClass().getPackage().getName()))
+ SystemPropertyUtils.resolvePlaceholders(this.getClass().getPackage().getName()))
+ "/" + "**/*.class";
final Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
@@ -239,7 +241,7 @@ public class LoggerController extends AbstractTransactionalController<LoggerTO>
}
}
}
-
+
//SYNCOPE-608
final EventCategoryTO authenticationControllerEvents = new EventCategoryTO();
authenticationControllerEvents.setCategory("AuthenticationController");
@@ -270,6 +272,18 @@ public class LoggerController extends AbstractTransactionalController<LoggerTO>
pushEventCategoryTO.getEvents().add(resourceOperation.name().toLowerCase());
}
+ for (UnmatchingRule unmatching : UnmatchingRule.values()) {
+ String event = UnmatchingRule.toEventName(unmatching);
+ syncEventCategoryTO.getEvents().add(event);
+ pushEventCategoryTO.getEvents().add(event);
+ }
+
+ for (MatchingRule matching : MatchingRule.values()) {
+ String event = MatchingRule.toEventName(matching);
+ syncEventCategoryTO.getEvents().add(event);
+ pushEventCategoryTO.getEvents().add(event);
+ }
+
events.add(propEventCategoryTO);
events.add(syncEventCategoryTO);
events.add(pushEventCategoryTO);
http://git-wip-us.apache.org/repos/asf/syncope/blob/89d5ad43/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java b/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
index f8cb658..e8954e9 100644
--- a/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
+++ b/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
@@ -132,7 +132,7 @@ public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeRe
} else {
try {
if (beforeObj == null) {
- operation = profile.getSyncTask().getUnmatchingRule().name().toLowerCase();
+ operation = UnmatchingRule.toEventName(profile.getSyncTask().getUnmatchingRule());
result.setOperation(getResourceOperation(profile.getSyncTask().getUnmatchingRule()));
switch (profile.getSyncTask().getUnmatchingRule()) {
@@ -172,12 +172,15 @@ public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeRe
}
break;
+ case IGNORE:
+ LOG.debug("Ignored subjectId: {}", subjectId);
+ break;
default:
// do nothing
}
} else {
- operation = profile.getSyncTask().getMatchingRule().name().toLowerCase();
+ operation = MatchingRule.toEventName(profile.getSyncTask().getMatchingRule());
result.setOperation(getResourceOperation(profile.getSyncTask().getMatchingRule()));
switch (profile.getSyncTask().getMatchingRule()) {
@@ -240,6 +243,9 @@ public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeRe
}
break;
+ case IGNORE:
+ LOG.debug("Ignored subjectId: {}", subjectId);
+ break;
default:
// do nothing
}
@@ -263,7 +269,7 @@ public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeRe
} finally {
notificationManager.createTasks(
AuditElements.EventCategoryType.PUSH,
- AttributableType.USER.name().toLowerCase(),
+ attrUtil.getType().name().toLowerCase(),
profile.getSyncTask().getResource().getName(),
operation,
resultStatus,
http://git-wip-us.apache.org/repos/asf/syncope/blob/89d5ad43/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java b/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java
index ff9bc3d..9a51ebe 100644
--- a/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java
+++ b/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectSyncResultHandler.java
@@ -31,7 +31,9 @@ import org.apache.syncope.common.mod.AbstractSubjectMod;
import org.apache.syncope.common.to.AbstractSubjectTO;
import org.apache.syncope.common.types.AuditElements;
import org.apache.syncope.common.types.AuditElements.Result;
+import org.apache.syncope.common.types.MatchingRule;
import org.apache.syncope.common.types.ResourceOperation;
+import org.apache.syncope.common.types.UnmatchingRule;
import org.apache.syncope.core.persistence.beans.SyncTask;
import org.apache.syncope.core.persistence.dao.NotFoundException;
import org.apache.syncope.core.persistence.dao.UserDAO;
@@ -126,7 +128,7 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
_delta = action.beforeAssign(this.getProfile(), _delta, transformed);
}
- create(transformed, _delta, attrUtil, "assign", result);
+ create(transformed, _delta, attrUtil, UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), result);
}
return Collections.singletonList(result);
@@ -161,7 +163,7 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
_delta = action.beforeProvision(this.getProfile(), _delta, transformed);
}
- create(transformed, _delta, attrUtil, "provision", result);
+ create(transformed, _delta, attrUtil, UnmatchingRule.toEventName(UnmatchingRule.PROVISION), result);
}
return Collections.<SyncResult>singletonList(result);
@@ -278,7 +280,7 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
resultStatus = Result.FAILURE;
}
}
- audit("update", resultStatus, before, output, delta);
+ audit(MatchingRule.toEventName(MatchingRule.UPDATE), resultStatus, before, output, delta);
}
updResults.add(result);
}
@@ -361,7 +363,9 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
resultStatus = Result.FAILURE;
}
}
- audit(unlink ? "unassign" : "deprovision", resultStatus, before, output, delta);
+ audit(unlink
+ ? MatchingRule.toEventName(MatchingRule.UNASSIGN)
+ : MatchingRule.toEventName(MatchingRule.DEPROVISION), resultStatus, before, output, delta);
}
updResults.add(result);
}
@@ -444,7 +448,8 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
resultStatus = Result.FAILURE;
}
}
- audit(unlink ? "unlink" : "link", resultStatus, before, output, delta);
+ audit(unlink ? MatchingRule.toEventName(MatchingRule.UNLINK)
+ : MatchingRule.toEventName(MatchingRule.LINK), resultStatus, before, output, delta);
}
updResults.add(result);
}
@@ -500,7 +505,7 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
action.after(this.getProfile(), delta, before, result);
}
- audit("delete", resultStatus, before, output, delta);
+ audit(ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, output, delta);
}
delResults.add(result);
@@ -517,6 +522,30 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
return delResults;
}
+ private List<SyncResult> ignore(SyncDelta delta, final AttributableUtil attrUtil, final boolean matching)
+ throws JobExecutionException {
+
+ LOG.debug("Subject to ignore {}", delta.getObject().getUid().getUidValue());
+
+ final List<SyncResult> ignoreResults = new ArrayList<SyncResult>();
+ final SyncResult result = new SyncResult();
+
+ result.setId(null);
+ result.setName(delta.getObject().getUid().getUidValue());
+ result.setOperation(ResourceOperation.NONE);
+ result.setSubjectType(attrUtil.getType());
+ result.setStatus(SyncResult.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)/role(s).
*
@@ -566,6 +595,9 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
case PROVISION:
profile.getResults().addAll(create(delta, attrUtil));
break;
+ case IGNORE:
+ profile.getResults().addAll(ignore(delta, attrUtil, false));
+ break;
default:
// do nothing
}
@@ -586,6 +618,9 @@ public abstract class AbstractSubjectSyncResultHandler extends AbstractSyncopeRe
case UNLINK:
profile.getResults().addAll(link(delta, subjectIds, attrUtil, true));
break;
+ case IGNORE:
+ profile.getResults().addAll(ignore(delta, attrUtil, true));
+ break;
default:
// do nothing
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/89d5ad43/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncJob.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncJob.java b/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncJob.java
index cabeb4f..0124082 100644
--- a/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncJob.java
+++ b/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSyncJob.java
@@ -116,12 +116,14 @@ public abstract class AbstractSyncJob<T extends AbstractSyncTask, A extends Abst
List<SyncResult> uFailUpdate = new ArrayList<SyncResult>();
List<SyncResult> uSuccDelete = new ArrayList<SyncResult>();
List<SyncResult> uFailDelete = new ArrayList<SyncResult>();
+ List<SyncResult> uSuccNone = new ArrayList<SyncResult>();
List<SyncResult> rSuccCreate = new ArrayList<SyncResult>();
List<SyncResult> rFailCreate = new ArrayList<SyncResult>();
List<SyncResult> rSuccUpdate = new ArrayList<SyncResult>();
List<SyncResult> rFailUpdate = new ArrayList<SyncResult>();
List<SyncResult> rSuccDelete = new ArrayList<SyncResult>();
List<SyncResult> rFailDelete = new ArrayList<SyncResult>();
+ List<SyncResult> rSuccNone = new ArrayList<SyncResult>();
for (SyncResult syncResult : syncResults) {
switch (syncResult.getStatus()) {
@@ -169,6 +171,20 @@ public abstract class AbstractSyncJob<T extends AbstractSyncTask, A extends Abst
}
break;
+ case NONE:
+ switch (syncResult.getSubjectType()) {
+ case USER:
+ uSuccNone.add(syncResult);
+ break;
+
+ case ROLE:
+ rSuccNone.add(syncResult);
+ break;
+
+ default:
+ }
+ break;
+
default:
}
break;
@@ -232,13 +248,16 @@ public abstract class AbstractSyncJob<T extends AbstractSyncTask, A extends Abst
append("[updated/failures]: ").append(uSuccUpdate.size()).append('/').append(uFailUpdate.size()).
append(' ').
append("[deleted/failures]: ").append(uSuccDelete.size()).append('/').append(uFailDelete.size()).
- append('\n');
+ append(' ').
+ append("[ignored]: ").append(uSuccNone.size()).append('\n');
report.append("Roles ").
append("[created/failures]: ").append(rSuccCreate.size()).append('/').append(rFailCreate.size()).
append(' ').
append("[updated/failures]: ").append(rSuccUpdate.size()).append('/').append(rFailUpdate.size()).
append(' ').
- append("[deleted/failures]: ").append(rSuccDelete.size()).append('/').append(rFailDelete.size());
+ append("[deleted/failures]: ").append(rSuccDelete.size()).append('/').append(rFailDelete.size()).
+ append(' ').
+ append("[ignored]: ").append(rSuccNone.size());
// Failures
if (syncTraceLevel == TraceLevel.FAILURES || syncTraceLevel == TraceLevel.ALL) {
@@ -276,13 +295,17 @@ public abstract class AbstractSyncJob<T extends AbstractSyncTask, A extends Abst
.append("\nUsers updated:\n")
.append(SyncResult.produceReport(uSuccUpdate, syncTraceLevel))
.append("\nUsers deleted:\n")
- .append(SyncResult.produceReport(uSuccDelete, syncTraceLevel));
+ .append(SyncResult.produceReport(uSuccDelete, syncTraceLevel))
+ .append("\nUsers ignored:\n")
+ .append(SyncResult.produceReport(uSuccNone, syncTraceLevel));
report.append("\n\nRoles created:\n")
.append(SyncResult.produceReport(rSuccCreate, syncTraceLevel))
.append("\nRoles updated:\n")
.append(SyncResult.produceReport(rSuccUpdate, syncTraceLevel))
.append("\nRoles deleted:\n")
- .append(SyncResult.produceReport(rSuccDelete, syncTraceLevel));
+ .append(SyncResult.produceReport(rSuccDelete, syncTraceLevel))
+ .append("\nRoles ignored:\n")
+ .append(SyncResult.produceReport(rSuccNone, syncTraceLevel));
}
return report.toString();
@@ -316,7 +339,7 @@ public abstract class AbstractSyncJob<T extends AbstractSyncTask, A extends Abst
} catch (Exception e) {
final String msg = String.
format("Connector instance bean for resource %s and connInstance %s not found",
- syncTask.getResource(), syncTask.getResource().getConnector());
+ syncTask.getResource(), syncTask.getResource().getConnector());
throw new JobExecutionException(msg, e);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/89d5ad43/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java b/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
index 468b8f9..0cc137a 100644
--- a/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
+++ b/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
@@ -1385,4 +1385,48 @@ public class TaskTestITCase extends AbstractTest {
}
}
}
+
+ @Test
+ public void issueSYNCOPE648() {
+ //1. Create Push Task
+ final PushTaskTO task = new PushTaskTO();
+ task.setName("Test create Push");
+ task.setResource(RESOURCE_NAME_LDAP);
+ task.setUserFilter(
+ SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("_NO_ONE_").query());
+ task.setRoleFilter(
+ SyncopeClient.getRoleSearchConditionBuilder().is("name").equalTo("citizen").query());
+ task.setMatchingRule(MatchingRule.IGNORE);
+ task.setUnmatchingRule(UnmatchingRule.IGNORE);
+
+ final Response response = taskService.create(task);
+ final PushTaskTO actual = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
+ assertNotNull(actual);
+
+ // 2. Create notification
+ NotificationTO notification = new NotificationTO();
+ notification.setTraceLevel(TraceLevel.FAILURES);
+ notification.getEvents().add("[PushTask]:[role]:[resource-ldap]:[matchingrule_ignore]:[SUCCESS]");
+ notification.getEvents().add("[PushTask]:[role]:[resource-ldap]:[unmatchingrule_ignore]:[SUCCESS]");
+
+ notification.getStaticRecipients().add("issueyncope648@syncope.apache.org");
+ notification.setSelfAsRecipient(false);
+ notification.setRecipientAttrName("email");
+ notification.setRecipientAttrType(IntMappingType.UserSchema);
+
+ notification.setSender("syncope648@syncope.apache.org");
+ String subject = "Test notification";
+ notification.setSubject(subject);
+ notification.setTemplate("optin");
+ notification.setActive(true);
+
+ Response responseNotification = notificationService.create(notification);
+ notification = getObject(responseNotification.getLocation(), NotificationService.class, NotificationTO.class);
+ assertNotNull(notification);
+
+ execSyncTask(actual.getId(), 50, false);
+
+ NotificationTaskTO taskTO = findNotificationTaskBySender("syncope648@syncope.apache.org");
+ assertNotNull(taskTO);
+ }
}
[5/5] syncope git commit: [SYNCOPE-648]Merge from 1_2_X
Posted by md...@apache.org.
[SYNCOPE-648]Merge from 1_2_X
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/b606a4b8
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/b606a4b8
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/b606a4b8
Branch: refs/heads/master
Commit: b606a4b87fa00140d24dfcecac3681734955acfb
Parents: 53721b8 12570a0
Author: Marco Di Sabatino Di Diodoro <md...@apache.org>
Authored: Wed Mar 18 09:48:51 2015 +0100
Committer: Marco Di Sabatino Di Diodoro <md...@apache.org>
Committed: Wed Mar 18 09:48:51 2015 +0100
----------------------------------------------------------------------
----------------------------------------------------------------------
[4/5] syncope git commit: [SYNCOPE-648]Add getAttributableUtil in
User/Role PushResultHandler
Posted by md...@apache.org.
[SYNCOPE-648]Add getAttributableUtil in User/Role PushResultHandler
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/12570a04
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/12570a04
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/12570a04
Branch: refs/heads/master
Commit: 12570a042846044c41fea54c8c6d27daf280de0d
Parents: 89d5ad4
Author: Marco Di Sabatino Di Diodoro <md...@apache.org>
Authored: Wed Mar 18 09:36:03 2015 +0100
Committer: Marco Di Sabatino Di Diodoro <md...@apache.org>
Committed: Wed Mar 18 09:36:03 2015 +0100
----------------------------------------------------------------------
.../core/sync/impl/AbstractSubjectPushResultHandler.java | 9 +++++----
.../syncope/core/sync/impl/RolePushResultHandler.java | 7 +++++++
.../syncope/core/sync/impl/UserPushResultHandler.java | 7 +++++++
3 files changed, 19 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/12570a04/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java b/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
index e8954e9..9ea22fd 100644
--- a/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
+++ b/core/src/main/java/org/apache/syncope/core/sync/impl/AbstractSubjectPushResultHandler.java
@@ -52,6 +52,8 @@ import org.springframework.transaction.annotation.Transactional;
public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions> {
+ protected abstract AttributableUtil getAttributableUtil();
+
protected abstract String getName(final AbstractSubject subject);
protected abstract AbstractMapping getMapping();
@@ -114,7 +116,6 @@ public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeRe
String operation = null;
// Try to read remote object (user / group) BEFORE any actual operation
-
final String accountId = MappingUtil.getAccountIdValue(
subject, profile.getSyncTask().getResource(), getMapping().getAccountIdItem());
@@ -180,7 +181,7 @@ public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeRe
}
} else {
- operation = MatchingRule.toEventName(profile.getSyncTask().getMatchingRule());
+ operation = MatchingRule.toEventName(profile.getSyncTask().getMatchingRule());
result.setOperation(getResourceOperation(profile.getSyncTask().getMatchingRule()));
switch (profile.getSyncTask().getMatchingRule()) {
@@ -269,7 +270,7 @@ public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeRe
} finally {
notificationManager.createTasks(
AuditElements.EventCategoryType.PUSH,
- attrUtil.getType().name().toLowerCase(),
+ getAttributableUtil().getType().name().toLowerCase(),
profile.getSyncTask().getResource().getName(),
operation,
resultStatus,
@@ -278,7 +279,7 @@ public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeRe
subject);
auditManager.audit(
AuditElements.EventCategoryType.PUSH,
- AttributableType.USER.name().toLowerCase(),
+ getAttributableUtil().getType().name().toLowerCase(),
profile.getSyncTask().getResource().getName(),
operation,
resultStatus,
http://git-wip-us.apache.org/repos/asf/syncope/blob/12570a04/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java b/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
index c29b49e..18f7008 100644
--- a/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
+++ b/core/src/main/java/org/apache/syncope/core/sync/impl/RolePushResultHandler.java
@@ -24,6 +24,7 @@ import java.util.List;
import org.apache.syncope.common.mod.RoleMod;
import org.apache.syncope.common.to.AbstractSubjectTO;
import org.apache.syncope.common.to.RoleTO;
+import org.apache.syncope.common.types.AttributableType;
import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.core.persistence.beans.AbstractMapping;
import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
@@ -31,6 +32,7 @@ import org.apache.syncope.core.persistence.beans.AbstractSubject;
import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
import org.apache.syncope.core.propagation.PropagationByResource;
import org.apache.syncope.core.propagation.TimeoutException;
+import org.apache.syncope.core.util.AttributableUtil;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.Uid;
@@ -38,6 +40,11 @@ import org.identityconnectors.framework.common.objects.Uid;
public class RolePushResultHandler extends AbstractSubjectPushResultHandler {
@Override
+ protected AttributableUtil getAttributableUtil() {
+ return AttributableUtil.getInstance(AttributableType.ROLE);
+ }
+
+ @Override
protected AbstractSubject deprovision(final AbstractSubject sbj) {
final RoleTO before = roleDataBinder.getRoleTO(SyncopeRole.class.cast(sbj));
http://git-wip-us.apache.org/repos/asf/syncope/blob/12570a04/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java b/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java
index 4637692..a608c6a 100644
--- a/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java
+++ b/core/src/main/java/org/apache/syncope/core/sync/impl/UserPushResultHandler.java
@@ -24,6 +24,7 @@ import java.util.List;
import org.apache.syncope.common.mod.UserMod;
import org.apache.syncope.common.to.AbstractSubjectTO;
import org.apache.syncope.common.to.UserTO;
+import org.apache.syncope.common.types.AttributableType;
import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.core.persistence.beans.AbstractMapping;
import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
@@ -31,6 +32,7 @@ import org.apache.syncope.core.persistence.beans.AbstractSubject;
import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
import org.apache.syncope.core.propagation.PropagationByResource;
import org.apache.syncope.core.propagation.TimeoutException;
+import org.apache.syncope.core.util.AttributableUtil;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.Uid;
@@ -38,6 +40,11 @@ import org.identityconnectors.framework.common.objects.Uid;
public class UserPushResultHandler extends AbstractSubjectPushResultHandler {
@Override
+ protected AttributableUtil getAttributableUtil() {
+ return AttributableUtil.getInstance(AttributableType.USER);
+ }
+
+ @Override
protected AbstractSubject deprovision(final AbstractSubject sbj) {
final UserTO before = userDataBinder.getUserTO(sbj.getId());
[2/5] syncope git commit: [SYNCOPE-648]Merge from 1_2_X
Posted by md...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/RolePushResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/RolePushResultHandlerImpl.java
index d51b26f,0000000..c160556
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/RolePushResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/RolePushResultHandlerImpl.java
@@@ -1,155 -1,0 +1,162 @@@
+/*
+ * 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.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.RoleTO;
++import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
++import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.Mapping;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.sync.RolePushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+
+public class RolePushResultHandlerImpl extends AbstractPushResultHandler implements RolePushResultHandler {
+
+ @Override
++ protected AttributableUtil getAttributableUtil() {
++ return attrUtilFactory.getInstance(AttributableType.ROLE);
++ }
++
++ @Override
+ protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
+ final RoleTO before = roleTransfer.getRoleTO(Role.class.cast(sbj));
+
+ final List<String> noPropResources = new ArrayList<>(before.getResources());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getRoleDeleteTaskIds(before.getKey(), noPropResources));
+
+ return roleDAO.authFetch(before.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+ final RoleTO before = roleTransfer.getRoleTO(Role.class.cast(sbj));
+
+ final List<String> noPropResources = new ArrayList<>(before.getResources());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ final PropagationByResource propByRes = new PropagationByResource();
+ propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getRoleCreateTaskIds(
+ before.getKey(),
+ Collections.unmodifiableCollection(before.getVirAttrs()),
+ propByRes,
+ noPropResources));
+
+ return roleDAO.authFetch(before.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
+ final RoleMod roleMod = new RoleMod();
+ roleMod.setKey(sbj.getKey());
+
+ if (unlink) {
+ roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+ } else {
+ roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+ }
+
+ rwfAdapter.update(roleMod);
+
+ return roleDAO.authFetch(sbj.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
+ final RoleMod roleMod = new RoleMod();
+ roleMod.setKey(sbj.getKey());
+ roleMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+ rwfAdapter.update(roleMod);
+ return deprovision(sbj);
+ }
+
+ @Override
+ protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+ final RoleMod roleMod = new RoleMod();
+ roleMod.setKey(sbj.getKey());
+ roleMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+ rwfAdapter.update(roleMod);
+ return provision(sbj, enabled);
+ }
+
+ @Override
+ protected String getName(final Subject<?, ?, ?> subject) {
+ return Role.class.cast(subject).getName();
+ }
+
+ @Override
+ protected AbstractSubjectTO getSubjectTO(final long key) {
+ try {
+ return roleTransfer.getRoleTO(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving user {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected Subject<?, ?, ?> getSubject(final long key) {
+ try {
+ return roleDAO.authFetch(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving role {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected ConnectorObject getRemoteObject(final String accountId) {
+ ConnectorObject obj = null;
+
+ try {
+ final Uid uid = new Uid(accountId);
+
+ obj = profile.getConnector().getObject(
+ ObjectClass.GROUP,
+ uid,
+ profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
+ } catch (TimeoutException toe) {
+ LOG.debug("Request timeout", toe);
+ throw toe;
+ } catch (RuntimeException ignore) {
+ LOG.debug("While resolving {}", accountId, ignore);
+ }
+ return obj;
+ }
+
+ @Override
+ protected Mapping<?> getMapping() {
+ return profile.getTask().getResource().getRmapping();
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
index 0c1ad8b,0000000..61637a4
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java
@@@ -1,160 -1,0 +1,167 @@@
+/*
+ * 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.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.UserTO;
++import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
++import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.Mapping;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.sync.UserPushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+
+public class UserPushResultHandlerImpl extends AbstractPushResultHandler implements UserPushResultHandler {
+
+ @Override
++ protected AttributableUtil getAttributableUtil() {
++ return attrUtilFactory.getInstance(AttributableType.USER);
++ }
++
++ @Override
+ protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
+ final UserTO before = userTransfer.getUserTO(sbj.getKey());
+
+ final List<String> noPropResources = new ArrayList<>(before.getResources());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getUserDeleteTaskIds(before.getKey(),
+ Collections.singleton(profile.getTask().getResource().getKey()), noPropResources));
+
+ return userDAO.authFetch(before.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+ final UserTO before = userTransfer.getUserTO(sbj.getKey());
+
+ final List<String> noPropResources = new ArrayList<>(before.getResources());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ final PropagationByResource propByRes = new PropagationByResource();
+ propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getUserCreateTaskIds(
+ before.getKey(),
+ enabled,
+ propByRes,
+ null,
+ Collections.unmodifiableCollection(before.getVirAttrs()),
+ Collections.unmodifiableCollection(before.getMemberships()),
+ noPropResources));
+
+ return userDAO.authFetch(before.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(sbj.getKey());
+
+ if (unlink) {
+ userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+ } else {
+ userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+ }
+
+ uwfAdapter.update(userMod);
+
+ return userDAO.authFetch(userMod.getKey());
+ }
+
+ @Override
+ protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(sbj.getKey());
+ userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+ uwfAdapter.update(userMod);
+ return deprovision(sbj);
+ }
+
+ @Override
+ protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+ final UserMod userMod = new UserMod();
+ userMod.setKey(sbj.getKey());
+ userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+ uwfAdapter.update(userMod);
+ return provision(sbj, enabled);
+ }
+
+ @Override
+ protected String getName(final Subject<?, ?, ?> subject) {
+ return User.class.cast(subject).getUsername();
+ }
+
+ @Override
+ protected AbstractSubjectTO getSubjectTO(final long key) {
+ try {
+ return userTransfer.getUserTO(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving user {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected Subject<?, ?, ?> getSubject(final long key) {
+ try {
+ return userDAO.authFetch(key);
+ } catch (Exception e) {
+ LOG.warn("Error retrieving user {}", key, e);
+ return null;
+ }
+ }
+
+ @Override
+ protected ConnectorObject getRemoteObject(final String accountId) {
+ ConnectorObject obj = null;
+
+ try {
+ final Uid uid = new Uid(accountId);
+
+ obj = profile.getConnector().getObject(
+ ObjectClass.ACCOUNT,
+ uid,
+ profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
+
+ } catch (TimeoutException toe) {
+ LOG.debug("Request timeout", toe);
+ throw toe;
+ } catch (RuntimeException ignore) {
+ LOG.debug("While resolving {}", accountId, ignore);
+ }
+ return obj;
+ }
+
+ @Override
+ protected Mapping<?> getMapping() {
+ return profile.getTask().getResource().getUmapping();
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractTaskITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractTaskITCase.java
index 6ba1dd8,0000000..669d088
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractTaskITCase.java
@@@ -1,137 -1,0 +1,155 @@@
+/*
+ * 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.apache.syncope.fit.core.reference.AbstractITCase.taskService;
+import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
++import org.apache.syncope.common.lib.to.NotificationTaskTO;
++import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.syncope.common.lib.to.UserTO;
++import org.apache.syncope.common.lib.types.TaskType;
+
+public abstract class AbstractTaskITCase extends AbstractITCase {
+
+ protected static final Long SYNC_TASK_ID = 4L;
+
+ protected static final Long SCHED_TASK_ID = 5L;
+
+ protected static class ThreadExec implements Callable<TaskExecTO> {
+
+ private final AbstractTaskITCase test;
+
+ private final Long taskKey;
+
+ private final int maxWaitSeconds;
+
+ private final boolean dryRun;
+
+ public ThreadExec(AbstractTaskITCase test, Long taskKey, int maxWaitSeconds, boolean dryRun) {
+ this.test = test;
+ this.taskKey = taskKey;
+ this.maxWaitSeconds = maxWaitSeconds;
+ this.dryRun = dryRun;
+ }
+
+ @Override
+ public TaskExecTO call() throws Exception {
+ return test.execSyncTask(taskKey, maxWaitSeconds, dryRun);
+ }
+ }
+
+ /**
+ * Remove initial and synchronized users to make test re-runnable.
+ */
+ protected void removeTestUsers() {
+ for (int i = 0; i < 10; i++) {
+ String cUserName = "test" + i;
+ try {
+ UserTO cUserTO = readUser(cUserName);
+ userService.delete(cUserTO.getKey());
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ }
+
+ protected TaskExecTO execSyncTask(final Long taskKey, final int maxWaitSeconds, final boolean dryRun) {
+ AbstractTaskTO taskTO = taskService.read(taskKey);
+ assertNotNull(taskTO);
+ assertNotNull(taskTO.getExecutions());
+
+ int preSyncSize = taskTO.getExecutions().size();
+ TaskExecTO execution = taskService.execute(taskTO.getKey(), dryRun);
+ assertEquals("JOB_FIRED", execution.getStatus());
+
+ int i = 0;
+ int maxit = maxWaitSeconds;
+
+ // wait for sync completion (executions incremented)
+ do {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+
+ taskTO = taskService.read(taskTO.getKey());
+
+ assertNotNull(taskTO);
+ assertNotNull(taskTO.getExecutions());
+
+ i++;
+ } while (preSyncSize == taskTO.getExecutions().size() && i < maxit);
+ if (i == maxit) {
+ fail("Timeout when executing task " + taskKey);
+ }
+ return taskTO.getExecutions().get(taskTO.getExecutions().size() - 1);
+ }
+
+ protected Map<Long, TaskExecTO> execSyncTasks(
+ final Set<Long> taskKeys, final int maxWaitSeconds, final boolean dryRun) throws Exception {
+
+ final ExecutorService service = Executors.newFixedThreadPool(taskKeys.size());
+ final List<Future<TaskExecTO>> futures = new ArrayList<>();
+
+ for (Long key : taskKeys) {
+ futures.add(service.submit(new ThreadExec(this, key, maxWaitSeconds, dryRun)));
+ }
+
+ final Map<Long, TaskExecTO> res = new HashMap<>();
+
+ for (Future<TaskExecTO> future : futures) {
+ TaskExecTO taskExecTO = future.get(100, TimeUnit.SECONDS);
+ res.put(taskExecTO.getTask(), taskExecTO);
+ }
+
+ service.shutdownNow();
+
+ return res;
+ }
+
++ protected NotificationTaskTO findNotificationTaskBySender(final String sender) {
++ PagedResult<NotificationTaskTO> tasks = taskService.list(TaskType.NOTIFICATION);
++ assertNotNull(tasks);
++ assertFalse(tasks.getResult().isEmpty());
++ NotificationTaskTO taskTO = null;
++ for (NotificationTaskTO task : tasks.getResult()) {
++ if (sender.equals(task.getSender())) {
++ taskTO = task;
++ }
++ }
++ return taskTO;
++ }
++
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/NotificationTaskITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/NotificationTaskITCase.java
index 87a49fb,0000000..ee75de9
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/NotificationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/NotificationTaskITCase.java
@@@ -1,155 -1,0 +1,140 @@@
+/*
+ * 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.assertTrue;
+
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.common.lib.to.NotificationTaskTO;
- import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.IntMappingType;
- import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.common.rest.api.service.NotificationService;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class NotificationTaskITCase extends AbstractTaskITCase {
+
+ @Test
+ public void issueSYNCOPE81() {
+ String sender = "syncope81@syncope.apache.org";
+ createNotificationTask(sender);
+ NotificationTaskTO taskTO = findNotificationTaskBySender(sender);
+ assertNotNull(taskTO);
+
+ assertTrue(taskTO.getExecutions().isEmpty());
+
+ // generate an execution in order to verify the deletion of a notification task with one or more executions
+ TaskExecTO execution = taskService.execute(taskTO.getKey(), false);
+ assertEquals("NOT_SENT", execution.getStatus());
+
+ int i = 0;
+ int maxit = 50;
+ int executions = 0;
+
+ // wait for task exec completion (executions incremented)
+ do {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+
+ taskTO = taskService.read(taskTO.getKey());
+
+ assertNotNull(taskTO);
+ assertNotNull(taskTO.getExecutions());
+
+ i++;
+ } while (executions == taskTO.getExecutions().size() && i < maxit);
+
+ assertFalse(taskTO.getExecutions().isEmpty());
+
+ taskService.delete(taskTO.getKey());
+ }
+
+ @Test
+ public void issueSYNCOPE86() {
+ // 1. create notification task
+ String sender = "syncope86@syncope.apache.org";
+ createNotificationTask(sender);
+
+ // 2. get NotificationTaskTO for user just created
+ NotificationTaskTO taskTO = findNotificationTaskBySender(sender);
+ assertNotNull(taskTO);
+ assertTrue(taskTO.getExecutions().isEmpty());
+
+ try {
+ // 3. execute the generated NotificationTask
+ TaskExecTO execution = taskService.execute(taskTO.getKey(), false);
+ assertNotNull(execution);
+
+ // 4. verify
+ taskTO = taskService.read(taskTO.getKey());
+ assertNotNull(taskTO);
+ assertEquals(1, taskTO.getExecutions().size());
+ } finally {
+ // Remove execution to make test re-runnable
+ taskService.deleteExecution(taskTO.getExecutions().get(0).getKey());
+ }
+ }
+
- private NotificationTaskTO findNotificationTaskBySender(final String sender) {
- PagedResult<NotificationTaskTO> tasks = taskService.list(TaskType.NOTIFICATION);
- assertNotNull(tasks);
- assertFalse(tasks.getResult().isEmpty());
- NotificationTaskTO taskTO = null;
- for (NotificationTaskTO task : tasks.getResult()) {
- if (sender.equals(task.getSender())) {
- taskTO = task;
- }
- }
- return taskTO;
- }
-
+ private void createNotificationTask(final String sender) {
+ // 1. Create notification
+ NotificationTO notification = new NotificationTO();
+ notification.setTraceLevel(TraceLevel.FAILURES);
+ notification.getEvents().add("[REST]:[UserLogic]:[]:[create]:[SUCCESS]");
+
+ notification.setUserAbout(SyncopeClient.getUserSearchConditionBuilder().hasRoles(7L).query());
+
+ notification.setRecipients(SyncopeClient.getUserSearchConditionBuilder().hasRoles(8L).query());
+ notification.setSelfAsRecipient(true);
+
+ notification.setRecipientAttrName("email");
+ notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
+
+ notification.setSender(sender);
+ String subject = "Test notification";
+ notification.setSubject(subject);
+ notification.setTemplate("optin");
+ notification.setActive(true);
+
+ Response response = notificationService.create(notification);
+ notification = getObject(response.getLocation(), NotificationService.class, NotificationTO.class);
+ assertNotNull(notification);
+
+ // 2. create user
+ UserTO userTO = UserITCase.getUniqueSampleTO("syncope@syncope.apache.org");
+ MembershipTO membershipTO = new MembershipTO();
+ membershipTO.setRoleId(7);
+ userTO.getMemberships().add(membershipTO);
+
+ userTO = createUser(userTO);
+ assertNotNull(userTO);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PushTaskITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PushTaskITCase.java
index d7d354f,0000000..3427a1d
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PushTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PushTaskITCase.java
@@@ -1,353 -1,0 +1,401 @@@
+/*
+ * 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.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.MappingTO;
++import org.apache.syncope.common.lib.to.NotificationTO;
++import org.apache.syncope.common.lib.to.NotificationTaskTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.PushTaskTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.lib.types.TaskType;
++import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
++import org.apache.syncope.common.rest.api.service.NotificationService;
+import org.apache.syncope.common.rest.api.service.ResourceService;
+import org.apache.syncope.common.rest.api.service.TaskService;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class PushTaskITCase extends AbstractTaskITCase {
+
+ @Test
+ public void getPushActionsClasses() {
+ List<String> actions = syncopeService.info().getPushActions();
+ assertNotNull(actions);
+ }
+
+ @Test
+ public void read() {
+ PushTaskTO pushTaskTO = taskService.<PushTaskTO>read(17L);
+ assertEquals(UnmatchingRule.ASSIGN, pushTaskTO.getUnmatchingRule());
+ assertEquals(MatchingRule.UPDATE, pushTaskTO.getMatchingRule());
+ }
+
+ @Test
+ public void list() {
+ final PagedResult<PushTaskTO> tasks = taskService.list(TaskType.PUSH);
+ assertFalse(tasks.getResult().isEmpty());
+ for (AbstractTaskTO task : tasks.getResult()) {
+ if (!(task instanceof PushTaskTO)) {
+ fail();
+ }
+ }
+ }
+
+ @Test
+ public void createPushTask() {
+ PushTaskTO task = new PushTaskTO();
+ task.setName("Test create Push");
+ task.setResource(RESOURCE_NAME_WS2);
+ task.setUserFilter(
+ SyncopeClient.getUserSearchConditionBuilder().hasNotResources(RESOURCE_NAME_TESTDB2).query());
+ task.setRoleFilter(
+ SyncopeClient.getRoleSearchConditionBuilder().isNotNull("cool").query());
+ task.setMatchingRule(MatchingRule.LINK);
+
+ final Response response = taskService.create(task);
+ final PushTaskTO actual = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
+ assertNotNull(actual);
+
+ task = taskService.read(actual.getKey());
+ assertNotNull(task);
+ assertEquals(task.getKey(), actual.getKey());
+ assertEquals(task.getJobClassName(), actual.getJobClassName());
+ assertEquals(task.getUserFilter(), actual.getUserFilter());
+ assertEquals(task.getRoleFilter(), actual.getRoleFilter());
+ assertEquals(UnmatchingRule.ASSIGN, actual.getUnmatchingRule());
+ assertEquals(MatchingRule.LINK, actual.getMatchingRule());
+ }
+
+ @Test
+ public void pushMatchingUnmatchingRoles() {
+ assertFalse(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
+
+ execSyncTask(23L, 50, false);
+
+ assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, 3L));
+ assertTrue(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
+
+ execSyncTask(23L, 50, false);
+
+ assertNotNull(resourceService.getConnectorObject(RESOURCE_NAME_LDAP, SubjectType.ROLE, 3L));
+ assertFalse(roleService.read(3L).getResources().contains(RESOURCE_NAME_LDAP));
+ }
+
+ @Test
+ public void pushUnmatchingUsers() throws Exception {
+ assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ assertFalse(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ assertFalse(userService.read(4L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ assertTrue(userService.read(5L).getResources().contains(RESOURCE_NAME_TESTDB2));
+
+ final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+ assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='puccini'").size());
+
+ // ------------------------------------------
+ // Unmatching --> Assign --> dryRuyn
+ // ------------------------------------------
+ execSyncTask(13L, 50, true);
+ assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='vivaldi'").size());
+ assertFalse(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ // ------------------------------------------
+
+ final Set<Long> pushTaskIds = new HashSet<>();
+ pushTaskIds.add(13L);
+ pushTaskIds.add(14L);
+ pushTaskIds.add(15L);
+ pushTaskIds.add(16L);
+ execSyncTasks(pushTaskIds, 50, false);
+
+ // ------------------------------------------
+ // Unatching --> Ignore
+ // ------------------------------------------
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+ assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ // ------------------------------------------
+
+ // ------------------------------------------
+ // Unmatching --> Assign
+ // ------------------------------------------
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='vivaldi'").size());
+ assertTrue(userService.read(3L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ jdbcTemplate.execute("DELETE FROM test2 WHERE ID='vivaldi'");
+ // ------------------------------------------
+
+ // ------------------------------------------
+ // Unmatching --> Provision
+ // ------------------------------------------
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='bellini'").size());
+ assertFalse(userService.read(4L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ jdbcTemplate.execute("DELETE FROM test2 WHERE ID='bellini'");
+ // ------------------------------------------
+
+ // ------------------------------------------
+ // Unmatching --> Unlink
+ // ------------------------------------------
+ assertEquals(0, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='puccini'").size());
+ assertFalse(userService.read(5L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ // ------------------------------------------
+ }
+
+ @Test
+ public void pushMatchingUser() throws Exception {
+ assertTrue(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+
+ final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
+
+ // ------------------------------------------
+ // Matching --> Deprovision --> dryRuyn
+ // ------------------------------------------
+ execSyncTask(19L, 50, true);
+ assertTrue(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
+ // ------------------------------------------
+
+ final Set<Long> pushTaskIds = new HashSet<>();
+ pushTaskIds.add(18L);
+ pushTaskIds.add(19L);
+ pushTaskIds.add(16L);
+
+ execSyncTasks(pushTaskIds, 50, false);
+
+ // ------------------------------------------
+ // Matching --> Deprovision && Ignore
+ // ------------------------------------------
+ assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ // DELETE Capability not available ....
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+ // ------------------------------------------
+
+ // ------------------------------------------
+ // Matching --> Unassign
+ // ------------------------------------------
+ assertFalse(userService.read(1L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ // DELETE Capability not available ....
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='rossini'").size());
+ // ------------------------------------------
+
+ // ------------------------------------------
+ // Matching --> Link
+ // ------------------------------------------
+ execSyncTask(20L, 50, false);
+ assertTrue(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+ // ------------------------------------------
+
+ pushTaskIds.clear();
+ pushTaskIds.add(21L);
+ pushTaskIds.add(22L);
+
+ execSyncTasks(pushTaskIds, 50, false);
+
+ // ------------------------------------------
+ // Matching --> Unlink && Update
+ // ------------------------------------------
+ assertFalse(userService.read(2L).getResources().contains(RESOURCE_NAME_TESTDB2));
+ assertEquals(1, jdbcTemplate.queryForList("SELECT ID FROM test2 WHERE ID='verdi'").size());
+ // ------------------------------------------
+ }
+
+ @Test
+ public void issueSYNCOPE598() {
+ // create a new role schema
+ final PlainSchemaTO schemaTO = new PlainSchemaTO();
+ schemaTO.setKey("LDAPGroupName" + getUUIDString());
+ schemaTO.setType(AttrSchemaType.String);
+ schemaTO.setMandatoryCondition("true");
+
+ final PlainSchemaTO newPlainSchemaTO = createSchema(AttributableType.ROLE, SchemaType.PLAIN, schemaTO);
+ assertEquals(schemaTO, newPlainSchemaTO);
+
+ // create a new sample role
+ RoleTO roleTO = new RoleTO();
+ roleTO.setName("all" + getUUIDString());
+ roleTO.setParent(8L);
+
+ roleTO.getRPlainAttrTemplates().add(newPlainSchemaTO.getKey());
+ roleTO.getPlainAttrs().add(attrTO(newPlainSchemaTO.getKey(), "all"));
+
+ roleTO = createRole(roleTO);
+ assertNotNull(roleTO);
+
+ String resourceName = "resource-ldap-roleonly";
+ ResourceTO newResourceTO = null;
+
+ try {
+ // Create resource ad-hoc
+ ResourceTO resourceTO = new ResourceTO();
+ resourceTO.setKey(resourceName);
+ resourceTO.setConnectorId(105L);
+
+ final MappingTO umapping = new MappingTO();
+ MappingItemTO item = new MappingItemTO();
+ item.setIntMappingType(IntMappingType.Username);
+ item.setExtAttrName("cn");
+ item.setAccountid(true);
+ item.setPurpose(MappingPurpose.PROPAGATION);
+ item.setMandatoryCondition("true");
+ umapping.setAccountIdItem(item);
+
+ item = new MappingItemTO();
+ item.setIntMappingType(IntMappingType.UserPlainSchema);
+ item.setExtAttrName("surname");
+ item.setIntAttrName("sn");
+ item.setPurpose(MappingPurpose.BOTH);
+ umapping.addItem(item);
+
+ item = new MappingItemTO();
+ item.setIntMappingType(IntMappingType.UserPlainSchema);
+ item.setExtAttrName("email");
+ item.setIntAttrName("mail");
+ item.setPurpose(MappingPurpose.BOTH);
+ umapping.addItem(item);
+
+ item = new MappingItemTO();
+ item.setIntMappingType(IntMappingType.Password);
+ item.setPassword(true);
+ item.setPurpose(MappingPurpose.BOTH);
+ item.setMandatoryCondition("true");
+ umapping.addItem(item);
+
+ umapping.setAccountLink("'cn=' + username + ',ou=people,o=isp'");
+
+ final MappingTO rmapping = new MappingTO();
+
+ item = new MappingItemTO();
+ item.setIntMappingType(IntMappingType.RolePlainSchema);
+ item.setExtAttrName("cn");
+ item.setIntAttrName(newPlainSchemaTO.getKey());
+ item.setAccountid(true);
+ item.setPurpose(MappingPurpose.BOTH);
+ rmapping.setAccountIdItem(item);
+
+ rmapping.setAccountLink("'cn=' + " + newPlainSchemaTO.getKey() + " + ',ou=groups,o=isp'");
+
+ resourceTO.setRmapping(rmapping);
+
+ Response response = resourceService.create(resourceTO);
+ newResourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
+
+ assertNotNull(newResourceTO);
+ assertNull(newResourceTO.getUmapping());
+ assertNotNull(newResourceTO.getRmapping());
+
+ // create push task ad-hoc
+ final PushTaskTO task = new PushTaskTO();
+ task.setName("issueSYNCOPE598");
+ task.setResource(resourceName);
+ task.setPerformCreate(true);
+ task.setPerformDelete(true);
+ task.setPerformUpdate(true);
+ task.setUnmatchingRule(UnmatchingRule.ASSIGN);
+ task.setMatchingRule(MatchingRule.UPDATE);
+
+ response = taskService.create(task);
+ final PushTaskTO push = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
+
+ assertNotNull(push);
+
+ // execute the new task
+ final TaskExecTO pushExec = execSyncTask(push.getKey(), 50, false);
+ assertTrue(PropagationTaskExecStatus.valueOf(pushExec.getStatus()).isSuccessful());
+ } finally {
+ roleService.delete(roleTO.getKey());
+ if (newResourceTO != null) {
+ resourceService.delete(resourceName);
+ }
+ }
+ }
++
++ @Test
++ public void issueSYNCOPE648() {
++ //1. Create Push Task
++ final PushTaskTO task = new PushTaskTO();
++ task.setName("Test create Push");
++ task.setResource(RESOURCE_NAME_LDAP);
++ task.setUserFilter(
++ SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("_NO_ONE_").query());
++ task.setRoleFilter(
++ SyncopeClient.getRoleSearchConditionBuilder().is("name").equalTo("citizen").query());
++ task.setMatchingRule(MatchingRule.IGNORE);
++ task.setUnmatchingRule(UnmatchingRule.IGNORE);
++
++ final Response response = taskService.create(task);
++ final PushTaskTO actual = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
++ assertNotNull(actual);
++
++ // 2. Create notification
++ NotificationTO notification = new NotificationTO();
++ notification.setTraceLevel(TraceLevel.FAILURES);
++ notification.getEvents().add("[PushTask]:[role]:[resource-ldap]:[matchingrule_ignore]:[SUCCESS]");
++ notification.getEvents().add("[PushTask]:[role]:[resource-ldap]:[unmatchingrule_ignore]:[SUCCESS]");
++
++ notification.getStaticRecipients().add("issueyncope648@syncope.apache.org");
++ notification.setSelfAsRecipient(false);
++ notification.setRecipientAttrName("email");
++ notification.setRecipientAttrType(IntMappingType.UserPlainSchema);
++
++ notification.setSender("syncope648@syncope.apache.org");
++ String subject = "Test notification";
++ notification.setSubject(subject);
++ notification.setTemplate("optin");
++ notification.setActive(true);
++
++ Response responseNotification = notificationService.create(notification);
++ notification = getObject(responseNotification.getLocation(), NotificationService.class, NotificationTO.class);
++ assertNotNull(notification);
++
++ execSyncTask(actual.getKey(), 50, false);
++
++ NotificationTaskTO taskTO = findNotificationTaskBySender("syncope648@syncope.apache.org");
++ assertNotNull(taskTO);
++ }
+}
[3/5] syncope git commit: [SYNCOPE-648]Merge from 1_2_X
Posted by md...@apache.org.
[SYNCOPE-648]Merge from 1_2_X
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/53721b82
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/53721b82
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/53721b82
Branch: refs/heads/master
Commit: 53721b8242f506260d91c70af456491ab85ab53f
Parents: 28aef6c 89d5ad4
Author: Marco Di Sabatino Di Diodoro <md...@apache.org>
Authored: Tue Mar 17 17:52:15 2015 +0100
Committer: Marco Di Sabatino Di Diodoro <md...@apache.org>
Committed: Tue Mar 17 17:52:15 2015 +0100
----------------------------------------------------------------------
.../syncope/common/lib/types/MatchingRule.java | 6 ++-
.../common/lib/types/UnmatchingRule.java | 6 ++-
.../apache/syncope/core/logic/LoggerLogic.java | 14 ++++++
.../java/sync/AbstractProvisioningJob.java | 31 +++++++++++--
.../java/sync/AbstractPushResultHandler.java | 17 +++++--
.../java/sync/AbstractSyncResultHandler.java | 49 +++++++++++++++++---
.../java/sync/RolePushResultHandlerImpl.java | 7 +++
.../java/sync/UserPushResultHandlerImpl.java | 7 +++
.../fit/core/reference/AbstractTaskITCase.java | 18 +++++++
.../core/reference/NotificationTaskITCase.java | 15 ------
.../fit/core/reference/PushTaskITCase.java | 48 +++++++++++++++++++
11 files changed, 185 insertions(+), 33 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/common/lib/src/main/java/org/apache/syncope/common/lib/types/MatchingRule.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/types/MatchingRule.java
index 39764d9,0000000..4b3ad53
mode 100644,000000..100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/MatchingRule.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/MatchingRule.java
@@@ -1,54 -1,0 +1,58 @@@
+/*
+ * 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.common.lib.types;
+
+import javax.xml.bind.annotation.XmlEnum;
+
+/**
+ * Sync/Push task matching rule.
+ */
+@XmlEnum
+public enum MatchingRule {
+
+ /**
+ * Do not perform any action.
+ */
+ IGNORE,
+ /**
+ * Update matching entity.
+ */
+ UPDATE,
+ /**
+ * Delete resource entity.
+ */
+ DEPROVISION,
+ /**
+ * Unlink resource and delete resource entity.
+ */
+ UNASSIGN,
+ /**
+ * Just unlink resource without performing any (de-)provisioning operation.
+ */
+ UNLINK,
+ /**
+ * Just link resource without performing any (de-)provisioning operation.
+ */
- LINK
++ LINK;
+
++ public static String toEventName(final MatchingRule rule) {
++ return new StringBuilder(MatchingRule.class.getSimpleName()).
++ append("_").append(rule.name()).toString().toLowerCase();
++ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/common/lib/src/main/java/org/apache/syncope/common/lib/types/UnmatchingRule.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/types/UnmatchingRule.java
index f579128,0000000..ef3b732
mode 100644,000000..100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/UnmatchingRule.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/UnmatchingRule.java
@@@ -1,47 -1,0 +1,51 @@@
+/*
+ * 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.common.lib.types;
+
+import javax.xml.bind.annotation.XmlEnum;
+
+/**
+ * Sync/Push task un-matching rule.
+ */
+@XmlEnum
+public enum UnmatchingRule {
+
+ /**
+ * Do not perform any action.
+ */
+ IGNORE,
+ /**
+ * Link the resource and create entity.
+ */
+ ASSIGN,
+ /**
+ * Create entity without linking the resource.
+ */
+ PROVISION,
+ /**
+ * Just unlink resource without performing any (de-)provisioning operation.
+ * In case of sync task UNLINK and IGNORE will coincide.
+ */
- UNLINK
++ UNLINK;
+
++ public static String toEventName(final UnmatchingRule rule) {
++ return new StringBuilder(UnmatchingRule.class.getSimpleName()).
++ append("_").append(rule.name()).toString().toLowerCase();
++ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
index f1efdea,0000000..7a0f10e
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
@@@ -1,307 -1,0 +1,321 @@@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.EventCategoryTO;
+import org.apache.syncope.common.lib.to.LoggerTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.AuditElements.EventCategoryType;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.LoggerLevel;
+import org.apache.syncope.common.lib.types.LoggerType;
++import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.TaskType;
++import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.Logger;
+import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.core.misc.spring.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.SystemPropertyUtils;
+
+@Component
+public class LoggerLogic extends AbstractTransactionalLogic<LoggerTO> {
+
+ @Autowired
+ private LoggerDAO loggerDAO;
+
+ @Autowired
+ private ExternalResourceDAO resourceDAO;
+
+ @Autowired
+ private TaskDAO taskDAO;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ private List<LoggerTO> list(final LoggerType type) {
+ List<LoggerTO> result = new ArrayList<>();
+ for (Logger logger : loggerDAO.findAll(type)) {
+ LoggerTO loggerTO = new LoggerTO();
+ BeanUtils.copyProperties(logger, loggerTO);
+ result.add(loggerTO);
+ }
+
+ return result;
+ }
+
+ @PreAuthorize("hasRole('LOG_LIST')")
+ @Transactional(readOnly = true)
+ public List<LoggerTO> listLogs() {
+ return list(LoggerType.LOG);
+ }
+
+ @PreAuthorize("hasRole('AUDIT_LIST')")
+ @Transactional(readOnly = true)
+ public List<AuditLoggerName> listAudits() {
+ List<AuditLoggerName> result = new ArrayList<>();
+
+ for (LoggerTO logger : list(LoggerType.AUDIT)) {
+ try {
+ result.add(AuditLoggerName.fromLoggerName(logger.getKey()));
+ } catch (Exception e) {
+ LOG.warn("Unexpected audit logger name: {}", logger.getKey(), e);
+ }
+ }
+
+ return result;
+ }
+
+ private void throwInvalidLogger(final LoggerType type) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidLogger);
+ sce.getElements().add("Expected " + type.name());
+
+ throw sce;
+ }
+
+ private LoggerTO setLevel(final String name, final Level level, final LoggerType expectedType) {
+ Logger syncopeLogger = loggerDAO.find(name);
+ if (syncopeLogger == null) {
+ LOG.debug("Logger {} not found: creating new...", name);
+
+ syncopeLogger = entityFactory.newEntity(Logger.class);
+ syncopeLogger.setKey(name);
+ syncopeLogger.setType(name.startsWith(LoggerType.AUDIT.getPrefix())
+ ? LoggerType.AUDIT
+ : LoggerType.LOG);
+ }
+
+ if (expectedType != syncopeLogger.getType()) {
+ throwInvalidLogger(expectedType);
+ }
+
+ syncopeLogger.setLevel(LoggerLevel.fromLevel(level));
+ syncopeLogger = loggerDAO.save(syncopeLogger);
+
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ LoggerConfig logConf = SyncopeConstants.ROOT_LOGGER.equals(name)
+ ? ctx.getConfiguration().getLoggerConfig(LogManager.ROOT_LOGGER_NAME)
+ : ctx.getConfiguration().getLoggerConfig(name);
+ logConf.setLevel(level);
+ ctx.updateLoggers();
+
+ LoggerTO result = new LoggerTO();
+ BeanUtils.copyProperties(syncopeLogger, result);
+
+ return result;
+ }
+
+ @PreAuthorize("hasRole('LOG_SET_LEVEL')")
+ public LoggerTO setLogLevel(final String name, final Level level) {
+ return setLevel(name, level, LoggerType.LOG);
+ }
+
+ @PreAuthorize("hasRole('AUDIT_ENABLE')")
+ public void enableAudit(final AuditLoggerName auditLoggerName) {
+ try {
+ setLevel(auditLoggerName.toLoggerName(), Level.DEBUG, LoggerType.AUDIT);
+ } catch (IllegalArgumentException e) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidLogger);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ }
+
+ private LoggerTO delete(final String name, final LoggerType expectedType) throws NotFoundException {
+ Logger syncopeLogger = loggerDAO.find(name);
+ if (syncopeLogger == null) {
+ throw new NotFoundException("Logger " + name);
+ } else if (expectedType != syncopeLogger.getType()) {
+ throwInvalidLogger(expectedType);
+ }
+
+ LoggerTO loggerToDelete = new LoggerTO();
+ BeanUtils.copyProperties(syncopeLogger, loggerToDelete);
+
+ // remove SyncopeLogger from local storage, so that LoggerLoader won't load this next time
+ loggerDAO.delete(syncopeLogger);
+
+ // set log level to OFF in order to disable configured logger until next reboot
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ org.apache.logging.log4j.core.Logger logger = SyncopeConstants.ROOT_LOGGER.equals(name)
+ ? ctx.getLogger(LogManager.ROOT_LOGGER_NAME) : ctx.getLogger(name);
+ logger.setLevel(Level.OFF);
+ ctx.updateLoggers();
+
+ return loggerToDelete;
+ }
+
+ @PreAuthorize("hasRole('LOG_DELETE')")
+ public LoggerTO deleteLog(final String name) throws NotFoundException {
+ return delete(name, LoggerType.LOG);
+ }
+
+ @PreAuthorize("hasRole('AUDIT_DISABLE')")
+ public void disableAudit(final AuditLoggerName auditLoggerName) {
+ try {
+ delete(auditLoggerName.toLoggerName(), LoggerType.AUDIT);
+ } catch (NotFoundException e) {
+ LOG.debug("Ignoring disable of non existing logger {}", auditLoggerName.toLoggerName());
+ } catch (IllegalArgumentException e) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidLogger);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ }
+
+ @PreAuthorize("hasRole('AUDIT_LIST') or hasRole('NOTIFICATION_LIST')")
+ public List<EventCategoryTO> listAuditEvents() {
+ // use set to avoi duplications or null elements
+ final Set<EventCategoryTO> events = new HashSet<EventCategoryTO>();
+
+ try {
+ final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
+ final MetadataReaderFactory metadataReaderFactory =
+ new CachingMetadataReaderFactory(resourcePatternResolver);
+
+ final String packageSearchPath =
+ ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ + ClassUtils.convertClassNameToResourcePath(
+ SystemPropertyUtils.resolvePlaceholders(this.getClass().getPackage().getName()))
+ + "/" + "**/*.class";
+
+ final Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
+ for (Resource resource : resources) {
+ if (resource.isReadable()) {
+ final MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
+ final Class<?> clazz = Class.forName(metadataReader.getClassMetadata().getClassName());
+
+ if (clazz.isAnnotationPresent(Component.class)
+ && AbstractLogic.class.isAssignableFrom(clazz)) {
+ final EventCategoryTO eventCategoryTO = new EventCategoryTO();
+ eventCategoryTO.setCategory(clazz.getSimpleName());
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (Modifier.isPublic(method.getModifiers())) {
+ eventCategoryTO.getEvents().add(method.getName());
+ }
+ }
+ events.add(eventCategoryTO);
+ }
+ }
+ }
+
+ //SYNCOPE-608
+ final EventCategoryTO authenticationControllerEvents = new EventCategoryTO();
+ authenticationControllerEvents.setCategory("AuthenticationController");
+ authenticationControllerEvents.getEvents().add("login");
+ events.add(authenticationControllerEvents);
+
+ events.add(new EventCategoryTO(EventCategoryType.PROPAGATION));
+ events.add(new EventCategoryTO(EventCategoryType.SYNCHRONIZATION));
+ events.add(new EventCategoryTO(EventCategoryType.PUSH));
+
+ for (AttributableType attributableType : AttributableType.values()) {
+ for (ExternalResource resource : resourceDAO.findAll()) {
+ final EventCategoryTO propEventCategoryTO = new EventCategoryTO(EventCategoryType.PROPAGATION);
+ final EventCategoryTO syncEventCategoryTO = new EventCategoryTO(EventCategoryType.SYNCHRONIZATION);
+ final EventCategoryTO pushEventCategoryTO = new EventCategoryTO(EventCategoryType.PUSH);
+
+ propEventCategoryTO.setCategory(attributableType.name().toLowerCase());
+ propEventCategoryTO.setSubcategory(resource.getKey());
+
+ syncEventCategoryTO.setCategory(attributableType.name().toLowerCase());
+ pushEventCategoryTO.setCategory(attributableType.name().toLowerCase());
+ syncEventCategoryTO.setSubcategory(resource.getKey());
+ pushEventCategoryTO.setSubcategory(resource.getKey());
+
+ for (ResourceOperation resourceOperation : ResourceOperation.values()) {
+ propEventCategoryTO.getEvents().add(resourceOperation.name().toLowerCase());
+ syncEventCategoryTO.getEvents().add(resourceOperation.name().toLowerCase());
+ pushEventCategoryTO.getEvents().add(resourceOperation.name().toLowerCase());
+ }
+
++ for (UnmatchingRule unmatching : UnmatchingRule.values()) {
++ String event = UnmatchingRule.toEventName(unmatching);
++ syncEventCategoryTO.getEvents().add(event);
++ pushEventCategoryTO.getEvents().add(event);
++ }
++
++ for (MatchingRule matching : MatchingRule.values()) {
++ String event = MatchingRule.toEventName(matching);
++ syncEventCategoryTO.getEvents().add(event);
++ pushEventCategoryTO.getEvents().add(event);
++ }
++
+ events.add(propEventCategoryTO);
+ events.add(syncEventCategoryTO);
+ events.add(pushEventCategoryTO);
+ }
+ }
+
+ for (SchedTask task : taskDAO.<SchedTask>findAll(TaskType.SCHEDULED)) {
+ final EventCategoryTO eventCategoryTO = new EventCategoryTO(EventCategoryType.TASK);
+ eventCategoryTO.setCategory(Class.forName(task.getJobClassName()).getSimpleName());
+ events.add(eventCategoryTO);
+ }
+
+ for (SyncTask task : taskDAO.<SyncTask>findAll(TaskType.SYNCHRONIZATION)) {
+ final EventCategoryTO eventCategoryTO = new EventCategoryTO(EventCategoryType.TASK);
+ eventCategoryTO.setCategory(Class.forName(task.getJobClassName()).getSimpleName());
+ events.add(eventCategoryTO);
+ }
+ } catch (Exception e) {
+ LOG.error("Failure retrieving audit/notification events", e);
+ }
+
+ return new ArrayList<>(events);
+ }
+
+ @Override
+ protected LoggerTO resolveReference(final Method method, final Object... args)
+ throws UnresolvedReferenceException {
+
+ throw new UnresolvedReferenceException();
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJob.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJob.java
index ea03a12,0000000..59c665d
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractProvisioningJob.java
@@@ -1,375 -1,0 +1,398 @@@
+/*
+ * 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.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.core.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
+import org.apache.syncope.core.persistence.api.entity.Entitlement;
+import org.apache.syncope.core.persistence.api.entity.role.RMapping;
+import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.persistence.api.entity.user.UMapping;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.ConnectorFactory;
+import org.apache.syncope.core.provisioning.api.sync.ProvisioningActions;
+import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.core.provisioning.java.job.AbstractTaskJob;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * Job for executing synchronization tasks.
+ *
+ * @see AbstractTaskJob
+ * @see SyncTask
+ * @see PushTask
+ */
+public abstract class AbstractProvisioningJob<T extends ProvisioningTask, A extends ProvisioningActions>
+ extends AbstractTaskJob {
+
+ /**
+ * ConnInstance loader.
+ */
+ @Autowired
+ protected ConnectorFactory connFactory;
+
+ /**
+ * Resource DAO.
+ */
+ @Autowired
+ protected ExternalResourceDAO resourceDAO;
+
+ /**
+ * Entitlement DAO.
+ */
+ @Autowired
+ protected EntitlementDAO entitlementDAO;
+
+ /**
+ * Policy DAO.
+ */
+ @Autowired
+ protected PolicyDAO policyDAO;
+
+ /**
+ * SyncJob actions.
+ */
+ protected List<A> actions;
+
+ public void setActions(final List<A> actions) {
+ this.actions = actions;
+ }
+
+ /**
+ * Create a textual report of the synchronization, based on the trace level.
+ *
+ * @param provResults Sync results
+ * @param syncTraceLevel Sync trace level
+ * @param dryRun dry run?
+ * @return report as string
+ */
+ protected String createReport(final Collection<ProvisioningResult> provResults, final TraceLevel syncTraceLevel,
+ final boolean dryRun) {
+
+ if (syncTraceLevel == TraceLevel.NONE) {
+ return null;
+ }
+
+ StringBuilder report = new StringBuilder();
+
+ if (dryRun) {
+ report.append("==>Dry run only, no modifications were made<==\n\n");
+ }
+
+ List<ProvisioningResult> uSuccCreate = new ArrayList<>();
+ List<ProvisioningResult> uFailCreate = new ArrayList<>();
+ List<ProvisioningResult> uSuccUpdate = new ArrayList<>();
+ List<ProvisioningResult> uFailUpdate = new ArrayList<>();
+ List<ProvisioningResult> uSuccDelete = new ArrayList<>();
+ List<ProvisioningResult> uFailDelete = new ArrayList<>();
++ List<ProvisioningResult> uSuccNone = new ArrayList<>();
+ List<ProvisioningResult> rSuccCreate = new ArrayList<>();
+ List<ProvisioningResult> rFailCreate = new ArrayList<>();
+ List<ProvisioningResult> rSuccUpdate = new ArrayList<>();
+ List<ProvisioningResult> rFailUpdate = new ArrayList<>();
+ List<ProvisioningResult> rSuccDelete = new ArrayList<>();
+ List<ProvisioningResult> rFailDelete = new ArrayList<>();
++ List<ProvisioningResult> rSuccNone = new ArrayList<>();
+
+ for (ProvisioningResult provResult : provResults) {
+ switch (provResult.getStatus()) {
+ case SUCCESS:
+ switch (provResult.getOperation()) {
+ case CREATE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uSuccCreate.add(provResult);
+ break;
+
+ case ROLE:
+ rSuccCreate.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ case UPDATE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uSuccUpdate.add(provResult);
+ break;
+
+ case ROLE:
+ rSuccUpdate.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ case DELETE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uSuccDelete.add(provResult);
+ break;
+
+ case ROLE:
+ rSuccDelete.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
++ case NONE:
++ switch (provResult.getSubjectType()) {
++ case USER:
++ uSuccNone.add(provResult);
++ break;
++
++ case ROLE:
++ rSuccNone.add(provResult);
++ break;
++
++ default:
++ }
++ break;
++
+ default:
+ }
+ break;
+
+ case FAILURE:
+ switch (provResult.getOperation()) {
+ case CREATE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uFailCreate.add(provResult);
+ break;
+
+ case ROLE:
+ rFailCreate.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ case UPDATE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uFailUpdate.add(provResult);
+ break;
+
+ case ROLE:
+ rFailUpdate.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ case DELETE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uFailDelete.add(provResult);
+ break;
+
+ case ROLE:
+ rFailDelete.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ default:
+ }
+ break;
+
+ default:
+ }
+ }
+
+ // Summary, also to be included for FAILURE and ALL, so create it anyway.
+ report.append("Users ").
+ append("[created/failures]: ").append(uSuccCreate.size()).append('/').append(uFailCreate.size()).
+ append(' ').
+ append("[updated/failures]: ").append(uSuccUpdate.size()).append('/').append(uFailUpdate.size()).
+ append(' ').
+ append("[deleted/failures]: ").append(uSuccDelete.size()).append('/').append(uFailDelete.size()).
- append('\n');
++ append(' ').
++ append("[ignored]: ").append(uSuccNone.size()).append('\n');
+ report.append("Roles ").
+ append("[created/failures]: ").append(rSuccCreate.size()).append('/').append(rFailCreate.size()).
+ append(' ').
+ append("[updated/failures]: ").append(rSuccUpdate.size()).append('/').append(rFailUpdate.size()).
+ append(' ').
- append("[deleted/failures]: ").append(rSuccDelete.size()).append('/').append(rFailDelete.size());
++ append("[deleted/failures]: ").append(rSuccDelete.size()).append('/').append(rFailDelete.size()).
++ append(' ').
++ append("[ignored]: ").append(rSuccNone.size());
+
+ // Failures
+ if (syncTraceLevel == TraceLevel.FAILURES || syncTraceLevel == TraceLevel.ALL) {
+ if (!uFailCreate.isEmpty()) {
+ report.append("\n\nUsers failed to create: ");
+ report.append(ProvisioningResult.produceReport(uFailCreate, syncTraceLevel));
+ }
+ if (!uFailUpdate.isEmpty()) {
+ report.append("\nUsers failed to update: ");
+ report.append(ProvisioningResult.produceReport(uFailUpdate, syncTraceLevel));
+ }
+ if (!uFailDelete.isEmpty()) {
+ report.append("\nUsers failed to delete: ");
+ report.append(ProvisioningResult.produceReport(uFailDelete, syncTraceLevel));
+ }
+
+ if (!rFailCreate.isEmpty()) {
+ report.append("\n\nRoles failed to create: ");
+ report.append(ProvisioningResult.produceReport(rFailCreate, syncTraceLevel));
+ }
+ if (!rFailUpdate.isEmpty()) {
+ report.append("\nRoles failed to update: ");
+ report.append(ProvisioningResult.produceReport(rFailUpdate, syncTraceLevel));
+ }
+ if (!rFailDelete.isEmpty()) {
+ report.append("\nRoles failed to delete: ");
+ report.append(ProvisioningResult.produceReport(rFailDelete, syncTraceLevel));
+ }
+ }
+
+ // Succeeded, only if on 'ALL' level
+ if (syncTraceLevel == TraceLevel.ALL) {
+ report.append("\n\nUsers created:\n")
+ .append(ProvisioningResult.produceReport(uSuccCreate, syncTraceLevel))
+ .append("\nUsers updated:\n")
+ .append(ProvisioningResult.produceReport(uSuccUpdate, syncTraceLevel))
+ .append("\nUsers deleted:\n")
- .append(ProvisioningResult.produceReport(uSuccDelete, syncTraceLevel));
++ .append(ProvisioningResult.produceReport(uSuccDelete, syncTraceLevel))
++ .append("\nUsers ignored:\n")
++ .append(ProvisioningResult.produceReport(uSuccNone, syncTraceLevel));
+ report.append("\n\nRoles created:\n")
+ .append(ProvisioningResult.produceReport(rSuccCreate, syncTraceLevel))
+ .append("\nRoles updated:\n")
+ .append(ProvisioningResult.produceReport(rSuccUpdate, syncTraceLevel))
+ .append("\nRoles deleted:\n")
- .append(ProvisioningResult.produceReport(rSuccDelete, syncTraceLevel));
++ .append(ProvisioningResult.produceReport(rSuccDelete, syncTraceLevel))
++ .append("\nRoles ignored:\n")
++ .append(ProvisioningResult.produceReport(rSuccNone, syncTraceLevel));
+ }
+
+ return report.toString();
+ }
+
+ @Override
+ protected String doExecute(final boolean dryRun) throws JobExecutionException {
+ // PRE: grant all authorities (i.e. setup the SecurityContextHolder)
+ final List<GrantedAuthority> authorities = new ArrayList<>();
+
+ for (Entitlement entitlement : entitlementDAO.findAll()) {
+ authorities.add(new SimpleGrantedAuthority(entitlement.getKey()));
+ }
+
+ final UserDetails userDetails = new User("admin", "FAKE_PASSWORD", true, true, true, true, authorities);
+
+ SecurityContextHolder.getContext().setAuthentication(
+ new UsernamePasswordAuthenticationToken(userDetails, "FAKE_PASSWORD", authorities));
+
+ try {
+ final Class<T> clazz = getTaskClassReference();
+ if (!clazz.isAssignableFrom(task.getClass())) {
+ throw new JobExecutionException("Task " + taskId + " isn't a SyncTask");
+ }
+
+ final T syncTask = clazz.cast(this.task);
+
+ final Connector connector;
+ try {
+ connector = connFactory.getConnector(syncTask.getResource());
+ } catch (Exception e) {
+ final String msg = String.
+ format("Connector instance bean for resource %s and connInstance %s not found",
+ syncTask.getResource(), syncTask.getResource().getConnector());
+
+ throw new JobExecutionException(msg, e);
+ }
+
+ final UMapping uMapping = syncTask.getResource().getUmapping();
+ if (uMapping != null && uMapping.getAccountIdItem() == null) {
+ throw new JobExecutionException(
+ "Invalid user account id mapping for resource " + syncTask.getResource());
+ }
+ final RMapping rMapping = syncTask.getResource().getRmapping();
+ if (rMapping != null && rMapping.getAccountIdItem() == null) {
+ throw new JobExecutionException(
+ "Invalid role account id mapping for resource " + syncTask.getResource());
+ }
+ if (uMapping == null && rMapping == null) {
+ return "No mapping configured for both users and roles: aborting...";
+ }
+
+ return executeWithSecurityContext(
+ syncTask,
+ connector,
+ uMapping,
+ rMapping,
+ dryRun);
+ } catch (Throwable t) {
+ LOG.error("While executing provisioning job {}", getClass().getName(), t);
+ throw t;
+ } finally {
+ // POST: clean up the SecurityContextHolder
+ SecurityContextHolder.clearContext();
+ }
+ }
+
+ protected abstract String executeWithSecurityContext(
+ final T task,
+ final Connector connector,
+ final UMapping uMapping,
+ final RMapping rMapping,
+ final boolean dryRun) throws JobExecutionException;
+
+ @Override
+ protected boolean hasToBeRegistered(final TaskExec execution) {
+ final ProvisioningTask provTask = (ProvisioningTask) task;
+
+ // True if either failed and failures have to be registered, or if ALL has to be registered.
+ return (Status.valueOf(execution.getStatus()) == Status.FAILURE
+ && provTask.getResource().getSyncTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+ || provTask.getResource().getSyncTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal();
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class<T> getTaskClassReference() {
+ return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
index 5161c74,0000000..42dfedd
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AbstractPushResultHandler.java
@@@ -1,374 -1,0 +1,381 @@@
+/*
+ * 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.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.MembershipMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
- import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.Mapping;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.persistence.api.entity.membership.Membership;
+import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.core.provisioning.api.sync.PushActions;
+import org.apache.syncope.core.misc.MappingUtil;
+import org.apache.syncope.core.provisioning.api.sync.SyncopePushResultHandler;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.quartz.JobExecutionException;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions>
+ implements SyncopePushResultHandler {
+
++ protected abstract AttributableUtil getAttributableUtil();
++
+ protected abstract String getName(final Subject<?, ?, ?> subject);
+
+ protected abstract Mapping<?> getMapping();
+
+ protected abstract AbstractSubjectTO getSubjectTO(final long key);
+
+ protected abstract Subject<?, ?, ?> getSubject(final long key);
+
+ protected abstract Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj);
+
+ protected abstract Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled);
+
+ protected abstract Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink);
+
+ protected abstract Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj);
+
+ protected abstract Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, Boolean enabled);
+
+ protected abstract ConnectorObject getRemoteObject(final String accountId);
+
+ @Transactional
+ @Override
+ public boolean handle(final long subjectId) {
+ try {
+ doHandle(subjectId);
+ return true;
+ } catch (JobExecutionException e) {
+ LOG.error("Synchronization failed", e);
+ return false;
+ }
+ }
+
+ protected final void doHandle(final long subjectId)
+ throws JobExecutionException {
+
+ final Subject<?, ?, ?> subject = getSubject(subjectId);
+
+ final AttributableUtil attrUtil = attrUtilFactory.getInstance(subject);
+
+ final ProvisioningResult result = new ProvisioningResult();
+ profile.getResults().add(result);
+
+ result.setId(subject.getKey());
+ result.setSubjectType(attrUtil.getType());
+ result.setName(getName(subject));
+
+ final Boolean enabled = subject instanceof User && profile.getTask().isSyncStatus()
+ ? ((User) subject).isSuspended() ? Boolean.FALSE : Boolean.TRUE
+ : null;
+
+ LOG.debug("Propagating {} with key {} towards {}",
+ attrUtil.getType(), subject.getKey(), profile.getTask().getResource());
+
+ Object output = null;
+ Result resultStatus = null;
+ ConnectorObject beforeObj = null;
+ String operation = null;
+
+ // Try to read remote object (user / group) BEFORE any actual operation
+ final String accountId = MappingUtil.getAccountIdValue(
+ subject, profile.getTask().getResource(), getMapping().getAccountIdItem());
+
+ beforeObj = getRemoteObject(accountId);
+
+ Boolean status = profile.getTask().isSyncStatus() ? enabled : null;
+
+ if (profile.isDryRun()) {
+ if (beforeObj == null) {
+ result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+ } else {
+ result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+ }
+ result.setStatus(ProvisioningResult.Status.SUCCESS);
+ } else {
+ try {
+ if (beforeObj == null) {
- operation = profile.getTask().getUnmatchingRule().name().toLowerCase();
++ operation = UnmatchingRule.toEventName(profile.getTask().getUnmatchingRule());
+ result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+
+ switch (profile.getTask().getUnmatchingRule()) {
+ case ASSIGN:
+ for (PushActions action : profile.getActions()) {
+ action.beforeAssign(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformCreate()) {
+ LOG.debug("PushTask not configured for create");
+ } else {
+ assign(subject, status);
+ }
+
+ break;
+ case PROVISION:
+ for (PushActions action : profile.getActions()) {
+ action.beforeProvision(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformCreate()) {
+ LOG.debug("PushTask not configured for create");
+ } else {
+ provision(subject, status);
+ }
+
+ break;
+ case UNLINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnlink(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(subject, true);
+ }
+
+ break;
++ case IGNORE:
++ LOG.debug("Ignored subjectId: {}", subjectId);
++ break;
+ default:
+ // do nothing
+ }
+
+ } else {
- operation = profile.getTask().getMatchingRule().name().toLowerCase();
++ operation = MatchingRule.toEventName(profile.getTask().getMatchingRule());
+ result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+
+ switch (profile.getTask().getMatchingRule()) {
+ case UPDATE:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUpdate(this.getProfile(), subject);
+ }
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ update(subject, status);
+ }
+
+ break;
+ case DEPROVISION:
+ for (PushActions action : profile.getActions()) {
+ action.beforeDeprovision(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformDelete()) {
+ LOG.debug("PushTask not configured for delete");
+ } else {
+ deprovision(subject);
+ }
+
+ break;
+ case UNASSIGN:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnassign(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformDelete()) {
+ LOG.debug("PushTask not configured for delete");
+ } else {
+ unassign(subject);
+ }
+
+ break;
+ case LINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeLink(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(subject, false);
+ }
+
+ break;
+ case UNLINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnlink(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(subject, true);
+ }
+
+ break;
++ case IGNORE:
++ LOG.debug("Ignored subjectId: {}", subjectId);
++ break;
+ default:
+ // do nothing
+ }
+ }
+
+ for (PushActions action : profile.getActions()) {
+ action.after(this.getProfile(), subject, result);
+ }
+
+ result.setStatus(ProvisioningResult.Status.SUCCESS);
+ resultStatus = AuditElements.Result.SUCCESS;
+ output = getRemoteObject(accountId);
+ } catch (Exception e) {
+ result.setStatus(ProvisioningResult.Status.FAILURE);
+ result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+ resultStatus = AuditElements.Result.FAILURE;
+ output = e;
+
+ LOG.warn("Error pushing {} towards {}", subject, profile.getTask().getResource(), e);
+ throw new JobExecutionException(e);
+ } finally {
+ notificationManager.createTasks(
+ AuditElements.EventCategoryType.PUSH,
- AttributableType.USER.name().toLowerCase(),
++ getAttributableUtil().getType().name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ operation,
+ resultStatus,
+ beforeObj,
+ output,
+ subject);
+ auditManager.audit(
+ AuditElements.EventCategoryType.PUSH,
- AttributableType.USER.name().toLowerCase(),
++ getAttributableUtil().getType().name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ operation,
+ resultStatus,
+ beforeObj,
+ output,
+ subject);
+ }
+ }
+ }
+
+ private ResourceOperation getResourceOperation(final UnmatchingRule rule) {
+ switch (rule) {
+ case ASSIGN:
+ case PROVISION:
+ return ResourceOperation.CREATE;
+ default:
+ return ResourceOperation.NONE;
+ }
+ }
+
+ private ResourceOperation getResourceOperation(final MatchingRule rule) {
+ switch (rule) {
+ case UPDATE:
+ return ResourceOperation.UPDATE;
+ case DEPROVISION:
+ case UNASSIGN:
+ return ResourceOperation.DELETE;
+ default:
+ return ResourceOperation.NONE;
+ }
+ }
+
+ protected Subject<?, ?, ?> update(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+
+ final Set<MembershipMod> membsToAdd = new HashSet<>();
+ final Set<String> vattrToBeRemoved = new HashSet<>();
+ final Set<String> membVattrToBeRemoved = new HashSet<>();
+ final Set<AttrMod> vattrToBeUpdated = new HashSet<>();
+
+ // Search for all mapped vattrs
+ final Mapping<?> umapping = getMapping();
+ for (MappingItem mappingItem : umapping.getItems()) {
+ if (mappingItem.getIntMappingType() == IntMappingType.UserVirtualSchema) {
+ vattrToBeRemoved.add(mappingItem.getIntAttrName());
+ } else if (mappingItem.getIntMappingType() == IntMappingType.MembershipVirtualSchema) {
+ membVattrToBeRemoved.add(mappingItem.getIntAttrName());
+ }
+ }
+
+ // Search for all user's vattrs and:
+ // 1. add mapped vattrs not owned by the user to the set of vattrs to be removed
+ // 2. add all vattrs owned by the user to the set of vattrs to be update
+ for (VirAttr vattr : sbj.getVirAttrs()) {
+ vattrToBeRemoved.remove(vattr.getSchema().getKey());
+ final AttrMod mod = new AttrMod();
+ mod.setSchema(vattr.getSchema().getKey());
+ mod.getValuesToBeAdded().addAll(vattr.getValues());
+ vattrToBeUpdated.add(mod);
+ }
+
+ final boolean changepwd;
+
+ if (sbj instanceof User) {
+ changepwd = true;
+
+ // Search for memberships
+ for (Membership membership : User.class.cast(sbj).getMemberships()) {
+ final MembershipMod membershipMod = new MembershipMod();
+ membershipMod.setKey(membership.getKey());
+ membershipMod.setRole(membership.getRole().getKey());
+
+ for (VirAttr vattr : membership.getVirAttrs()) {
+ membVattrToBeRemoved.remove(vattr.getSchema().getKey());
+ final AttrMod mod = new AttrMod();
+ mod.setSchema(vattr.getSchema().getKey());
+ mod.getValuesToBeAdded().addAll(vattr.getValues());
+ membershipMod.getVirAttrsToUpdate().add(mod);
+ }
+
+ membsToAdd.add(membershipMod);
+ }
+
+ if (!membsToAdd.isEmpty()) {
+ membsToAdd.iterator().next().getVirAttrsToRemove().addAll(membVattrToBeRemoved);
+ }
+ } else {
+ changepwd = false;
+ }
+
+ final List<String> noPropResources = new ArrayList<>(sbj.getResourceNames());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ final PropagationByResource propByRes = new PropagationByResource();
+ propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getUpdateTaskIds(
+ sbj, null, changepwd, enabled, vattrToBeRemoved, vattrToBeUpdated, propByRes, noPropResources,
+ membsToAdd));
+
+ return userDAO.authFetch(sbj.getKey());
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/53721b82/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 04ad24d,0000000..02b9a1a
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,617 -1,0 +1,652 @@@
+/*
+ * 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.AttributableUtil;
+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.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.core.misc.security.UnauthorizedRoleException;
++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 SyncUtilities syncUtilities;
+
+ @Autowired
+ protected AttributableTransformer attrTransformer;
+
+ protected abstract AttributableUtil getAttributableUtil();
+
+ protected abstract String getName(AbstractSubjectTO subjectTO);
+
+ protected abstract AbstractSubjectTO getSubjectTO(long key);
+
+ protected abstract AbstractSubjectMod getSubjectMod(AbstractSubjectTO subjectTO, SyncDelta delta);
+
+ protected abstract AbstractSubjectTO create(AbstractSubjectTO subjectTO, SyncDelta _delta, ProvisioningResult result);
+
+ protected abstract AbstractSubjectTO link(AbstractSubjectTO before, ProvisioningResult result, boolean unlink);
+
+ protected abstract AbstractSubjectTO update(AbstractSubjectTO before, AbstractSubjectMod subjectMod,
+ SyncDelta delta, ProvisioningResult result);
+
+ protected abstract void deprovision(Long key, boolean unlink);
+
+ protected abstract void delete(Long key);
+
+ @Override
+ public boolean handle(final SyncDelta delta) {
+ try {
+ doHandle(delta);
+ return true;
+ } catch (JobExecutionException e) {
+ LOG.error("Synchronization failed", e);
+ return false;
+ }
+ }
+
+ protected List<ProvisioningResult> assign(final SyncDelta delta, final AttributableUtil attrUtil)
+ throws JobExecutionException {
+ if (!profile.getTask().isPerformCreate()) {
+ LOG.debug("SyncTask not configured for create");
+ return Collections.<ProvisioningResult>emptyList();
+ }
+
+ final AbstractSubjectTO subjectTO =
+ connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
+
+ subjectTO.getResources().add(profile.getTask().getResource().getKey());
+
+ final ProvisioningResult result = new ProvisioningResult();
+ result.setOperation(ResourceOperation.CREATE);
+ result.setSubjectType(attrUtil.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);
+ } else {
+ SyncDelta _delta = delta;
+ for (SyncActions action : profile.getActions()) {
+ _delta = action.beforeAssign(this.getProfile(), _delta, transformed);
+ }
+
- create(transformed, _delta, attrUtil, "assign", result);
++ create(transformed, _delta, attrUtil, UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), result);
+ }
+
+ return Collections.singletonList(result);
+ }
+
+ protected List<ProvisioningResult> create(final SyncDelta delta, final AttributableUtil attrUtil)
+ throws JobExecutionException {
+
+ if (!profile.getTask().isPerformCreate()) {
+ LOG.debug("SyncTask not configured for create");
+ return Collections.<ProvisioningResult>emptyList();
+ }
+
+ final AbstractSubjectTO subjectTO =
+ connObjectUtil.getSubjectTO(delta.getObject(), profile.getTask(), attrUtil);
+
+ // Attributable transformation (if configured)
+ AbstractSubjectTO transformed = attrTransformer.transform(subjectTO);
+ LOG.debug("Transformed: {}", transformed);
+
+ final ProvisioningResult result = new ProvisioningResult();
+ result.setOperation(ResourceOperation.CREATE);
+ result.setSubjectType(attrUtil.getType());
+ result.setStatus(ProvisioningResult.Status.SUCCESS);
+
+ result.setName(getName(transformed));
+
+ if (profile.isDryRun()) {
+ result.setId(0L);
+ } else {
+ SyncDelta _delta = delta;
+ for (SyncActions action : profile.getActions()) {
+ _delta = action.beforeProvision(this.getProfile(), _delta, transformed);
+ }
+
- create(transformed, _delta, attrUtil, "provision", result);
++ create(transformed, _delta, attrUtil, UnmatchingRule.toEventName(UnmatchingRule.PROVISION), result);
+ }
+
+ return Collections.<ProvisioningResult>singletonList(result);
+ }
+
+ private void create(
+ final AbstractSubjectTO subjectTO,
+ final SyncDelta delta,
+ final AttributableUtil attrUtil,
+ final String operation,
+ final ProvisioningResult result)
+ throws JobExecutionException {
+
+ Object output;
+ Result resultStatus;
+
+ try {
+ AbstractSubjectTO actual = create(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 {} {}", attrUtil.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 {} {} ", attrUtil.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 AttributableUtil attrUtil)
+ 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(attrUtil.getType());
+ result.setStatus(ProvisioningResult.Status.SUCCESS);
+ result.setId(key);
+
+ AbstractSubjectTO before = getSubjectTO(key);
+ if (before == null) {
+ result.setStatus(ProvisioningResult.Status.FAILURE);
+ result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.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);
+
+ 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", attrUtil.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 {} {}", attrUtil.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 {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+ output = e;
+ resultStatus = Result.FAILURE;
+ }
+ }
- audit("update", resultStatus, before, output, delta);
++ 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 AttributableUtil attrUtil,
+ 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(attrUtil.getType());
+ result.setStatus(ProvisioningResult.Status.SUCCESS);
+ result.setId(id);
+
+ final AbstractSubjectTO before = getSubjectTO(id);
+
+ if (before == null) {
+ result.setStatus(ProvisioningResult.Status.FAILURE);
+ result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.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);
+ 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", attrUtil.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 {} {}", attrUtil.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 {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+ output = e;
+ resultStatus = Result.FAILURE;
+ }
+ }
- audit(unlink ? "unassign" : "deprovision", resultStatus, before, output, delta);
++ 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 AttributableUtil attrUtil,
+ 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(attrUtil.getType());
+ result.setStatus(ProvisioningResult.Status.SUCCESS);
+ result.setId(id);
+
+ final AbstractSubjectTO before = getSubjectTO(id);
+
+ if (before == null) {
+ result.setStatus(ProvisioningResult.Status.FAILURE);
+ result.setMessage(String.format("Subject '%s(%d)' not found", attrUtil.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);
+
+ for (SyncActions action : profile.getActions()) {
+ action.after(this.getProfile(), delta, AbstractSubjectTO.class.cast(output), result);
+ }
+
+ resultStatus = Result.SUCCESS;
+ LOG.debug("{} {} successfully updated", attrUtil.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 {} {}", attrUtil.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 {} {}", attrUtil.getType(), delta.getUid().getUidValue(), e);
+ output = e;
+ resultStatus = Result.FAILURE;
+ }
+ }
- audit(unlink ? "unlink" : "link", resultStatus, before, output, delta);
++ 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 AttributableUtil attrUtil)
+ 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.setName(getName(before));
+ result.setOperation(ResourceOperation.DELETE);
+ result.setSubjectType(attrUtil.getType());
+ result.setStatus(ProvisioningResult.Status.SUCCESS);
+
+ if (!profile.isDryRun()) {
+ for (SyncActions action : profile.getActions()) {
+ delta = action.beforeDelete(this.getProfile(), delta, before);
+ }
+
+ try {
+ delete(id);
+ output = null;
+ resultStatus = Result.SUCCESS;
+ } catch (Exception e) {
+ result.setStatus(ProvisioningResult.Status.FAILURE);
+ result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+ LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
+ output = e;
+ }
+
+ for (SyncActions action : profile.getActions()) {
+ action.after(this.getProfile(), delta, before, result);
+ }
+
- audit("delete", resultStatus, before, output, delta);
++ audit(ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, output, delta);
+ }
+
+ delResults.add(result);
+
+ } catch (NotFoundException e) {
+ LOG.error("Could not find {} {}", attrUtil.getType(), id, e);
+ } catch (UnauthorizedRoleException e) {
+ LOG.error("Not allowed to read {} {}", attrUtil.getType(), id, e);
+ } catch (Exception e) {
+ LOG.error("Could not delete {} {}", attrUtil.getType(), id, e);
+ }
+ }
+
+ return delResults;
+ }
+
++ private List<ProvisioningResult> ignore(SyncDelta delta, final AttributableUtil attrUtil, final boolean matching)
++ throws JobExecutionException {
++
++ LOG.debug("Subject to ignore {}", delta.getObject().getUid().getUidValue());
++
++ final List<ProvisioningResult> ignoreResults = new ArrayList<ProvisioningResult>();
++ final ProvisioningResult result = new ProvisioningResult();
++
++ result.setId(null);
++ result.setName(delta.getObject().getUid().getUidValue());
++ result.setOperation(ResourceOperation.NONE);
++ result.setSubjectType(attrUtil.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)/role(s).
+ *
+ * @param delta returned by the underlying profile.getConnector()
+ * @throws JobExecutionException in case of synchronization failure.
+ */
+ protected final void doHandle(final SyncDelta delta)
+ throws JobExecutionException {
+
+ final AttributableUtil attrUtil = getAttributableUtil();
+
+ 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(), attrUtil);
+
+ 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, attrUtil));
+ break;
+ case PROVISION:
+ profile.getResults().addAll(create(delta, attrUtil));
+ break;
++ case IGNORE:
++ profile.getResults().addAll(ignore(delta, attrUtil, false));
++ break;
+ default:
+ // do nothing
+ }
+ } else {
+ switch (profile.getTask().getMatchingRule()) {
+ case UPDATE:
+ profile.getResults().addAll(update(delta, subjectKeys, attrUtil));
+ break;
+ case DEPROVISION:
+ profile.getResults().addAll(deprovision(delta, subjectKeys, attrUtil, false));
+ break;
+ case UNASSIGN:
+ profile.getResults().addAll(deprovision(delta, subjectKeys, attrUtil, true));
+ break;
+ case LINK:
+ profile.getResults().addAll(link(delta, subjectKeys, attrUtil, false));
+ break;
+ case UNLINK:
+ profile.getResults().addAll(link(delta, subjectKeys, attrUtil, true));
+ break;
++ case IGNORE:
++ profile.getResults().addAll(ignore(delta, attrUtil, 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, attrUtil));
+ }
+ }
+ } 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,
+ getAttributableUtil().getType().name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ event,
+ result,
+ before,
+ output,
+ input);
+
+ auditManager.audit(
+ AuditElements.EventCategoryType.SYNCHRONIZATION,
+ getAttributableUtil().getType().name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ event,
+ result,
+ before,
+ output,
+ input);
+ }
+}