Author: ilgrosso
Date: Mon Dec 24 09:56:52 2012
New Revision: 1425615
URL: http://svn.apache.org/viewvc?rev=1425615&view=rev
Log:
[SYNCOPE-26] Added sample (but working) LDAPMembershipSyncActions
Added:
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/LDAPMembershipSyncActions.java
(with props)
Modified:
syncope/trunk/archetype/src/main/resources/archetype-resources/core/src/test/resources/content.xml
syncope/trunk/build-tools/src/main/resources/content.ldif
syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncActions.java
syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java
syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/LDAPMembershipPropagationActions.java
syncope/trunk/core/src/main/java/org/apache/syncope/core/quartz/SampleJob.java
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncJob.java
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncopeSyncResultHandler.java
syncope/trunk/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/SyncopeUserQueryImpl.java
syncope/trunk/core/src/test/java/org/apache/syncope/core/quartz/TestSyncActions.java
syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
syncope/trunk/core/src/test/resources/content.xml
Modified:
syncope/trunk/archetype/src/main/resources/archetype-resources/core/src/test/resources/content.xml
URL:
http://svn.apache.org/viewvc/syncope/trunk/archetype/src/main/resources/archetype-resources/core/src/test/resources/content.xml?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/archetype/src/main/resources/archetype-resources/core/src/test/resources/content.xml
(original)
+++
syncope/trunk/archetype/src/main/resources/archetype-resources/core/src/test/resources/content.xml
Mon Dec 24 09:56:52 2012
@@ -542,7 +542,7 @@ under the License.
<UMapping id="11" resource_name="resource-ldap"
accountlink="'uid=' + username +
',ou=people,o=isp'"/>
<UMappingItem id="311" accountid="1" password="0" mapping_id="11"
- extAttrName="__NAME__" intAttrName="Username"
intMappingType="Username"
+ extAttrName="__UID__" intAttrName="Username"
intMappingType="Username"
mandatoryCondition="true"/>
<UMappingItem id="312" accountid="0" password="1" mapping_id="11"
extAttrName="__PASSWORD__" intAttrName="Password"
intMappingType="Password"
@@ -551,7 +551,7 @@ under the License.
extAttrName="sn" intAttrName="surname"
intMappingType="UserSchema"
mandatoryCondition="true"/>
<UMappingItem id="314" accountid="0" password="0" mapping_id="11"
- extAttrName="cn" intAttrName="firstname"
intMappingType="UserSchema"
+ extAttrName="cn" intAttrName="fullname"
intMappingType="UserSchema"
mandatoryCondition="true"/>
<UMappingItem id="315" accountid="0" password="0" mapping_id="11"
extAttrName="mail" intAttrName="email"
intMappingType="UserSchema"
@@ -562,6 +562,9 @@ under the License.
<UMappingItem id="317" accountid="0" password="0" mapping_id="11"
extAttrName="postalAddress" intAttrName="postalAddress"
intMappingType="MembershipSchema"
mandatoryCondition="false"/>
+ <UMappingItem id="318" accountid="0" password="0" mapping_id="11"
+ extAttrName="mail" intAttrName="userId"
intMappingType="UserSchema"
+ mandatoryCondition="false"/>
<RMapping id="1" resource_name="resource-ldap"
accountlink="'cn=' + name +
',ou=groups,o=isp'"/>
<RMappingItem id="1" accountid="1" password="0" mapping_id="1"
@@ -626,8 +629,9 @@ under the License.
fullReconciliation="1" performCreate="1" performDelete="1"
performUpdate="1" syncStatus="0"
jobClassName="org.apache.syncope.core.sync.SyncJob"/>
<Task DTYPE="SyncTask" id="11" name="LDAP Sync Task"
resource_name="resource-ldap"
- fullReconciliation="1" performCreate="1" performDelete="1"
performUpdate="1" syncStatus="0"
- jobClassName="org.apache.syncope.core.sync.SyncJob"/>
+ fullReconciliation="1" performCreate="1" performDelete="1"
performUpdate="1" syncStatus="0"
+
actionsClassName="org.apache.syncope.core.sync.LDAPMembershipSyncActions"
+ jobClassName="org.apache.syncope.core.sync.SyncJob"/>
<NotificationTask_recipients notificationtask_id="8"
address="[email protected]"/>
Modified: syncope/trunk/build-tools/src/main/resources/content.ldif
URL:
http://svn.apache.org/viewvc/syncope/trunk/build-tools/src/main/resources/content.ldif?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
--- syncope/trunk/build-tools/src/main/resources/content.ldif (original)
+++ syncope/trunk/build-tools/src/main/resources/content.ldif Mon Dec 24
09:56:52 2012
@@ -29,3 +29,16 @@ objectClass: groupOfUniqueNames
objectClass: top
cn: testLDAPGroup
uniqueMember: uid=admin,ou=system
+uniqueMember: uid=syncFromLDAP,ou=People,o=isp
+
+DN: uid=syncFromLDAP,ou=People,o=isp
+objectClass: organizationalPerson
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: top
+cn: Sync from LDAP
+description: Active
+mail: [email protected]
+sn: Surname
+uid: syncFromLDAP
+userpassword:: cGFzc3dvcmQxMjM=
Modified:
syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncActions.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncActions.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncActions.java
(original)
+++
syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncActions.java
Mon Dec 24 09:56:52 2012
@@ -21,6 +21,7 @@ package org.apache.syncope.core.persiste
import java.util.List;
import org.apache.syncope.client.mod.AbstractAttributableMod;
import org.apache.syncope.client.to.AbstractAttributableTO;
+import org.apache.syncope.core.sync.SyncopeSyncResultHandler;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.quartz.JobExecutionException;
@@ -32,62 +33,67 @@ public interface SyncActions {
/**
* Action to be executed before to start the synchronization task
execution.
*
- * @param task synchronization task to be executed.
+ * @param handler synchronization handler being executed.
* @throws JobExecutionException in case of generic failure.
*/
- void beforeAll(final SyncTask task) throws JobExecutionException;
+ void beforeAll(final SyncopeSyncResultHandler handler) throws
JobExecutionException;
/**
* Action to be executed before to create a synchronized user locally.
*
+ * @param handler synchronization handler being executed.
* @param delta retrieved synchronization information
* @param subject user / role to be created
* @return synchronization information used for user status evaluation and
to be passed to the 'after' method.
* @throws JobExecutionException in case of generic failure
*/
- <T extends AbstractAttributableTO> SyncDelta beforeCreate(final SyncDelta
delta, final T subject)
- throws JobExecutionException;
+ <T extends AbstractAttributableTO> SyncDelta beforeCreate(final
SyncopeSyncResultHandler handler,
+ final SyncDelta delta, final T subject) throws
JobExecutionException;
/**
* Action to be executed before to update a synchronized user locally.
*
+ * @param handler synchronization handler being executed.
* @param delta retrieved synchronization information
* @param subject local user / role information
* @param subjectMod modification
* @return synchronization information used for logging and to be passed
to the 'after' method.
* @throws JobExecutionException in case of generic failure.
*/
- <T extends AbstractAttributableTO, K extends AbstractAttributableMod>
SyncDelta beforeUpdate(final SyncDelta delta,
- final T subject, final K subjectMod) throws JobExecutionException;
+ <T extends AbstractAttributableTO, K extends AbstractAttributableMod>
SyncDelta beforeUpdate(
+ final SyncopeSyncResultHandler handler, final SyncDelta delta,
final T subject, final K subjectMod)
+ throws JobExecutionException;
/**
* Action to be executed before to delete a synchronized user locally.
*
+ * @param handler synchronization handler being executed.
* @param delta retrieved synchronization information
* @param subject lcao user / role to be deleted
* @return synchronization information used for logging and to be passed
to the 'after' method.
* @throws JobExecutionException in case of generic failure
*/
- <T extends AbstractAttributableTO> SyncDelta beforeDelete(final SyncDelta
delta, final T subject)
- throws JobExecutionException;
+ <T extends AbstractAttributableTO> SyncDelta beforeDelete(final
SyncopeSyncResultHandler handler,
+ final SyncDelta delta, final T subject) throws
JobExecutionException;
/**
* Action to be executed after each local user synchronization.
*
+ * @param handler synchronization handler being executed.
* @param delta retrieved synchronization information (may be modified by
'beforeCreate/beforeUpdate/beforeDelete')
* @param subject synchronized local user / role
* @param result global synchronization results at the current
synchronization step
* @throws JobExecutionException in case of generic failure
*/
- <T extends AbstractAttributableTO> void after(final SyncDelta delta, final
T subject, final SyncResult result)
- throws JobExecutionException;
+ <T extends AbstractAttributableTO> void after(final
SyncopeSyncResultHandler handler, final SyncDelta delta,
+ final T subject, final SyncResult result) throws
JobExecutionException;
/**
* Action to be executed after the synchronization task completion.
*
- * @param task executed synchronization task
+ * @param handler synchronization handler being executed.
* @param results synchronization result
* @throws JobExecutionException in case of generic failure
*/
- void afterAll(final SyncTask task, final List<SyncResult> results) throws
JobExecutionException;
+ void afterAll(final SyncopeSyncResultHandler handler, final
List<SyncResult> results) throws JobExecutionException;
}
Modified:
syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java
(original)
+++
syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/ConnectorFacadeProxy.java
Mon Dec 24 09:56:52 2012
@@ -20,6 +20,7 @@ package org.apache.syncope.core.propagat
import java.io.File;
import java.net.URI;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -57,6 +58,7 @@ import org.identityconnectors.framework.
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.identityconnectors.framework.common.objects.SyncToken;
import org.identityconnectors.framework.common.objects.Uid;
+import org.identityconnectors.framework.common.objects.filter.Filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;
@@ -352,6 +354,27 @@ public class ConnectorFacadeProxy {
return result;
}
+ public List<ConnectorObject> search(final ObjectClass objectClass, final
Filter filter,
+ final OperationOptions options) {
+
+ final List<ConnectorObject> result = new ArrayList<ConnectorObject>();
+
+ if
(activeConnInstance.getCapabilities().contains(ConnectorCapability.SEARCH)) {
+ connector.search(objectClass, filter, new ResultsHandler() {
+
+ @Override
+ public boolean handle(final ConnectorObject obj) {
+ return result.add(obj);
+ }
+ }, options);
+ } else {
+ LOG.info("Search was attempted, although the connector only has
these capabilities: {}. No action.",
+ activeConnInstance.getCapabilities());
+ }
+
+ return result;
+ }
+
/**
* Get remote object used by the propagation manager in order to choose
for a create (object doesn't exist) or an
* update (object exists).
Modified:
syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/LDAPMembershipPropagationActions.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/LDAPMembershipPropagationActions.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/LDAPMembershipPropagationActions.java
(original)
+++
syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/LDAPMembershipPropagationActions.java
Mon Dec 24 09:56:52 2012
@@ -43,21 +43,27 @@ import org.springframework.transaction.a
/**
* Simple action for propagating role memberships to LDAP groups, when the
same resource is configured for both users
* and roles.
+ *
+ * @see org.apache.syncope.core.sync.LDAPMembershipSyncActions
*/
public class LDAPMembershipPropagationActions extends
DefaultPropagationActions {
- private static final Logger LOG =
LoggerFactory.getLogger(LDAPMembershipPropagationActions.class);
-
- /**
- * Allows easy subclassing for the ConnId AD connector bundle.
- */
- protected static final String GROUP_MEMBERSHIP_ATTR = "ldapGroups";
+ protected static final Logger LOG =
LoggerFactory.getLogger(LDAPMembershipPropagationActions.class);
@Autowired
- private UserDAO userDAO;
+ protected UserDAO userDAO;
@Autowired
- private JexlUtil jexlUtil;
+ protected JexlUtil jexlUtil;
+
+ /**
+ * Allows easy subclassing for the ConnId AD connector bundle.
+ *
+ * @return the name of the attribute used to keep track of group
memberships
+ */
+ protected String getGroupMembershipAttrName() {
+ return "ldapGroups";
+ }
@Transactional(readOnly = true)
@Override
@@ -95,7 +101,7 @@ public class LDAPMembershipPropagationAc
if (!roleAccountLinks.isEmpty()) {
Set<Attribute> attributes = new
HashSet<Attribute>(task.getAttributes());
- attributes.add(AttributeBuilder.build(GROUP_MEMBERSHIP_ATTR,
roleAccountLinks));
+
attributes.add(AttributeBuilder.build(getGroupMembershipAttrName(),
roleAccountLinks));
task.setAttributes(attributes);
}
} else {
Modified:
syncope/trunk/core/src/main/java/org/apache/syncope/core/quartz/SampleJob.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/quartz/SampleJob.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/main/java/org/apache/syncope/core/quartz/SampleJob.java
(original)
+++
syncope/trunk/core/src/main/java/org/apache/syncope/core/quartz/SampleJob.java
Mon Dec 24 09:56:52 2012
@@ -18,9 +18,9 @@
*/
package org.apache.syncope.core.quartz;
-import org.quartz.JobExecutionException;
import org.apache.syncope.core.persistence.beans.SchedTask;
import org.apache.syncope.core.persistence.beans.TaskExec;
+import org.quartz.JobExecutionException;
/**
* Sample implementation for execution a scheduled task.
Modified:
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java
(original)
+++
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/DefaultSyncActions.java
Mon Dec 24 09:56:52 2012
@@ -23,7 +23,6 @@ import org.apache.syncope.client.mod.Abs
import org.apache.syncope.client.to.AbstractAttributableTO;
import org.apache.syncope.core.persistence.beans.SyncActions;
import org.apache.syncope.core.persistence.beans.SyncResult;
-import org.apache.syncope.core.persistence.beans.SyncTask;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.quartz.JobExecutionException;
@@ -33,36 +32,38 @@ import org.quartz.JobExecutionException;
public class DefaultSyncActions implements SyncActions {
@Override
- public void beforeAll(final SyncTask task) throws JobExecutionException {
+ public void beforeAll(final SyncopeSyncResultHandler handler) throws
JobExecutionException {
}
@Override
- public <T extends AbstractAttributableTO> SyncDelta beforeCreate(final
SyncDelta delta, final T subject)
- throws JobExecutionException {
+ public <T extends AbstractAttributableTO> SyncDelta beforeCreate(final
SyncopeSyncResultHandler handler,
+ final SyncDelta delta, final T subject) throws
JobExecutionException {
return delta;
}
@Override
public <T extends AbstractAttributableTO, K extends
AbstractAttributableMod> SyncDelta beforeUpdate(
- final SyncDelta delta, final T subject, final K subjectMod) throws
JobExecutionException {
+ final SyncopeSyncResultHandler handler, final SyncDelta delta,
final T subject, final K subjectMod)
+ throws JobExecutionException {
return delta;
}
@Override
- public <T extends AbstractAttributableTO> SyncDelta beforeDelete(final
SyncDelta delta, final T subject)
- throws JobExecutionException {
+ public <T extends AbstractAttributableTO> SyncDelta beforeDelete(
+ final SyncopeSyncResultHandler handler, final SyncDelta delta,
final T subject) throws JobExecutionException {
return delta;
}
@Override
- public <T extends AbstractAttributableTO> void after(final SyncDelta
delta, final T subject,
- final SyncResult result) throws JobExecutionException {
+ public <T extends AbstractAttributableTO> void after(final
SyncopeSyncResultHandler handler,
+ final SyncDelta delta, final T subject, final SyncResult result)
throws JobExecutionException {
}
@Override
- public void afterAll(final SyncTask task, final List<SyncResult> results)
throws JobExecutionException {
+ public void afterAll(final SyncopeSyncResultHandler handler, final
List<SyncResult> results)
+ throws JobExecutionException {
}
}
Added:
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/LDAPMembershipSyncActions.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/LDAPMembershipSyncActions.java?rev=1425615&view=auto
==============================================================================
---
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/LDAPMembershipSyncActions.java
(added)
+++
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/LDAPMembershipSyncActions.java
Mon Dec 24 09:56:52 2012
@@ -0,0 +1,282 @@
+/*
+ * 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.sync;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.client.mod.AbstractAttributableMod;
+import org.apache.syncope.client.mod.MembershipMod;
+import org.apache.syncope.client.mod.UserMod;
+import org.apache.syncope.client.to.AbstractAttributableTO;
+import org.apache.syncope.client.to.RoleTO;
+import org.apache.syncope.core.notification.NotificationManager;
+import org.apache.syncope.core.persistence.beans.ExternalResource;
+import org.apache.syncope.core.persistence.beans.PropagationTask;
+import org.apache.syncope.core.persistence.beans.SyncResult;
+import org.apache.syncope.core.persistence.beans.SyncTask;
+import org.apache.syncope.core.persistence.beans.membership.Membership;
+import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
+import org.apache.syncope.core.persistence.dao.RoleDAO;
+import org.apache.syncope.core.propagation.ConnectorFacadeProxy;
+import org.apache.syncope.core.propagation.ConnectorFactory;
+import org.apache.syncope.core.propagation.PropagationException;
+import org.apache.syncope.core.propagation.PropagationManager;
+import org.apache.syncope.core.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.util.AttributableUtil;
+import org.apache.syncope.core.workflow.WorkflowResult;
+import org.apache.syncope.core.workflow.user.UserWorkflowAdapter;
+import org.apache.syncope.types.AttributableType;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Simple action for synchronizing LDAP groups memberships to Syncope role
memberships, when the same resource is
+ * configured for both users and roles.
+ *
+ * @see org.apache.syncope.core.propagation.LDAPMembershipPropagationActions
+ */
+public class LDAPMembershipSyncActions extends DefaultSyncActions {
+
+ protected static final Logger LOG =
LoggerFactory.getLogger(LDAPMembershipSyncActions.class);
+
+ @Autowired
+ protected RoleDAO roleDAO;
+
+ @Autowired
+ protected ConnectorFactory connInstanceLoader;
+
+ @Autowired
+ protected UserWorkflowAdapter uwfAdapter;
+
+ @Autowired
+ protected PropagationManager propagationManager;
+
+ @Autowired
+ private PropagationTaskExecutor taskExecutor;
+
+ @Autowired
+ private NotificationManager notificationManager;
+
+ protected Map<Long, Long> membersBeforeRoleUpdate = Collections.<Long,
Long>emptyMap();
+
+ /**
+ * Allows easy subclassing for the ConnId AD connector bundle.
+ *
+ * @return the name of the attribute used to keep track of group
memberships
+ */
+ protected String getGroupMembershipAttrName() {
+ return "uniquemember";
+ }
+
+ /**
+ * Keep track of members of the role being updated <b>before</b> actual
update takes place. This is not needed on
+ * <ul> <li>beforeCreate() - because the synchronizing role does not exist
yet on Syncope</li> <li>beforeDelete() -
+ * because role delete cascades as membership removal for all users
involved</li> </ul>
+ */
+ @Transactional(readOnly = true)
+ @Override
+ public <T extends AbstractAttributableTO, K extends
AbstractAttributableMod> SyncDelta beforeUpdate(
+ final SyncopeSyncResultHandler handler, final SyncDelta delta,
final T subject, final K subjectMod)
+ throws JobExecutionException {
+
+ if (subject instanceof RoleTO) {
+ // search for all users assigned to given role
+ SyncopeRole role = roleDAO.find(subject.getId());
+ if (role != null) {
+ List<Membership> membs = roleDAO.findMemberships(role);
+ // save memberships before role update takes place
+ membersBeforeRoleUpdate = new HashMap<Long,
Long>(membs.size());
+ for (Membership memb : membs) {
+ membersBeforeRoleUpdate.put(memb.getSyncopeUser().getId(),
memb.getId());
+ }
+ }
+ }
+
+ return super.beforeUpdate(handler, delta, subject, subjectMod);
+ }
+
+ /**
+ * Build UserMod for adding membership to given user, for given role.
+ *
+ * @param userId user to be assigned membership to given role
+ * @param roleTO role for adding membership
+ * @return UserMod for user update
+ */
+ protected UserMod getUserMod(final Long userId, final RoleTO roleTO) {
+ UserMod userMod = new UserMod();
+ // no actual modification takes place when user has already the role
assigned
+ if (membersBeforeRoleUpdate.containsKey(userId)) {
+ membersBeforeRoleUpdate.remove(userId);
+ } else {
+ userMod.setId(userId);
+
+ MembershipMod membershipMod = new MembershipMod();
+ membershipMod.setRole(roleTO.getId());
+ userMod.addMembershipToBeAdded(membershipMod);
+ }
+
+ return userMod;
+ }
+
+ /**
+ * Read values of attribute returned by getGroupMembershipAttrName(); if
not present in the given delta, perform an
+ * additioanl read on the underlying connector.
+ *
+ * @param delta representing the synchronizing role
+ * @param connector associated to the current resource
+ * @return value of attribute returned by getGroupMembershipAttrName()
+ * @see getGroupMembershipAttrName()
+ */
+ protected List<Object> getMembAttrValues(final SyncDelta delta, final
ConnectorFacadeProxy connector) {
+ List<Object> result = Collections.<Object>emptyList();
+
+ // first, try to read the configured attribute from delta, returned by
the ongoing synchronization
+ Attribute membAttr =
delta.getObject().getAttributeByName(getGroupMembershipAttrName());
+ // if not found, perform an additional read on the underlying
connector for the same connector object
+ if (membAttr == null) {
+ final OperationOptionsBuilder oob = new OperationOptionsBuilder();
+ oob.setAttributesToGet(getGroupMembershipAttrName());
+ membAttr = connector.getObjectAttribute(
+ ObjectClass.GROUP, delta.getUid(), oob.build(),
getGroupMembershipAttrName());
+ }
+ if (membAttr != null && membAttr.getValue() != null) {
+ result = membAttr.getValue();
+ }
+
+ return result;
+ }
+
+ /**
+ * Perform actual modifications (i.e. membership add / remove) for the
given role on the given resource.
+ *
+ * @param userMod modifications to perform on the user
+ * @param resourceName resource to be propagated for changes
+ */
+ protected void userUpdate(final UserMod userMod, final String
resourceName) {
+ if (userMod.getId() == 0) {
+ return;
+ }
+
+ try {
+ WorkflowResult<Map.Entry<Long, Boolean>> updated =
uwfAdapter.update(userMod);
+
+ List<PropagationTask> tasks =
propagationManager.getUserUpdateTaskIds(updated,
+ userMod.getPassword(),
userMod.getVirtualAttributesToBeRemoved(),
+ userMod.getVirtualAttributesToBeUpdated(),
+ Collections.singleton(resourceName));
+
+ taskExecutor.execute(tasks);
+
+ notificationManager.createTasks(updated.getResult().getKey(),
updated.getPerformedTasks());
+ } catch (PropagationException e) {
+ LOG.error("Could not propagate {}", userMod, e);
+ } catch (Exception e) {
+ LOG.error("Could not perform update {}", userMod, e);
+ }
+ }
+
+ /**
+ * Synchronize Syncope memberships with the situation read on the external
resource's group.
+ *
+ * @param handler syncope sync result handler
+ * @param delta representing the synchronizing role
+ * @param roleTO role after modification performed by the handler
+ * @throws JobExecutionException if anything goes wrong
+ */
+ protected void synchronizeMemberships(final SyncopeSyncResultHandler
handler, final SyncDelta delta,
+ final RoleTO roleTO) throws JobExecutionException {
+
+ final SyncTask task = handler.getSyncTask();
+ final ExternalResource resource = task.getResource();
+
+ ConnectorFacadeProxy connector;
+ try {
+ connector = connInstanceLoader.getConnector(resource);
+ } catch (Exception e) {
+ final String msg = String.format("Connector instance bean for
resource %s and connInstance %s not found",
+ resource, resource.getConnector());
+
+ throw new JobExecutionException(msg, e);
+ }
+
+ for (Object membValue : getMembAttrValues(delta, connector)) {
+
+ final List<ConnectorObject> found =
connector.search(ObjectClass.ACCOUNT,
+ new EqualsFilter(new Name(membValue.toString())),
+
connector.getOperationOptions(resource.getUmapping().getItems()));
+
+ if (found.isEmpty()) {
+ LOG.debug("No account found on {} with __NAME__ {}", resource,
membValue.toString());
+ } else {
+ if (found.size() > 1) {
+ LOG.warn("More than one account found on {} with __NAME__
{} - taking first only",
+ resource, membValue.toString());
+ }
+
+ ConnectorObject externalAccount = found.iterator().next();
+ final List<Long> userIds =
handler.findExisting(externalAccount.getUid().getUidValue(),
+ externalAccount,
AttributableUtil.getInstance(AttributableType.USER));
+ if (userIds.isEmpty()) {
+ LOG.debug("No matching user found for {}, aborting",
externalAccount);
+ } else {
+ if (userIds.size() > 1) {
+ LOG.warn("More than one user found {} - taking first
only", userIds);
+ }
+
+ UserMod userMod = getUserMod(userIds.iterator().next(),
roleTO);
+ userUpdate(userMod, resource.getName());
+ }
+ }
+ }
+
+ // finally remove any residual membership that was present before role
update but not any more
+ for (Map.Entry<Long, Long> member :
membersBeforeRoleUpdate.entrySet()) {
+ UserMod userMod = new UserMod();
+ userMod.setId(member.getKey());
+ userMod.addMembershipToBeRemoved(member.getValue());
+ userUpdate(userMod, resource.getName());
+ }
+ }
+
+ /**
+ * Synchronize membership at role synchronization time (because SyncJob
first synchronize users then roles).
+ */
+ @Override
+ public <T extends AbstractAttributableTO> void after(final
SyncopeSyncResultHandler handler, final SyncDelta delta,
+ final T subject, final SyncResult result) throws
JobExecutionException {
+
+ if (!(subject instanceof RoleTO) ||
handler.getSyncTask().getResource().getUmapping() == null) {
+ super.after(handler, delta, subject, result);
+ } else {
+ synchronizeMemberships(handler, delta, (RoleTO) subject);
+ }
+ }
+}
Propchange:
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/LDAPMembershipSyncActions.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/LDAPMembershipSyncActions.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange:
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/LDAPMembershipSyncActions.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified:
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncJob.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncJob.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncJob.java
(original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncJob.java
Mon Dec 24 09:56:52 2012
@@ -355,7 +355,7 @@ public class SyncJob extends AbstractTas
handler.setResults(results);
handler.setSyncTask(syncTask);
- actions.beforeAll(syncTask);
+ actions.beforeAll(handler);
try {
if (syncTask.isFullReconciliation()) {
if (uMapping != null) {
@@ -390,7 +390,7 @@ public class SyncJob extends AbstractTas
} catch (Exception e) {
throw new JobExecutionException("While syncing on connector", e);
}
- actions.afterAll(syncTask, results);
+ actions.afterAll(handler, results);
final String result = createReport(results,
syncTask.getResource().getSyncTraceLevel(), dryRun);
Modified:
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncopeSyncResultHandler.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncopeSyncResultHandler.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncopeSyncResultHandler.java
(original)
+++
syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncopeSyncResultHandler.java
Mon Dec 24 09:56:52 2012
@@ -70,6 +70,7 @@ import org.apache.syncope.types.Resource
import org.apache.syncope.types.SyncPolicySpec;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.OperationalAttributes;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.identityconnectors.framework.common.objects.SyncDeltaType;
@@ -177,22 +178,42 @@ public class SyncopeSyncResultHandler im
private boolean dryRun;
+ public SyncActions getActions() {
+ return actions;
+ }
+
public void setActions(final SyncActions actions) {
this.actions = actions;
}
+ public Collection<SyncResult> getResults() {
+ return results;
+ }
+
public void setResults(final Collection<SyncResult> results) {
this.results = results;
}
+ public SyncTask getSyncTask() {
+ return syncTask;
+ }
+
public void setSyncTask(final SyncTask syncTask) {
this.syncTask = syncTask;
}
+ public ConflictResolutionAction getResAct() {
+ return resAct;
+ }
+
public void setResAct(final ConflictResolutionAction resAct) {
this.resAct = resAct;
}
+ public boolean isDryRun() {
+ return dryRun;
+ }
+
public void setDryRun(final boolean dryRun) {
this.dryRun = dryRun;
}
@@ -283,7 +304,7 @@ public class SyncopeSyncResultHandler im
return result;
}
- private List<Long> findByAttributableSearch(final SyncDelta delta, final
SyncPolicySpec policySpec,
+ private List<Long> findByAttributableSearch(final ConnectorObject connObj,
final SyncPolicySpec policySpec,
final AttributableUtil attrUtil) {
final List<Long> result = new ArrayList<Long>();
@@ -293,7 +314,7 @@ public class SyncopeSyncResultHandler im
final Map<String, Attribute> extValues = new HashMap<String,
Attribute>();
for (AbstractMappingItem item :
attrUtil.getMappingItems(syncTask.getResource())) {
- extValues.put(item.getIntAttrName(),
delta.getObject().getAttributeByName(item.getExtAttrName()));
+ extValues.put(item.getIntAttrName(),
connObj.getAttributeByName(item.getExtAttrName()));
}
// search for user/role by attribute(s) specified in the policy
@@ -352,15 +373,12 @@ public class SyncopeSyncResultHandler im
/**
* Find users / roles based on mapped uid value (or previous uid value, if
updated).
*
- * @param delta sync delta
+ * @param uid for finding by account id
+ * @param connObj for finding by attribute value
* @param attrUtil attributable util
* @return list of matching users / roles
*/
- protected List<Long> findExisting(final SyncDelta delta, final
AttributableUtil attrUtil) {
- final String uid = delta.getPreviousUid() == null
- ? delta.getUid().getUidValue()
- : delta.getPreviousUid().getUidValue();
-
+ public List<Long> findExisting(final String uid, final ConnectorObject
connObj, final AttributableUtil attrUtil) {
SyncPolicySpec policySpec = null;
if (syncTask.getResource().getSyncPolicy() != null) {
policySpec = (SyncPolicySpec)
syncTask.getResource().getSyncPolicy().getSpecification();
@@ -368,7 +386,7 @@ public class SyncopeSyncResultHandler im
return policySpec == null ||
attrUtil.getAltSearchSchemas(policySpec).isEmpty()
? findByAccountIdItem(uid, attrUtil)
- : findByAttributableSearch(delta, policySpec, attrUtil);
+ : findByAttributableSearch(connObj, policySpec, attrUtil);
}
protected List<SyncResult> create(SyncDelta delta, final AttributableUtil
attrUtil,
@@ -385,7 +403,7 @@ public class SyncopeSyncResultHandler im
AbstractAttributableTO subjectTO =
connObjectUtil.getAttributableTO(delta.getObject(), syncTask, attrUtil);
- delta = actions.beforeCreate(delta, subjectTO);
+ delta = actions.beforeCreate(this, delta, subjectTO);
if (dryRun) {
result.setId(0L);
@@ -458,7 +476,7 @@ public class SyncopeSyncResultHandler im
}
}
- actions.after(delta, subjectTO, result);
+ actions.after(this, delta, subjectTO, result);
return Collections.singletonList(result);
}
@@ -486,7 +504,7 @@ public class SyncopeSyncResultHandler im
try {
final AbstractAttributableMod mod =
connObjectUtil.getAttributableMod(
id, delta.getObject(), subjectTO, syncTask,
attrUtil);
- delta = actions.beforeUpdate(delta, subjectTO, mod);
+ delta = actions.beforeUpdate(this, delta, subjectTO, mod);
result.setStatus(SyncResult.Status.SUCCESS);
result.setId(mod.getId());
@@ -532,7 +550,7 @@ public class SyncopeSyncResultHandler im
LOG.error("Could not update {} {}", attrUtil.getType(),
delta.getUid().getUidValue(), e);
}
- actions.after(delta, subjectTO, result);
+ actions.after(this, delta, subjectTO, result);
updResults.add(result);
} catch (NotFoundException e) {
LOG.error("Could not find {} {}", attrUtil.getType(), id, e);
@@ -561,7 +579,7 @@ public class SyncopeSyncResultHandler im
AbstractAttributableTO subjectTO = AttributableType.USER ==
attrUtil.getType()
? userDataBinder.getUserTO(id)
: roleDataBinder.getRoleTO(id);
- delta = actions.beforeDelete(delta, subjectTO);
+ delta = actions.beforeDelete(this, delta, subjectTO);
final SyncResult result = new SyncResult();
result.setId(id);
@@ -604,7 +622,7 @@ public class SyncopeSyncResultHandler im
}
}
- actions.after(delta, subjectTO, result);
+ actions.after(this, delta, subjectTO, result);
delResults.add(result);
} catch (NotFoundException e) {
LOG.error("Could not find {} {}", attrUtil.getType(), id, e);
@@ -631,7 +649,10 @@ public class SyncopeSyncResultHandler im
AttributableUtil attrUtil =
AttributableUtil.getInstance(delta.getObject().getObjectClass());
- final List<Long> subjects = findExisting(delta, attrUtil);
+ final String uid = delta.getPreviousUid() == null
+ ? delta.getUid().getUidValue()
+ : delta.getPreviousUid().getUidValue();
+ final List<Long> subjects = findExisting(uid, delta.getObject(),
attrUtil);
if (SyncDeltaType.CREATE_OR_UPDATE == delta.getDeltaType()) {
if (subjects.isEmpty()) {
Modified:
syncope/trunk/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/SyncopeUserQueryImpl.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/SyncopeUserQueryImpl.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/SyncopeUserQueryImpl.java
(original)
+++
syncope/trunk/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/SyncopeUserQueryImpl.java
Mon Dec 24 09:56:52 2012
@@ -48,7 +48,6 @@ public class SyncopeUserQueryImpl implem
private List<User> result;
public SyncopeUserQueryImpl(final UserDAO userDAO, final RoleDAO roleDAO,
final EntitlementDAO entitlementDAO) {
-
this.userDAO = userDAO;
this.roleDAO = roleDAO;
this.entitlementDAO = entitlementDAO;
@@ -133,20 +132,22 @@ public class SyncopeUserQueryImpl implem
return new UserEntity(syncopeUser.getUsername());
}
- private void execute(int page, int itemsPerPage) {
+ private void execute(final int page, final int itemsPerPage) {
if (username != null) {
SyncopeUser user = userDAO.find(username);
- if (user != null) {
+ if (user == null) {
+ result = Collections.<User>emptyList();
+ } else {
if (memberOf == null || user.getRoleIds().contains(memberOf)) {
result = Collections.singletonList(fromSyncopeUser(user));
}
- } else {
- result = Collections.emptyList();
}
}
if (memberOf != null) {
SyncopeRole role = roleDAO.find(memberOf);
- if (role != null) {
+ if (role == null) {
+ result = Collections.<User>emptyList();
+ } else {
result = new ArrayList<User>();
List<Membership> memberships = roleDAO.findMemberships(role);
User user;
@@ -156,8 +157,6 @@ public class SyncopeUserQueryImpl implem
result.add(user);
}
}
- } else {
- result = Collections.emptyList();
}
}
// THIS CAN BE *VERY* DANGEROUS
@@ -210,7 +209,7 @@ public class SyncopeUserQueryImpl implem
}
@Override
- public UserQuery potentialStarter(String string) {
+ public UserQuery potentialStarter(final String string) {
throw new UnsupportedOperationException();
}
}
Modified:
syncope/trunk/core/src/test/java/org/apache/syncope/core/quartz/TestSyncActions.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/quartz/TestSyncActions.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/test/java/org/apache/syncope/core/quartz/TestSyncActions.java
(original)
+++
syncope/trunk/core/src/test/java/org/apache/syncope/core/quartz/TestSyncActions.java
Mon Dec 24 09:56:52 2012
@@ -24,6 +24,7 @@ import org.apache.syncope.client.mod.Att
import org.apache.syncope.client.to.AbstractAttributableTO;
import org.apache.syncope.client.to.AttributeTO;
import org.apache.syncope.core.sync.DefaultSyncActions;
+import org.apache.syncope.core.sync.SyncopeSyncResultHandler;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.quartz.JobExecutionException;
@@ -32,8 +33,8 @@ public class TestSyncActions extends Def
private int counter = 0;
@Override
- public <T extends AbstractAttributableTO> SyncDelta beforeCreate(final
SyncDelta delta, final T subject)
- throws JobExecutionException {
+ public <T extends AbstractAttributableTO> SyncDelta beforeCreate(final
SyncopeSyncResultHandler handler,
+ final SyncDelta delta, final T subject) throws
JobExecutionException {
AttributeTO attrTO = null;
for (int i = 0; i < subject.getAttributes().size(); i++) {
@@ -53,7 +54,8 @@ public class TestSyncActions extends Def
@Override
public <T extends AbstractAttributableTO, K extends
AbstractAttributableMod> SyncDelta beforeUpdate(
- final SyncDelta delta, final T subject, final K subjectMod) throws
JobExecutionException {
+ final SyncopeSyncResultHandler handler, final SyncDelta delta,
final T subject, final K subjectMod)
+ throws JobExecutionException {
subjectMod.addAttributeToBeRemoved("fullname");
Modified:
syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
---
syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
(original)
+++
syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
Mon Dec 24 09:56:52 2012
@@ -362,10 +362,12 @@ public class TaskTestITCase extends Abst
TaskExecTO execution = execTask(SyncTaskTO.class, actual.getId(), 20,
false);
+ // 1. verify execution status
final String status = execution.getStatus();
assertNotNull(status);
assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
+ // 2. verify that synchronized role is found, with expected attributes
final AttributableCond rolenameLeafCond = new
AttributableCond(AttributableCond.Type.EQ);
rolenameLeafCond.setSchema("name");
rolenameLeafCond.setExpression("testLDAPGroup");
@@ -379,6 +381,14 @@ public class TaskTestITCase extends Abst
assertEquals("testLDAPGroup", roleTO.getName());
assertEquals(8L, roleTO.getParent());
assertEquals("true",
roleTO.getAttributeMap().get("show").getValues().get(0));
+
+ // 3. verify that LDAP group membership is propagated as Syncope role
membership
+ final MembershipCond membershipCond = new MembershipCond();
+ membershipCond.setRoleId(roleTO.getId());
+ final List<UserTO> members =
Arrays.asList(restTemplate.postForObject(BASE_URL + "user/search",
+ NodeCond.getLeafCond(membershipCond), UserTO[].class));
+ assertNotNull(members);
+ assertEquals(1, members.size());
}
@Test
Modified: syncope/trunk/core/src/test/resources/content.xml
URL:
http://svn.apache.org/viewvc/syncope/trunk/core/src/test/resources/content.xml?rev=1425615&r1=1425614&r2=1425615&view=diff
==============================================================================
--- syncope/trunk/core/src/test/resources/content.xml (original)
+++ syncope/trunk/core/src/test/resources/content.xml Mon Dec 24 09:56:52 2012
@@ -542,7 +542,7 @@ under the License.
<UMapping id="11" resource_name="resource-ldap"
accountlink="'uid=' + username +
',ou=people,o=isp'"/>
<UMappingItem id="311" accountid="1" password="0" mapping_id="11"
- extAttrName="__NAME__" intAttrName="Username"
intMappingType="Username"
+ extAttrName="__UID__" intAttrName="Username"
intMappingType="Username"
mandatoryCondition="true"/>
<UMappingItem id="312" accountid="0" password="1" mapping_id="11"
extAttrName="__PASSWORD__" intAttrName="Password"
intMappingType="Password"
@@ -551,7 +551,7 @@ under the License.
extAttrName="sn" intAttrName="surname"
intMappingType="UserSchema"
mandatoryCondition="true"/>
<UMappingItem id="314" accountid="0" password="0" mapping_id="11"
- extAttrName="cn" intAttrName="firstname"
intMappingType="UserSchema"
+ extAttrName="cn" intAttrName="fullname"
intMappingType="UserSchema"
mandatoryCondition="true"/>
<UMappingItem id="315" accountid="0" password="0" mapping_id="11"
extAttrName="mail" intAttrName="email"
intMappingType="UserSchema"
@@ -562,6 +562,9 @@ under the License.
<UMappingItem id="317" accountid="0" password="0" mapping_id="11"
extAttrName="postalAddress" intAttrName="postalAddress"
intMappingType="MembershipSchema"
mandatoryCondition="false"/>
+ <UMappingItem id="318" accountid="0" password="0" mapping_id="11"
+ extAttrName="mail" intAttrName="userId"
intMappingType="UserSchema"
+ mandatoryCondition="false"/>
<RMapping id="1" resource_name="resource-ldap"
accountlink="'cn=' + name +
',ou=groups,o=isp'"/>
<RMappingItem id="1" accountid="1" password="0" mapping_id="1"
@@ -626,8 +629,9 @@ under the License.
fullReconciliation="1" performCreate="1" performDelete="1"
performUpdate="1" syncStatus="0"
jobClassName="org.apache.syncope.core.sync.SyncJob"/>
<Task DTYPE="SyncTask" id="11" name="LDAP Sync Task"
resource_name="resource-ldap"
- fullReconciliation="1" performCreate="1" performDelete="1"
performUpdate="1" syncStatus="0"
- jobClassName="org.apache.syncope.core.sync.SyncJob"/>
+ fullReconciliation="1" performCreate="1" performDelete="1"
performUpdate="1" syncStatus="0"
+
actionsClassName="org.apache.syncope.core.sync.LDAPMembershipSyncActions"
+ jobClassName="org.apache.syncope.core.sync.SyncJob"/>
<NotificationTask_recipients notificationtask_id="8"
address="[email protected]"/>