This is an automated email from the ASF dual-hosted git repository. andreapatricelli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push: new bdeaaf4 [SYNCOPE-1656] remediation now is generated also on pull update failure (#303) bdeaaf4 is described below commit bdeaaf4660a7b9b3fbecf20f41c9db20cca6aed0 Author: Andrea Patricelli <andreapatrice...@apache.org> AuthorDate: Mon Jan 17 09:31:32 2022 +0100 [SYNCOPE-1656] remediation now is generated also on pull update failure (#303) * [SYNCOPE-1656] remediation now is generated also on pull update failure, made some refactoring --- .../java/pushpull/AbstractPullResultHandler.java | 103 +++++++++++------ .../pushpull/DefaultUserPullResultHandler.java | 53 +++------ .../org/apache/syncope/fit/AbstractITCase.java | 32 ++++++ .../apache/syncope/fit/core/PullTaskITCase.java | 123 ++++++++++++++++++++- 4 files changed, 234 insertions(+), 77 deletions(-) diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java index ac41367..6c8de99 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java @@ -23,6 +23,7 @@ import java.util.Date; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.syncope.common.lib.AnyOperations; import org.apache.syncope.common.lib.request.AnyCR; @@ -35,10 +36,12 @@ import org.apache.syncope.common.lib.types.AuditElements.Result; import org.apache.syncope.common.lib.types.MatchType; import org.apache.syncope.common.lib.types.MatchingRule; import org.apache.syncope.common.lib.types.PatchOperation; +import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.provisioning.api.PropagationByResource; import org.apache.syncope.common.lib.types.PullMode; import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.common.lib.types.UnmatchingRule; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; import org.apache.syncope.core.persistence.api.dao.RemediationDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; @@ -91,6 +94,9 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan protected UserDAO userDAO; @Autowired + protected AnyTypeDAO anyTypeDAO; + + @Autowired protected TaskDAO taskDAO; @Autowired @@ -254,18 +260,12 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan if (profile.getTask().isRemediation()) { // set to SUCCESS to let the incremental flow go on in case of errors resultStatus = Result.SUCCESS; - Remediation entity = entityFactory.newEntity(Remediation.class); - entity.setAnyType(provision.getAnyType()); - entity.setOperation(ResourceOperation.CREATE); - entity.setPayload(anyCR); - entity.setError(result.getMessage()); - entity.setInstant(new Date()); - entity.setRemoteName(delta.getObject().getName().getNameValue()); - if (taskDAO.find(profile.getTask().getKey()) != null) { - entity.setPullTask(profile.getTask()); - } - - remediationDAO.save(entity); + createRemediation( + provision.getAnyType(), + anyCR, + taskDAO.find(profile.getTask().getKey()) != null ? profile.getTask() : null, + result, + delta); } else { resultStatus = Result.FAILURE; } @@ -382,17 +382,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan if (profile.getTask().isRemediation()) { // set to SUCCESS to let the incremental flow go on in case of errors resultStatus = Result.SUCCESS; - - Remediation entity = entityFactory.newEntity(Remediation.class); - entity.setAnyType(provision.getAnyType()); - entity.setOperation(ResourceOperation.UPDATE); - entity.setPayload(anyUR); - entity.setError(result.getMessage()); - entity.setInstant(new Date()); - entity.setRemoteName(delta.getObject().getName().getNameValue()); - entity.setPullTask(profile.getTask()); - - remediationDAO.save(entity); + createRemediation(provision.getAnyType(), anyUR, profile.getTask(), result, delta); } else { resultStatus = Result.FAILURE; } @@ -689,16 +679,8 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan if (profile.getTask().isRemediation()) { // set to SUCCESS to let the incremental flow go on in case of errors resultStatus = Result.SUCCESS; - Remediation entity = entityFactory.newEntity(Remediation.class); - entity.setAnyType(provision.getAnyType()); - entity.setOperation(ResourceOperation.DELETE); - entity.setPayload(match.getAny().getKey()); - entity.setError(result.getMessage()); - entity.setInstant(new Date()); - entity.setRemoteName(delta.getObject().getName().getNameValue()); - entity.setPullTask(profile.getTask()); - - remediationDAO.save(entity); + createRemediation( + provision.getAnyType(), match.getAny().getKey(), profile.getTask(), result, delta); } } @@ -955,4 +937,59 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan delta, furtherInput); } + + protected Remediation createRemediation( + final AnyType anyType, + final String anyKey, + final PullTask pullTask, + final ProvisioningReport result, + final SyncDelta delta) { + return createRemediation(anyType, anyKey, null, null, pullTask, result, delta); + } + + protected Remediation createRemediation( + final AnyType anyType, + final AnyCR anyCR, + final PullTask pullTask, + final ProvisioningReport result, + final SyncDelta delta) { + return createRemediation(anyType, null, anyCR, null, pullTask, result, delta); + } + + protected Remediation createRemediation( + final AnyType anyType, + final AnyUR anyUR, + final PullTask pullTask, + final ProvisioningReport result, + final SyncDelta delta) { + return createRemediation(anyType, null, null, anyUR, pullTask, result, delta); + } + + protected Remediation createRemediation( + final AnyType anyType, + final String anyKey, + final AnyCR anyCR, + final AnyUR anyUR, + final PullTask pullTask, + final ProvisioningReport result, + final SyncDelta delta) { + Remediation entity = entityFactory.newEntity(Remediation.class); + + entity.setAnyType(anyType); + entity.setOperation(anyUR == null ? ResourceOperation.CREATE : ResourceOperation.UPDATE); + if (StringUtils.isNotBlank(anyKey)) { + entity.setPayload(anyKey); + } else if (anyCR != null) { + entity.setPayload(anyCR); + } else if (anyUR != null) { + entity.setPayload(anyUR); + } + entity.setError(result.getMessage()); + entity.setInstant(new Date()); + entity.setRemoteName(delta.getObject().getName().getNameValue()); + entity.setPullTask(pullTask); + + return remediationDAO.save(entity); + } + } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java index 11cd1ba..238acdd 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.provisioning.java.pushpull; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -48,7 +47,6 @@ import org.apache.syncope.common.lib.types.UnmatchingRule; import org.apache.syncope.core.persistence.api.dao.PullMatch; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.Remediation; import org.apache.syncope.core.persistence.api.entity.resource.Provision; import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; import org.apache.syncope.core.persistence.api.entity.user.User; @@ -138,6 +136,11 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl profile.getExecutor(), getContext()); + if (ProvisioningReport.Status.FAILURE == result.getStatus() && profile.getTask().isRemediation()) { + createRemediation(anyTypeDAO.find(result.getAnyType()), anyUR, profile.getTask(), result, + delta); + } + return updated.getLeft(); } @@ -409,16 +412,7 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl resultStatus = Result.FAILURE; if (profile.getTask().isRemediation()) { - Remediation entity = entityFactory.newEntity(Remediation.class); - entity.setAnyType(provision.getAnyType()); - entity.setOperation(ResourceOperation.UPDATE); - entity.setPayload(req); - entity.setError(report.getMessage()); - entity.setInstant(new Date()); - entity.setRemoteName(delta.getObject().getName().getNameValue()); - entity.setPullTask(profile.getTask()); - - remediationDAO.save(entity); + createRemediation(provision.getAnyType(), req, profile.getTask(), report, delta); } } @@ -486,13 +480,13 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl }); update.getPlainAttrs().removeIf(attr -> attrsToRemove.contains(attr.getSchema())); - UserUR patch = new UserUR(); - patch.setKey(account.getOwner().getKey()); - patch.getLinkedAccounts().add(new LinkedAccountUR.Builder(). + UserUR userUR = new UserUR(); + userUR.setKey(account.getOwner().getKey()); + userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder(). operation(PatchOperation.ADD_REPLACE).linkedAccountTO(update).build()); for (PullActions action : profile.getActions()) { - action.beforeUpdate(profile, delta, before, patch); + action.beforeUpdate(profile, delta, before, userUR); } Result resultStatus; @@ -500,7 +494,7 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl try { userProvisioningManager.update( - patch, + userUR, report, null, Set.of(profile.getTask().getResource().getKey()), @@ -509,12 +503,11 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl getContext()); resultStatus = Result.SUCCESS; - LinkedAccountTO updated = userDAO.find(patch.getKey()). + LinkedAccountTO updated = userDAO.find(userUR.getKey()). getLinkedAccount(account.getResource().getKey(), account.getConnObjectKeyValue()). map(acct -> userDataBinder.getLinkedAccountTO(acct)). orElse(null); output = updated; - resultStatus = Result.SUCCESS; for (PullActions action : profile.getActions()) { action.after(profile, delta, updated, report); @@ -537,16 +530,7 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl resultStatus = Result.FAILURE; if (profile.getTask().isRemediation()) { - Remediation entity = entityFactory.newEntity(Remediation.class); - entity.setAnyType(provision.getAnyType()); - entity.setOperation(ResourceOperation.UPDATE); - entity.setPayload(patch); - entity.setError(report.getMessage()); - entity.setInstant(new Date()); - entity.setRemoteName(delta.getObject().getName().getNameValue()); - entity.setPullTask(profile.getTask()); - - remediationDAO.save(entity); + createRemediation(provision.getAnyType(), userUR, profile.getTask(), report, delta); } } @@ -620,16 +604,7 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl output = e; if (profile.getTask().isRemediation()) { - Remediation entity = entityFactory.newEntity(Remediation.class); - entity.setAnyType(provision.getAnyType()); - entity.setOperation(ResourceOperation.UPDATE); - entity.setPayload(req); - entity.setError(report.getMessage()); - entity.setInstant(new Date()); - entity.setRemoteName(delta.getObject().getName().getNameValue()); - entity.setPullTask(profile.getTask()); - - remediationDAO.save(entity); + createRemediation(provision.getAnyType(), req, profile.getTask(), report, delta); } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index 933e1d2..a8760c4 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -34,12 +34,15 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.naming.Context; import javax.naming.NamingException; +import javax.naming.directory.Attribute; import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.ModificationItem; @@ -734,6 +737,34 @@ public abstract class AbstractITCase { } } + protected void createLdapRemoteObject( + final String bindDn, + final String bindPwd, + final Pair<String, Set<Attribute>> entryAttrs) throws NamingException { + + InitialDirContext ctx = null; + try { + ctx = getLdapResourceDirContext(bindDn, bindPwd); + + BasicAttributes entry = new BasicAttributes(); + entryAttrs.getRight().forEach(item -> entry.put(item)); + + ctx.createSubcontext(entryAttrs.getLeft(), entry); + + } catch (NamingException e) { + LOG.error("While creating {} with {}", entryAttrs.getLeft(), entryAttrs.getRight(), e); + throw e; + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (NamingException e) { + // ignore + } + } + } + } + protected void updateLdapRemoteObject( final String bindDn, final String bindPwd, @@ -923,4 +954,5 @@ public abstract class AbstractITCase { } while (results.isEmpty() && i < maxWaitSeconds); return results; } + } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java index 5c78985..f67a0b7 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java @@ -42,12 +42,19 @@ import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.HashSet; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.BasicAttribute; +import javax.sql.DataSource; import javax.ws.rs.core.Response; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.SerializationUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; import org.apache.syncope.client.lib.SyncopeClient; import org.apache.syncope.common.lib.SyncopeClientException; @@ -380,7 +387,7 @@ public class PullTaskITCase extends AbstractTaskITCase { // 2. verify that pulled group is found PagedResult<GroupTO> matchingGroups = groupService.search(new AnyQuery.Builder().realm( - SyncopeConstants.ROOT_REALM). + SyncopeConstants.ROOT_REALM). fiql(SyncopeClient.getGroupSearchConditionBuilder().is("name").equalTo("testLDAPGroup").query()). build()); assertNotNull(matchingGroups); @@ -780,7 +787,7 @@ public class PullTaskITCase extends AbstractTaskITCase { // 3b. remediation was created Optional<RemediationTO> remediation = remediationService.list( - new RemediationQuery.Builder().page(1).size(1000).build()).getResult().stream(). + new RemediationQuery.Builder().page(1).size(1000).build()).getResult().stream(). filter(r -> "uid=pullFromLDAP,ou=People,o=isp".equalsIgnoreCase(r.getRemoteName())). findFirst(); assertTrue(remediation.isPresent()); @@ -854,7 +861,7 @@ public class PullTaskITCase extends AbstractTaskITCase { assertEquals(ClientExceptionType.Reconciliation, sce.getType()); } Optional<RemediationTO> remediation = remediationService.list( - new RemediationQuery.Builder().page(1).size(1000).build()).getResult().stream(). + new RemediationQuery.Builder().page(1).size(1000).build()).getResult().stream(). filter(r -> "uid=pullFromLDAP,ou=People,o=isp".equalsIgnoreCase(r.getRemoteName())). findFirst(); assertTrue(remediation.isPresent()); @@ -867,8 +874,7 @@ public class PullTaskITCase extends AbstractTaskITCase { "SyncopeClientCompositeException: {[RequiredValuesMissing [userId]]}")); } finally { resourceService.delete(ldap.getKey()); - remediationService.list(new RemediationQuery.Builder().page(1).size(10).build()).getResult().forEach( - r -> remediationService.delete(r.getKey())); + cleanUpRemediations(); } } @@ -1390,4 +1396,111 @@ public class PullTaskITCase extends AbstractTaskITCase { } } } + + @Test + public void issueSYNCOPE1656() throws NamingException { + // preliminary create a new LDAP object + createLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, prepareLdapAttributes( + "pullFromLDAP_issue1656", + "pullfromldap_issue1...@syncope.apache.org", + "Active", + "pullFromLDAP_issue1656", + "Surname", + "5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8", + "odd", + "password")); + try { + // 1. Pull from resource-ldap + PullTaskTO pullTask = new PullTaskTO(); + pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM); + pullTask.setName("SYNCOPE1656"); + pullTask.setActive(true); + pullTask.setPerformCreate(true); + pullTask.setPerformUpdate(true); + pullTask.setRemediation(true); + pullTask.setPullMode(PullMode.FULL_RECONCILIATION); + pullTask.setResource(RESOURCE_NAME_LDAP); + + Response taskResponse = taskService.create(TaskType.PULL, pullTask); + pullTask = getObject(taskResponse.getLocation(), TaskService.class, PullTaskTO.class); + assertNotNull(pullTask); + + ExecTO execution = execProvisioningTask( + taskService, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); + assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + + UserTO pullFromLDAP_issue1656 = userService.read("pullFromLDAP_issue1656"); + assertEquals("pullfromldap_issue1...@syncope.apache.org", + pullFromLDAP_issue1656.getPlainAttr("email").get().getValues().get(0)); + // 2. Edit mail attribute directly on the resource in order to have a not valid email + ConnObjectTO connObject = + resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), + pullFromLDAP_issue1656.getKey()); + assertNotNull(connObject); + assertEquals("pullfromldap_issue1...@syncope.apache.org", + connObject.getAttr("mail").get().getValues().get(0)); + Attr userDn = connObject.getAttr(Name.NAME).get(); + assertNotNull(userDn); + assertEquals(1, userDn.getValues().size()); + updateLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, + userDn.getValues().get(0), Collections.singletonMap("mail", "pullFromLDAP_issue1656@")); + // 3. Pull again from resource-ldap + execution = execProvisioningTask(taskService, TaskType.PULL, pullTask.getKey(), MAX_WAIT_SECONDS, false); + assertEquals(ExecStatus.SUCCESS, ExecStatus.valueOf(execution.getStatus())); + assertTrue(execution.getMessage().contains("UPDATE FAILURE")); + pullFromLDAP_issue1656 = userService.read("pullFromLDAP_issue1656"); + assertEquals("pullfromldap_issue1...@syncope.apache.org", + pullFromLDAP_issue1656.getPlainAttr("email").get().getValues().get(0)); + final String pullFromLDAP_issue1656_key = pullFromLDAP_issue1656.getKey(); + // a remediation for pullFromLDAP_issue1656 email should have been created + PagedResult<RemediationTO> remediations = + remediationService.list(new RemediationQuery.Builder().page(1).size(10).build()); + assertTrue(remediations.getResult().stream().filter(r -> r.getAnyURPayload() != null).anyMatch( + r -> pullFromLDAP_issue1656_key.equals(r.getAnyURPayload().getKey()))); + assertTrue(remediations.getResult().stream().anyMatch(r -> StringUtils.contains(r.getError(), + "\"pullFromLDAP_issue1656@\" is not a valid email address"))); + } finally { + // remove test entity + removeLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, + "uid=pullFromLDAP_issue1656,ou=People,o=isp"); + cleanUpRemediations(); + } + } + + private void cleanUpRemediations() { + remediationService.list(new RemediationQuery.Builder().page(1).size(100).build()).getResult().forEach( + r -> remediationService.delete(r.getKey())); + } + + private Pair<String, Set<Attribute>> prepareLdapAttributes( + final String uid, + final String email, + final String description, + final String givenName, + final String sn, + final String registeredAddress, + final String title, + final String password) { + String entryDn = "uid=" + uid + ",ou=People,o=isp"; + Set<Attribute> attributes = new HashSet<>(); + + attributes.add(new BasicAttribute("description", description)); + attributes.add(new BasicAttribute("givenName", givenName)); + attributes.add(new BasicAttribute("mail", email)); + attributes.add(new BasicAttribute("sn", sn)); + attributes.add(new BasicAttribute("cn", uid)); + attributes.add(new BasicAttribute("uid", uid)); + attributes.add(new BasicAttribute("registeredaddress", registeredAddress)); + attributes.add(new BasicAttribute("title", title)); + attributes.add(new BasicAttribute("userpassword", password)); + + Attribute oc = new BasicAttribute("objectClass"); + oc.add("top"); + oc.add("person"); + oc.add("inetOrgPerson"); + oc.add("organizationalPerson"); + attributes.add(oc); + + return Pair.of(entryDn, attributes); + } }