From commits-return-11082-archive-asf-public=cust-asf.ponee.io@syncope.apache.org Wed Mar 28 16:53:30 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 02897180652 for ; Wed, 28 Mar 2018 16:53:27 +0200 (CEST) Received: (qmail 8822 invoked by uid 500); 28 Mar 2018 14:53:27 -0000 Mailing-List: contact commits-help@syncope.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@syncope.apache.org Delivered-To: mailing list commits@syncope.apache.org Received: (qmail 8808 invoked by uid 99); 28 Mar 2018 14:53:27 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 28 Mar 2018 14:53:27 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id E9B4DE96A8; Wed, 28 Mar 2018 14:53:26 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: ilgrosso@apache.org To: commits@syncope.apache.org Date: Wed, 28 Mar 2018 14:53:26 -0000 Message-Id: <8b82ccebe4864c9bab886084a82d8db9@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] syncope git commit: [SYNCOPE-1287] Core implementation Repository: syncope Updated Branches: refs/heads/master 59a3422e4 -> 51b314fe5 http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java index d9cfafa..892850e 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java @@ -48,6 +48,7 @@ import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component public class SchemaLogic extends AbstractTransactionalLogic { @@ -145,11 +146,13 @@ public class SchemaLogic extends AbstractTransactionalLogic { } @PreAuthorize("isAuthenticated()") + @Transactional(readOnly = true) public List list(final SchemaType schemaType, final List anyTypeClasses) { return doSearch(schemaType, anyTypeClasses, null); } @PreAuthorize("isAuthenticated()") + @Transactional(readOnly = true) public List search( final SchemaType schemaType, final List anyTypeClasses, final String keyword) { http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java index f77f2fd..46285f2 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java @@ -50,8 +50,7 @@ public class SecurityQuestionLogic extends AbstractTransactionalLogic list() { - return securityQuestionDAO.findAll().stream(). - map(securityQuestion -> binder.getSecurityQuestionTO(securityQuestion)).collect(Collectors.toList()); + return securityQuestionDAO.findAll().stream().map(binder::getSecurityQuestionTO).collect(Collectors.toList()); } @PreAuthorize("hasRole('" + StandardEntitlement.SECURITY_QUESTION_READ + "')") http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java index 0fb587a..8aa7151 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java @@ -23,6 +23,7 @@ import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; import java.lang.reflect.Method; import java.net.InetAddress; +import java.net.URI; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; @@ -68,6 +69,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AttributeCond; import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Entity; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; @@ -210,8 +212,8 @@ public class SyncopeLogic extends AbstractLogic { PLATFORM_INFO.setBuildNumber(buildNumber); if (bundleManager.getLocations() != null) { - bundleManager.getLocations(). - forEach(location -> PLATFORM_INFO.getConnIdLocations().add(location.toASCIIString())); + PLATFORM_INFO.getConnIdLocations().addAll(bundleManager.getLocations().stream(). + map(URI::toASCIIString).collect(Collectors.toList())); } PLATFORM_INFO.setPropagationTaskExecutor(AopUtils.getTargetClass(propagationTaskExecutor).getName()); @@ -246,19 +248,19 @@ public class SyncopeLogic extends AbstractLogic { AuthContextUtils.execWithAuthContext(AuthContextUtils.getDomain(), () -> { PLATFORM_INFO.getAnyTypes().clear(); PLATFORM_INFO.getAnyTypes().addAll(anyTypeDAO.findAll().stream(). - map(type -> type.getKey()).collect(Collectors.toList())); + map(Entity::getKey).collect(Collectors.toList())); PLATFORM_INFO.getUserClasses().clear(); PLATFORM_INFO.getUserClasses().addAll(anyTypeDAO.findUser().getClasses().stream(). - map(cls -> cls.getKey()).collect(Collectors.toList())); + map(Entity::getKey).collect(Collectors.toList())); PLATFORM_INFO.getAnyTypeClasses().clear(); PLATFORM_INFO.getAnyTypeClasses().addAll(anyTypeClassDAO.findAll().stream(). - map(cls -> cls.getKey()).collect(Collectors.toList())); + map(Entity::getKey).collect(Collectors.toList())); PLATFORM_INFO.getResources().clear(); PLATFORM_INFO.getResources().addAll(resourceDAO.findAll().stream(). - map(resource -> resource.getKey()).collect(Collectors.toList())); + map(Entity::getKey).collect(Collectors.toList())); return null; }); } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java index baad7e8..bcbe645 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java @@ -63,6 +63,7 @@ import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component public class TaskLogic extends AbstractExecutableLogic { @@ -156,6 +157,7 @@ public class TaskLogic extends AbstractExecutableLogic { } @PreAuthorize("hasRole('" + StandardEntitlement.TASK_LIST + "')") + @Transactional(readOnly = true) @SuppressWarnings("unchecked") public Pair> list( final TaskType type, @@ -191,6 +193,7 @@ public class TaskLogic extends AbstractExecutableLogic { } @PreAuthorize("hasRole('" + StandardEntitlement.TASK_READ + "')") + @Transactional(readOnly = true) public T read(final TaskType type, final String key, final boolean details) { Task task = taskDAO.find(key); if (task == null) { http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java index 62b43ae..8fc4745 100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserWorkflowLogic.java @@ -58,7 +58,6 @@ public class UserWorkflowLogic extends AbstractTransactionalLogic getForms() { return uwfAdapter.getForms(); } @PreAuthorize("hasRole('" + StandardEntitlement.WORKFLOW_FORM_SUBMIT + "')") - @Transactional(rollbackFor = { Throwable.class }) public UserTO submitForm(final WorkflowFormTO form) { WorkflowResult updated = uwfAdapter.submitForm(form); http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java index f9db494..d6a3fe7 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java @@ -89,6 +89,8 @@ public class InvalidEntityException extends ValidationException { entityViolationType.setMessage(message.trim()); + entityViolationType.setPropertyPath(violation.getPropertyPath().toString()); + if (!this.violations.containsKey(violation.getLeafBean().getClass())) { this.violations.put(violation.getLeafBean().getClass(), EnumSet.noneOf(EntityViolationType.class)); } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RemediationDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RemediationDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RemediationDAO.java new file mode 100644 index 0000000..f00cb8a --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RemediationDAO.java @@ -0,0 +1,39 @@ +/* + * 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.persistence.api.dao; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Remediation; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; + +public interface RemediationDAO extends DAO { + + Remediation find(String key); + + List findByPullTask(PullTask pullTask); + + List findAll(); + + Remediation save(Remediation remediation); + + void delete(Remediation remediation); + + void delete(String key); + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Remediation.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Remediation.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Remediation.java new file mode 100644 index 0000000..1278241 --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Remediation.java @@ -0,0 +1,65 @@ +/* + * 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.persistence.api.entity; + +import java.util.Date; +import org.apache.syncope.common.lib.patch.AnyPatch; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; + +public interface Remediation extends Entity { + + AnyTypeKind getAnyTypeKind(); + + void setAnyTypeKind(AnyTypeKind anyTypeKind); + + ResourceOperation getOperation(); + + void setOperation(ResourceOperation operation); + + void setPayload(AnyTO anyTO); + + void setPayload(AnyPatch anyPatch); + + void setPayload(String key); + + T getPayloadAsTO(Class reference); + +

P getPayloadAsPatch(Class

reference); + + String getPayloadAsKey(); + + String getError(); + + void setError(String error); + + Date getInstant(); + + void setInstant(Date instant); + + PullTask getPullTask(); + + void setPullTask(PullTask pullTask); + + String getRemoteName(); + + void setRemoteName(String remoteName); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java index d4234f2..2d8ffdd 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java @@ -44,4 +44,9 @@ public interface PullTask extends ProvisioningTask { Optional getTemplate(AnyType anyType); List getTemplates(); + + void setRemediation(boolean remediation); + + boolean isRemediation(); + } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARemediationDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARemediationDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARemediationDAO.java new file mode 100644 index 0000000..b78c425 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARemediationDAO.java @@ -0,0 +1,73 @@ +/* + * 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.persistence.jpa.dao; + +import java.util.List; +import javax.persistence.TypedQuery; +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; +import org.apache.syncope.core.persistence.api.entity.Remediation; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.jpa.entity.JPARemediation; +import org.springframework.stereotype.Repository; + +@Repository +public class JPARemediationDAO extends AbstractDAO implements RemediationDAO { + + @Override + public Remediation find(final String key) { + return entityManager().find(JPARemediation.class, key); + } + + @Override + public List findByPullTask(final PullTask pullTask) { + TypedQuery query = entityManager().createQuery( + "SELECT e FROM " + JPARemediation.class.getSimpleName() + " e WHERE e.pullTask=:pullTask", + Remediation.class); + query.setParameter("pullTask", pullTask); + return query.getResultList(); + } + + @Override + public List findAll() { + TypedQuery query = entityManager().createQuery( + "SELECT e FROM " + JPARemediation.class.getSimpleName() + " e ", Remediation.class); + return query.getResultList(); + } + + @Override + public Remediation save(final Remediation remediation) { + return entityManager().merge(remediation); + } + + @Override + public void delete(final Remediation remediation) { + entityManager().remove(remediation); + } + + @Override + public void delete(final String key) { + Remediation remediation = find(key); + if (remediation == null) { + return; + } + + delete(remediation); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java index 792a661..d47ba5d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java @@ -25,6 +25,7 @@ import javax.persistence.TypedQuery; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; import org.apache.syncope.core.persistence.api.dao.TaskDAO; import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; import org.apache.syncope.core.persistence.api.entity.Entity; @@ -41,6 +42,7 @@ import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask; import org.apache.syncope.core.persistence.jpa.entity.task.AbstractTask; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ReflectionUtils; @@ -48,6 +50,9 @@ import org.springframework.util.ReflectionUtils; @Repository public class JPATaskDAO extends AbstractDAO implements TaskDAO { + @Autowired + private RemediationDAO remediationDAO; + @Override public Class getEntityReference(final TaskType type) { Class result = null; @@ -303,6 +308,12 @@ public class JPATaskDAO extends AbstractDAO implements TaskDAO { @Override public void delete(final Task task) { + if (task instanceof PullTask) { + remediationDAO.findByPullTask((PullTask) task).forEach(remediation -> { + remediation.setPullTask(null); + }); + } + entityManager().remove(task); } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java index aa2809a..8de177e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java @@ -130,6 +130,7 @@ import org.apache.syncope.core.persistence.api.entity.DynRealm; import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.Privilege; +import org.apache.syncope.core.persistence.api.entity.Remediation; import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRule; import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf; import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem; @@ -285,6 +286,8 @@ public class JPAEntityFactory implements EntityFactory { result = (E) new JPAAccessToken(); } else if (reference.equals(Implementation.class)) { result = (E) new JPAImplementation(); + } else if (reference.equals(Remediation.class)) { + result = (E) new JPARemediation(); } else { throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName()); } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARemediation.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARemediation.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARemediation.java new file mode 100644 index 0000000..6f31cc9 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARemediation.java @@ -0,0 +1,171 @@ +/* + * 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.persistence.jpa.entity; + +import java.util.Date; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Lob; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.validation.constraints.NotNull; +import org.apache.syncope.common.lib.patch.AnyPatch; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.entity.Remediation; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask; +import org.apache.syncope.core.persistence.jpa.validation.entity.RemediationCheck; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; + +@Entity +@Table(name = JPARemediation.TABLE) +@RemediationCheck +public class JPARemediation extends AbstractGeneratedKeyEntity implements Remediation { + + private static final long serialVersionUID = -1612530286294448682L; + + public static final String TABLE = "Remediation"; + + @NotNull + @Enumerated(EnumType.STRING) + private AnyTypeKind anyTypeKind; + + @NotNull + @Enumerated(EnumType.STRING) + private ResourceOperation operation; + + @NotNull + @Lob + private String payload; + + @NotNull + @Lob + private String error; + + @NotNull + @Temporal(TemporalType.TIMESTAMP) + private Date instant; + + @ManyToOne + private JPAPullTask pullTask; + + @NotNull + private String remoteName; + + @Override + public AnyTypeKind getAnyTypeKind() { + return anyTypeKind; + } + + @Override + public void setAnyTypeKind(final AnyTypeKind anyTypeKind) { + this.anyTypeKind = anyTypeKind; + } + + @Override + public ResourceOperation getOperation() { + return operation; + } + + @Override + public void setOperation(final ResourceOperation operation) { + this.operation = operation; + } + + @Override + public T getPayloadAsTO(final Class reference) { + return POJOHelper.deserialize(this.payload, reference); + } + + @Override + public

P getPayloadAsPatch(final Class

reference) { + return POJOHelper.deserialize(this.payload, reference); + } + + @Override + public String getPayloadAsKey() { + return this.payload; + } + + @Override + public void setPayload(final AnyTO anyTO) { + this.payload = POJOHelper.serialize(anyTO); + } + + @Override + public void setPayload(final AnyPatch anyPatch) { + this.payload = POJOHelper.serialize(anyPatch); + } + + @Override + public void setPayload(final String key) { + this.payload = key; + } + + @Override + public String getError() { + return error; + } + + @Override + public void setError(final String error) { + this.error = error; + } + + @Override + public Date getInstant() { + return instant == null + ? null + : new Date(instant.getTime()); + } + + @Override + public void setInstant(final Date instant) { + this.instant = instant == null + ? null + : new Date(instant.getTime()); + } + + @Override + public PullTask getPullTask() { + return pullTask; + } + + @Override + public void setPullTask(final PullTask pullTask) { + checkType(pullTask, JPAPullTask.class); + this.pullTask = (JPAPullTask) pullTask; + } + + @Override + public String getRemoteName() { + return remoteName; + } + + @Override + public void setRemoteName(final String remoteName) { + this.remoteName = remoteName; + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java index d42c3ea..5974800 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java @@ -21,6 +21,7 @@ package org.apache.syncope.core.persistence.jpa.entity.task; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; @@ -33,6 +34,8 @@ import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import org.apache.syncope.common.lib.types.ImplementationType; import org.apache.syncope.common.lib.types.PullMode; @@ -71,6 +74,11 @@ public class JPAPullTask extends AbstractProvisioningTask implements PullTask { @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pullTask") private List templates = new ArrayList<>(); + @Basic + @Min(0) + @Max(1) + private Integer remediation; + @Override public PullMode getPullMode() { return pullMode; @@ -134,4 +142,14 @@ public class JPAPullTask extends AbstractProvisioningTask implements PullTask { return templates; } + @Override + public void setRemediation(final boolean remediation) { + this.remediation = getBooleanAsInteger(remediation); + } + + @Override + public boolean isRemediation() { + return isBooleanAsInteger(remediation); + } + } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationCheck.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationCheck.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationCheck.java new file mode 100644 index 0000000..7a0a9c2 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationCheck.java @@ -0,0 +1,41 @@ +/* + * 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.persistence.jpa.validation.entity; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = RemediationValidator.class) +@Documented +public @interface RemediationCheck { + + String message() default "{org.apache.syncope.core.persistence.validation.remediation}"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationValidator.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationValidator.java new file mode 100644 index 0000000..25f9fb2 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationValidator.java @@ -0,0 +1,80 @@ +/* + * 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.persistence.jpa.validation.entity; + +import javax.validation.ConstraintValidatorContext; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.entity.Remediation; + +public class RemediationValidator extends AbstractValidator { + + @Override + public boolean isValid(final Remediation remediation, final ConstraintValidatorContext context) { + boolean isValid = true; + + switch (remediation.getOperation()) { + case CREATE: + if (remediation.getPayloadAsTO(remediation.getAnyTypeKind().getTOClass()) == null) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate( + getTemplate(EntityViolationType.InvalidRemediation, + "Expected " + remediation.getAnyTypeKind().getTOClass().getName())). + addPropertyNode("payload").addConstraintViolation(); + + isValid = false; + } + break; + + case UPDATE: + if (remediation.getPayloadAsPatch(remediation.getAnyTypeKind().getPatchClass()) == null) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate( + getTemplate(EntityViolationType.InvalidRemediation, + "Expected " + remediation.getAnyTypeKind().getPatchClass().getName())). + addPropertyNode("payload").addConstraintViolation(); + + isValid = false; + } + break; + + case DELETE: + if (!SyncopeConstants.UUID_PATTERN.matcher(remediation.getPayloadAsKey()).matches()) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate( + getTemplate(EntityViolationType.InvalidRemediation, "Expected UUID")). + addPropertyNode("payload").addConstraintViolation(); + + isValid = false; + } + break; + + case NONE: + default: + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate( + getTemplate(EntityViolationType.InvalidRemediation, "NONE is not allowed")). + addPropertyNode("operation").addConstraintViolation(); + + isValid = false; + } + + return isValid; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java new file mode 100644 index 0000000..02315fa --- /dev/null +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java @@ -0,0 +1,103 @@ +/* + * 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.persistence.jpa.inner; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.entity.Remediation; +import org.apache.syncope.core.persistence.jpa.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional("Master") +public class RemediationTest extends AbstractTest { + + @Autowired + private RemediationDAO remediationDAO; + + @Autowired + private TaskDAO taskDAO; + + @Test + public void findAll() { + List remediations = remediationDAO.findAll(); + assertTrue(remediations.isEmpty()); + } + + @Test + public void create() { + Remediation remediation = entityFactory.newEntity(Remediation.class); + remediation.setAnyTypeKind(AnyTypeKind.ANY_OBJECT); + remediation.setOperation(ResourceOperation.CREATE); + remediation.setError("Error"); + remediation.setInstant(new Date()); + remediation.setRemoteName("remote"); + remediation.setPullTask(taskDAO.find("38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1")); + + // missing payload + try { + remediationDAO.save(remediation); + fail("This should not happen"); + } catch (InvalidEntityException e) { + Set violations = e.getViolations().values().iterator().next(); + assertEquals(2, violations.size()); + assertTrue(violations.stream().allMatch(violation -> violation.getPropertyPath().equals("payload"))); + } + + remediation.setPayload(UUID.randomUUID().toString()); + + // wrong payload for operation + try { + remediationDAO.save(remediation); + fail("This should not happen"); + } catch (InvalidEntityException e) { + Set violations = e.getViolations().values().iterator().next(); + assertEquals(1, violations.size()); + assertTrue(violations.stream().anyMatch(violation -> violation.getPropertyPath().equals("payload"))); + } + + remediation.setOperation(ResourceOperation.DELETE); + + remediation = remediationDAO.save(remediation); + assertNotNull(remediation.getKey()); + assertNotNull(remediation.getPullTask()); + + taskDAO.delete(remediation.getPullTask()); + + remediationDAO.flush(); + + remediation = remediationDAO.find(remediation.getKey()); + assertNull(remediation.getPullTask()); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java index ae7074f..9fa892e 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java @@ -18,9 +18,11 @@ */ package org.apache.syncope.core.provisioning.api.data; +import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO; import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.types.ConnConfPropSchema; import org.apache.syncope.core.persistence.api.entity.ConnInstance; +import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf; import org.identityconnectors.framework.api.ConfigurationProperty; public interface ConnInstanceDataBinder { @@ -31,6 +33,8 @@ public interface ConnInstanceDataBinder { ConnInstanceTO getConnInstanceTO(ConnInstance connInstance); + ConnInstanceHistoryConfTO getConnInstanceHistoryConfTO(ConnInstanceHistoryConf history); + ConnInstance update(ConnInstanceTO connInstanceTO); } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RemediationDataBinder.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RemediationDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RemediationDataBinder.java new file mode 100644 index 0000000..42d8332 --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RemediationDataBinder.java @@ -0,0 +1,28 @@ +/* + * 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.api.data; + +import org.apache.syncope.common.lib.to.RemediationTO; +import org.apache.syncope.core.persistence.api.entity.Remediation; + +public interface RemediationDataBinder { + + RemediationTO getRemediationTO(Remediation remediation); + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ResourceDataBinder.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ResourceDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ResourceDataBinder.java index 9f1ea5d..84b2254 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ResourceDataBinder.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ResourceDataBinder.java @@ -18,8 +18,10 @@ */ package org.apache.syncope.core.provisioning.api.data; +import org.apache.syncope.common.lib.to.ResourceHistoryConfTO; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf; public interface ResourceDataBinder { @@ -29,4 +31,5 @@ public interface ResourceDataBinder { ExternalResource update(ExternalResource resource, ResourceTO resourceTO); + ResourceHistoryConfTO getResourceHistoryConfTO(ExternalResourceHistoryConf history); } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java index d4b9206..333311c 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Optional; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.ConnInstanceHistoryConfTO; import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.to.ConnPoolConfTO; import org.apache.syncope.common.lib.types.ClientExceptionType; @@ -277,4 +278,15 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { return connInstanceTO; } + + @Override + public ConnInstanceHistoryConfTO getConnInstanceHistoryConfTO(final ConnInstanceHistoryConf history) { + ConnInstanceHistoryConfTO historyTO = new ConnInstanceHistoryConfTO(); + historyTO.setKey(history.getKey()); + historyTO.setCreator(history.getCreator()); + historyTO.setCreation(history.getCreation()); + historyTO.setConnInstanceTO(history.getConf()); + + return historyTO; + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RemediationDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RemediationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RemediationDataBinderImpl.java new file mode 100644 index 0000000..d95f866 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RemediationDataBinderImpl.java @@ -0,0 +1,65 @@ +/* + * 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.data; + +import org.apache.syncope.common.lib.to.RemediationTO; +import org.apache.syncope.core.persistence.api.entity.Remediation; +import org.apache.syncope.core.provisioning.api.data.RemediationDataBinder; +import org.apache.syncope.core.spring.BeanUtils; +import org.springframework.stereotype.Component; + +@Component +public class RemediationDataBinderImpl implements RemediationDataBinder { + + private static final String[] IGNORE_PROPERTIES = { + "payload", "anyTOPayload", "anyPatchPayload", "keyPayload", "pullTask" }; + + @Override + public RemediationTO getRemediationTO(final Remediation remediation) { + RemediationTO remediationTO = new RemediationTO(); + + BeanUtils.copyProperties(remediation, remediationTO); + + switch (remediation.getOperation()) { + case CREATE: + remediationTO.setAnyTOPayload( + remediation.getPayloadAsTO(remediation.getAnyTypeKind().getTOClass())); + break; + + case UPDATE: + remediationTO.setAnyPatchPayload( + remediation.getPayloadAsPatch(remediation.getAnyTypeKind().getPatchClass())); + break; + + case DELETE: + remediationTO.setKeyPayload(remediation.getPayloadAsKey()); + break; + + default: + } + + if (remediation.getPullTask() != null) { + remediationTO.setPullTask(remediation.getPullTask().getKey()); + remediationTO.setResource(remediation.getPullTask().getResource().getKey()); + } + + return remediationTO; + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java index 1db8e56..681743e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java @@ -34,6 +34,7 @@ import org.apache.syncope.common.lib.to.ItemTO; import org.apache.syncope.common.lib.to.MappingTO; import org.apache.syncope.common.lib.to.OrgUnitTO; import org.apache.syncope.common.lib.to.ProvisionTO; +import org.apache.syncope.common.lib.to.ResourceHistoryConfTO; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.MappingPurpose; @@ -646,4 +647,15 @@ public class ResourceDataBinderImpl implements ResourceDataBinder { return resourceTO; } + + @Override + public ResourceHistoryConfTO getResourceHistoryConfTO(final ExternalResourceHistoryConf history) { + ResourceHistoryConfTO historyTO = new ResourceHistoryConfTO(); + historyTO.setKey(history.getKey()); + historyTO.setCreator(history.getCreator()); + historyTO.setCreation(history.getCreation()); + historyTO.setResourceTO(history.getConf()); + + return historyTO; + } } http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java index 1b30e59..6fcc05e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java @@ -219,6 +219,8 @@ public class TaskDataBinderImpl implements TaskDataBinder { // remove all templates not contained in the TO pullTask.getTemplates(). removeIf(anyTemplate -> !pullTaskTO.getTemplates().containsKey(anyTemplate.getAnyType().getKey())); + + pullTask.setRemediation(pullTaskTO.isRemediation()); } // 3. fill the remaining fields @@ -407,6 +409,8 @@ public class TaskDataBinderImpl implements TaskDataBinder { pullTask.getTemplates().forEach(template -> { pullTaskTO.getTemplates().put(template.getAnyType().getKey(), template.get()); }); + + pullTaskTO.setRemediation(pullTask.isRemediation()); break; case PUSH: http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java ---------------------------------------------------------------------- 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 2832cb5..a748562 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 @@ -20,6 +20,7 @@ package org.apache.syncope.core.provisioning.java.pushpull; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.syncope.common.lib.AnyOperations; @@ -36,10 +37,13 @@ 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.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; import org.apache.syncope.core.provisioning.api.propagation.PropagationException; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.Remediation; import org.apache.syncope.core.persistence.api.entity.VirSchema; import org.apache.syncope.core.persistence.api.entity.resource.Provision; import org.apache.syncope.core.persistence.api.entity.task.PullTask; @@ -78,11 +82,17 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan protected ConnObjectUtils connObjectUtils; @Autowired + protected RemediationDAO remediationDAO; + + @Autowired protected VirSchemaDAO virSchemaDAO; @Autowired protected VirAttrCache virAttrCache; + @Autowired + protected EntityFactory entityFactory; + protected SyncopePullExecutor executor; protected Result latestResult; @@ -299,6 +309,19 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan LOG.error("Could not create {} {} ", anyTO.getType(), delta.getUid().getUidValue(), e); output = e; resultStatus = Result.FAILURE; + + if (profile.getTask().isRemediation()) { + Remediation entity = entityFactory.newEntity(Remediation.class); + entity.setAnyTypeKind(getAnyUtils().getAnyTypeKind()); + entity.setOperation(ResourceOperation.CREATE); + entity.setPayload(anyTO); + entity.setError(result.getMessage()); + entity.setInstant(new Date()); + entity.setRemoteName(delta.getObject().getName().getNameValue()); + entity.setPullTask(profile.getTask()); + + remediationDAO.save(entity); + } } finalize(operation, resultStatus, null, output, delta); @@ -343,8 +366,9 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan resultStatus = Result.FAILURE; output = null; } else { + AnyPatch anyPatch = null; try { - AnyPatch anyPatch = connObjectUtils.getAnyPatch( + anyPatch = connObjectUtils.getAnyPatch( before.getKey(), delta.getObject(), before, @@ -384,6 +408,19 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan provision.getAnyType().getKey(), delta.getUid().getUidValue(), e); output = e; resultStatus = Result.FAILURE; + + if (profile.getTask().isRemediation()) { + Remediation entity = entityFactory.newEntity(Remediation.class); + entity.setAnyTypeKind(provision.getAnyType().getKind()); + entity.setOperation(ResourceOperation.UPDATE); + entity.setPayload(anyPatch); + entity.setError(result.getMessage()); + entity.setInstant(new Date()); + entity.setRemoteName(delta.getObject().getName().getNameValue()); + entity.setPullTask(profile.getTask()); + + remediationDAO.save(entity); + } } } finalize(MatchingRule.toEventName(MatchingRule.UPDATE), @@ -659,6 +696,19 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan result.setMessage(ExceptionUtils.getRootCauseMessage(e)); LOG.error("Could not delete {} {}", provision.getAnyType().getKey(), key, e); output = e; + + if (profile.getTask().isRemediation()) { + Remediation entity = entityFactory.newEntity(Remediation.class); + entity.setAnyTypeKind(provision.getAnyType().getKind()); + entity.setOperation(ResourceOperation.DELETE); + entity.setPayload(key); + entity.setError(result.getMessage()); + entity.setInstant(new Date()); + entity.setRemoteName(delta.getObject().getName().getNameValue()); + entity.setPullTask(profile.getTask()); + + remediationDAO.save(entity); + } } finalize(ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, output, delta); http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RemediationServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RemediationServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RemediationServiceImpl.java new file mode 100644 index 0000000..201bb48 --- /dev/null +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RemediationServiceImpl.java @@ -0,0 +1,116 @@ +/* + * 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.rest.cxf.service; + +import java.util.Date; +import java.util.List; +import javax.ws.rs.core.Response; +import org.apache.syncope.common.lib.patch.AnyPatch; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.RemediationTO; +import org.apache.syncope.common.rest.api.service.RemediationService; +import org.apache.syncope.core.logic.RemediationLogic; +import org.apache.syncope.core.persistence.api.dao.AnyDAO; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class RemediationServiceImpl extends AbstractServiceImpl implements RemediationService { + + @Autowired + private RemediationLogic logic; + + @Autowired + private UserDAO userDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Override + public List list() { + return logic.list(); + } + + @Override + public RemediationTO read(final String key) { + return logic.read(key); + } + + @Override + public Response delete(final String key) { + logic.delete(key); + return Response.noContent().build(); + } + + @Override + public Response remedy(final String key, final AnyTO anyTO) { + ProvisioningResult created = logic.remedy(key, anyTO, isNullPriorityAsync()); + return createResponse(created); + } + + private void check(final String key, final String anyKey) { + RemediationTO remediation = logic.read(key); + + AnyDAO anyDAO; + switch (remediation.getAnyTypeKind()) { + case USER: + default: + anyDAO = userDAO; + break; + + case GROUP: + anyDAO = groupDAO; + break; + + case ANY_OBJECT: + anyDAO = anyObjectDAO; + } + + Date etagDate = anyDAO.findLastChange(anyKey); + if (etagDate == null) { + throw new NotFoundException(remediation.getAnyTypeKind().name() + " for " + key); + } + checkETag(String.valueOf(etagDate.getTime())); + } + + @Override + public Response remedy(final String key, final AnyPatch anyPatch) { + check(key, anyPatch.getKey()); + + ProvisioningResult updated = logic.remedy(key, anyPatch, isNullPriorityAsync()); + return modificationResponse(updated); + } + + @Override + public Response remedy(final String key, final String anyKey) { + check(key, anyKey); + + ProvisioningResult deleted = logic.remedy(key, anyKey, isNullPriorityAsync()); + return modificationResponse(deleted); + } + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java ---------------------------------------------------------------------- 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 7143545..3151971 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 @@ -86,6 +86,7 @@ import org.apache.syncope.common.rest.api.service.ImplementationService; import org.apache.syncope.common.rest.api.service.MailTemplateService; import org.apache.syncope.common.rest.api.service.RealmService; import org.apache.syncope.common.rest.api.service.RelationshipTypeService; +import org.apache.syncope.common.rest.api.service.RemediationService; import org.apache.syncope.common.rest.api.service.ReportTemplateService; import org.apache.syncope.common.rest.api.service.ResourceHistoryService; import org.apache.syncope.common.rest.api.service.RoleService; @@ -241,6 +242,8 @@ public abstract class AbstractITCase { protected static ImplementationService implementationService; + protected static RemediationService remediationService; + protected static CamelRouteService camelRouteService; protected static SAML2SPService saml2SpService; @@ -311,6 +314,7 @@ public abstract class AbstractITCase { schemaService = adminClient.getService(SchemaService.class); securityQuestionService = adminClient.getService(SecurityQuestionService.class); implementationService = adminClient.getService(ImplementationService.class); + remediationService = adminClient.getService(RemediationService.class); camelRouteService = adminClient.getService(CamelRouteService.class); saml2SpService = adminClient.getService(SAML2SPService.class); saml2IdPService = adminClient.getService(SAML2IdPService.class); http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java index 11fbb0b..55c1a85 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AbstractTaskITCase.java @@ -35,7 +35,6 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.patch.DeassociationPatch; import org.apache.syncope.common.lib.to.TaskTO; import org.apache.syncope.common.lib.to.ExecTO; -import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.NotificationTaskTO; import org.apache.syncope.common.lib.to.PagedResult; import org.apache.syncope.common.lib.to.UserTO; @@ -102,29 +101,20 @@ public abstract class AbstractTaskITCase extends AbstractITCase { * Clean Syncope and LDAP resource status. */ protected void ldapCleanup() { - PagedResult matchingGroups = groupService.search(new AnyQuery.Builder().realm( - SyncopeConstants.ROOT_REALM). + groupService.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). fiql(SyncopeClient.getGroupSearchConditionBuilder().is("name").equalTo("testLDAPGroup").query()). - build()); - if (matchingGroups.getSize() > 0) { - for (GroupTO group : matchingGroups.getResult()) { - groupService.deassociate(new DeassociationPatch.Builder().key(group.getKey()). - action(ResourceDeassociationAction.UNLINK).resource(RESOURCE_NAME_LDAP).build()); - groupService.delete(group.getKey()); - } - } - PagedResult matchingUsers = userService.search( - new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). - fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP"). - query()). - build()); - if (matchingUsers.getSize() > 0) { - for (UserTO user : matchingUsers.getResult()) { - userService.deassociate(new DeassociationPatch.Builder().key(user.getKey()). - action(ResourceDeassociationAction.UNLINK).resource(RESOURCE_NAME_LDAP).build()); - userService.delete(user.getKey()); - } - } + build()).getResult().forEach(group -> { + groupService.deassociate(new DeassociationPatch.Builder().key(group.getKey()). + action(ResourceDeassociationAction.UNLINK).resource(RESOURCE_NAME_LDAP).build()); + groupService.delete(group.getKey()); + }); + userService.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). + fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("pullFromLDAP").query()). + build()).getResult().forEach(user -> { + userService.deassociate(new DeassociationPatch.Builder().key(user.getKey()). + action(ResourceDeassociationAction.UNLINK).resource(RESOURCE_NAME_LDAP).build()); + userService.delete(user.getKey()); + }); } protected static ExecTO execTask( http://git-wip-us.apache.org/repos/asf/syncope/blob/51b314fe/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java ---------------------------------------------------------------------- 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 3e6d67a..6522fcb 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 @@ -35,6 +35,7 @@ import java.util.Date; import java.util.HashSet; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.UUID; @@ -65,6 +66,7 @@ import org.apache.syncope.common.lib.to.PullTaskTO; import org.apache.syncope.common.lib.to.ExecTO; import org.apache.syncope.common.lib.to.ImplementationTO; import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.RemediationTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.CipherAlgorithm; @@ -77,6 +79,7 @@ import org.apache.syncope.common.lib.types.PolicyType; import org.apache.syncope.common.lib.types.PropagationTaskExecStatus; import org.apache.syncope.common.lib.types.ResourceDeassociationAction; import org.apache.syncope.common.lib.types.PullMode; +import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.beans.AnyQuery; @@ -659,6 +662,87 @@ public class PullTaskITCase extends AbstractTaskITCase { } @Test + public void remediation() { + // First of all, clear any potential conflict with existing user / group + ldapCleanup(); + + // 1. create ldap cloned resource, where 'userId' (mandatory on Syncope) is removed from mapping + ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP); + ldap.setKey("ldapForRemediation"); + + ProvisionTO provision = ldap.getProvision(AnyTypeKind.USER.name()).get(); + provision.getVirSchemas().clear(); + provision.getMapping().getItems().removeIf(item -> "userId".equals(item.getIntAttrName())); + + ldap = createResource(ldap); + + // 2. create PullTask with remediation enabled, for the new resource + PullTaskTO pullTask = (PullTaskTO) taskService.list(new TaskQuery.Builder(TaskType.PULL). + resource(RESOURCE_NAME_LDAP).build()).getResult().get(0); + assertNotNull(pullTask); + pullTask.setResource(ldap.getKey()); + pullTask.setRemediation(true); + pullTask.getActions().clear(); + + Response response = taskService.create(TaskType.PULL, pullTask); + if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) { + throw (RuntimeException) clientFactory.getExceptionMapper().fromResponse(response); + } + pullTask = getObject(response.getLocation(), TaskService.class, PullTaskTO.class); + assertNotNull(pullTask); + + try { + // 3. execute the pull task and verify that: + ExecTO execution = execProvisioningTask(taskService, TaskType.PULL, pullTask.getKey(), 50, false); + assertEquals(PropagationTaskExecStatus.SUCCESS, PropagationTaskExecStatus.valueOf(execution.getStatus())); + + // 3a. user was not pulled + try { + userService.read("pullFromLDAP"); + fail("This should never happen"); + } catch (SyncopeClientException e) { + assertEquals(ClientExceptionType.NotFound, e.getType()); + } + + // 3b. remediation was created + Optional remediation = remediationService.list().stream(). + filter(r -> "uid=pullFromLDAP,ou=People,o=isp".equalsIgnoreCase(r.getRemoteName())). + findFirst(); + assertTrue(remediation.isPresent()); + assertEquals(AnyTypeKind.USER, remediation.get().getAnyTypeKind()); + assertEquals(ResourceOperation.CREATE, remediation.get().getOperation()); + assertNotNull(remediation.get().getAnyTOPayload()); + assertNull(remediation.get().getAnyPatchPayload()); + assertNull(remediation.get().getKeyPayload()); + assertTrue(remediation.get().getError().contains("RequiredValuesMissing [userId]")); + + // 4. remedy by copying the email value to userId + UserTO user = (UserTO) remediation.get().getAnyTOPayload(); + user.getResources().clear(); + + String email = user.getPlainAttr("email").get().getValues().get(0); + user.getPlainAttrs().add(new AttrTO.Builder().schema("userId").value(email).build()); + + remediationService.remedy(remediation.get().getKey(), user); + + // 5. user is now found + user = userService.read("pullFromLDAP"); + assertNotNull(user); + assertEquals(email, user.getPlainAttr("userId").get().getValues().get(0)); + + // 6. remediation was removed + try { + remediationService.read(remediation.get().getKey()); + fail("This should never happen"); + } catch (SyncopeClientException e) { + assertEquals(ClientExceptionType.NotFound, e.getType()); + } + } finally { + resourceService.delete(ldap.getKey()); + } + } + + @Test public void issueSYNCOPE68() { //----------------------------- // Create a new user ... it should be updated applying pull policy