[SYNCOPE-1299] Core implementation
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/e5860a76 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/e5860a76 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/e5860a76 Branch: refs/heads/master Commit: e5860a76a5584edf780df180eb9229ddf44de684 Parents: 3c4a351 Author: Francesco Chicchiriccò <ilgro...@apache.org> Authored: Thu Apr 12 11:50:16 2018 +0200 Committer: Francesco Chicchiriccò <ilgro...@apache.org> Committed: Thu Apr 12 12:41:49 2018 +0200 ---------------------------------------------------------------------- .../common/lib/to/ProvisioningTaskTO.java | 3 +- .../common/lib/to/ReconciliationRequest.java | 100 ++++++++ .../common/lib/to/ReconciliationStatus.java | 56 +++++ .../common/lib/types/ClientExceptionType.java | 1 + .../common/lib/types/ReconciliationAction.java | 28 +++ .../rest/api/service/ReconciliationService.java | 78 +++++++ .../syncope/core/logic/ReconciliationLogic.java | 232 +++++++++++++++++++ .../core/persistence/api/entity/AnyUtils.java | 7 +- .../persistence/jpa/dao/JPAAnyObjectDAO.java | 4 +- .../persistence/jpa/entity/JPAAnyUtils.java | 26 ++- .../api/pushpull/SyncopeSinglePullExecutor.java | 37 +++ .../api/pushpull/SyncopeSinglePushExecutor.java | 34 +++ .../provisioning/java/MappingManagerImpl.java | 2 +- .../pushpull/AbstractPullResultHandler.java | 6 +- .../pushpull/AbstractPushResultHandler.java | 2 +- .../java/pushpull/PullJobDelegate.java | 49 ++-- .../provisioning/java/pushpull/PullUtils.java | 23 +- .../java/pushpull/PushJobDelegate.java | 86 ++----- .../java/pushpull/SinglePullJobDelegate.java | 174 ++++++++++++++ .../java/pushpull/SinglePushJobDelegate.java | 117 ++++++++++ .../java/utils/ConnObjectUtils.java | 4 +- .../rest/cxf/service/AbstractAnyService.java | 26 +-- .../rest/cxf/service/AbstractServiceImpl.java | 15 ++ .../rest/cxf/service/AnyObjectServiceImpl.java | 2 +- .../core/rest/cxf/service/GroupServiceImpl.java | 2 +- .../cxf/service/ReconciliationServiceImpl.java | 53 +++++ .../core/rest/cxf/service/UserServiceImpl.java | 2 +- .../org/apache/syncope/fit/AbstractITCase.java | 4 + .../syncope/fit/core/ReconciliationITCase.java | 134 +++++++++++ 29 files changed, 1151 insertions(+), 156 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/common/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java index 27d4f9b..cdef683 100644 --- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java @@ -33,8 +33,7 @@ import org.apache.syncope.common.lib.types.UnmatchingRule; @XmlRootElement(name = "provisioningTask") @XmlType @XmlSeeAlso({ PushTaskTO.class, PullTaskTO.class }) -@Schema( - allOf = { SchedTaskTO.class }, +@Schema(allOf = { SchedTaskTO.class }, subTypes = { PushTaskTO.class, PullTaskTO.class }, discriminatorProperty = "@class") public abstract class ProvisioningTaskTO extends SchedTaskTO { http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/common/lib/src/main/java/org/apache/syncope/common/lib/to/ReconciliationRequest.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ReconciliationRequest.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ReconciliationRequest.java new file mode 100644 index 0000000..2ebf699 --- /dev/null +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ReconciliationRequest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.common.lib.to; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import org.apache.syncope.common.lib.AbstractBaseBean; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ReconciliationAction; + +public class ReconciliationRequest extends AbstractBaseBean { + + private static final long serialVersionUID = -2592156800185957182L; + + private AnyTypeKind anyTypeKind; + + private String anyKey; + + private String resourceKey; + + private ReconciliationAction action; + + private boolean remediation; + + private final List<String> actions = new ArrayList<>(); + + @JsonProperty(required = true) + @XmlElement(required = true) + public AnyTypeKind getAnyTypeKind() { + return anyTypeKind; + } + + public void setAnyTypeKind(final AnyTypeKind anyTypeKind) { + this.anyTypeKind = anyTypeKind; + } + + @JsonProperty(required = true) + @XmlElement(required = true) + public String getAnyKey() { + return anyKey; + } + + public void setAnyKey(final String anyKey) { + this.anyKey = anyKey; + } + + @JsonProperty(required = true) + @XmlElement(required = true) + public String getResourceKey() { + return resourceKey; + } + + public void setResourceKey(final String resourceKey) { + this.resourceKey = resourceKey; + } + + @JsonProperty(required = true) + @XmlElement(required = true) + public ReconciliationAction getAction() { + return action; + } + + public void setAction(final ReconciliationAction action) { + this.action = action; + } + + public boolean isRemediation() { + return remediation; + } + + public void setRemediation(final boolean remediation) { + this.remediation = remediation; + } + + @XmlElementWrapper(name = "actions") + @XmlElement(name = "action") + @JsonProperty("actions") + public List<String> getActions() { + return actions; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/common/lib/src/main/java/org/apache/syncope/common/lib/to/ReconciliationStatus.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ReconciliationStatus.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ReconciliationStatus.java new file mode 100644 index 0000000..2d7af98 --- /dev/null +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ReconciliationStatus.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.common.lib.to; + +import io.swagger.v3.oas.annotations.media.Schema; +import org.apache.syncope.common.lib.AbstractBaseBean; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +/** + * Reconciliation status. + */ +@XmlRootElement(name = "reconciliationStatus") +@XmlType +public class ReconciliationStatus extends AbstractBaseBean { + + private static final long serialVersionUID = -8516345256596521490L; + + private ConnObjectTO onSyncope; + + private ConnObjectTO onResource; + + @Schema(accessMode = Schema.AccessMode.READ_ONLY) + public ConnObjectTO getOnSyncope() { + return onSyncope; + } + + public void setOnSyncope(final ConnObjectTO onSyncope) { + this.onSyncope = onSyncope; + } + + @Schema(accessMode = Schema.AccessMode.READ_ONLY) + public ConnObjectTO getOnResource() { + return onResource; + } + + public void setOnResource(final ConnObjectTO onResource) { + this.onResource = onResource; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java index ada4a1e..8a8f744 100644 --- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java @@ -74,6 +74,7 @@ public enum ClientExceptionType { InUse(Response.Status.BAD_REQUEST), Scheduling(Response.Status.BAD_REQUEST), DelegatedAdministration(Response.Status.FORBIDDEN), + Reconciliation(Response.Status.BAD_REQUEST), Unknown(Response.Status.BAD_REQUEST), Workflow(Response.Status.BAD_REQUEST); http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/common/lib/src/main/java/org/apache/syncope/common/lib/types/ReconciliationAction.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ReconciliationAction.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ReconciliationAction.java new file mode 100644 index 0000000..19b68c2 --- /dev/null +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ReconciliationAction.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.common.lib.types; + +import javax.xml.bind.annotation.XmlEnum; + +@XmlEnum +public enum ReconciliationAction { + PUSH, + PULL + +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java ---------------------------------------------------------------------- diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java new file mode 100644 index 0000000..77eb840 --- /dev/null +++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.common.rest.api.service; + +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; +import io.swagger.v3.oas.annotations.tags.Tag; +import javax.validation.constraints.NotNull; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.ReconciliationRequest; +import org.apache.syncope.common.lib.to.ReconciliationStatus; +import org.apache.syncope.common.lib.types.AnyTypeKind; + +/** + * REST operations for tasks. + */ +@Tag(name = "Reconciliation") +@SecurityRequirements({ + @SecurityRequirement(name = "BasicAuthentication"), + @SecurityRequirement(name = "Bearer") }) +@Path("reconciliation") +public interface ReconciliationService extends JAXRSService { + + /** + * Gets current attributes on Syncope and on the given External Resource, related to given user, group or + * any object. + * + * @param anyTypeKind anyTypeKind + * @param anyKey user, group or any object: if value looks like a UUID then it is interpreted as key, otherwise as + * a (user)name + * @param resourceKey resource key + * @return reconciliation status + */ + @GET + @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + ReconciliationStatus status( + @NotNull @QueryParam("anyTypeKind") AnyTypeKind anyTypeKind, + @NotNull @QueryParam("anyKey") String anyKey, + @NotNull @QueryParam("resourceKey") String resourceKey); + + /** + * Perform the required reconciliation action (PUSH or PULL) to the given user, group or any object and + * External Resource. + * + * @param request reconciliation request + */ + @ApiResponses( + @ApiResponse(responseCode = "204", description = "Operation was successful")) + @POST + @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML }) + void reconcile(@NotNull ReconciliationRequest request); +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java ---------------------------------------------------------------------- diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java new file mode 100644 index 0000000..a36c159 --- /dev/null +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.logic; + +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.AbstractBaseBean; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.collections.IteratorChain; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.ConnObjectTO; +import org.apache.syncope.common.lib.to.ReconciliationRequest; +import org.apache.syncope.common.lib.to.ReconciliationStatus; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.StandardEntitlement; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +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.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.ConnectorFactory; +import org.apache.syncope.core.provisioning.api.MappingManager; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecutor; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePushExecutor; +import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; +import org.apache.syncope.core.provisioning.java.utils.MappingUtils; +import org.identityconnectors.framework.common.objects.Attribute; +import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.AttributeUtil; +import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.Name; +import org.identityconnectors.framework.common.objects.Uid; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +@Component +public class ReconciliationLogic extends AbstractTransactionalLogic<AbstractBaseBean> { + + @Autowired + private AnyUtilsFactory anyUtilsFactory; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private VirSchemaDAO virSchemaDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private MappingManager mappingManager; + + @Autowired + private ConnectorFactory connFactory; + + @Autowired + private SyncopeSinglePullExecutor singlePullExecutor; + + @Autowired + private SyncopeSinglePushExecutor singlePushExecutor; + + @SuppressWarnings("unchecked") + private Pair<Any<?>, Provision> init(final AnyTypeKind anyTypeKind, final String anyKey, final String resourceKey) { + AnyUtils anyUtils = anyUtilsFactory.getInstance(anyTypeKind); + + Any<?> any = anyUtils.dao().authFind(anyKey); + if (any == null) { + throw new NotFoundException(anyTypeKind + " '" + anyKey + "'"); + } + + ExternalResource resource = resourceDAO.find(resourceKey); + if (resource == null) { + throw new NotFoundException("Resource '" + resourceKey + "'"); + } + Provision provision = resource.getProvision(any.getType()).orElseThrow(() + -> new NotFoundException("Provision for " + any.getType() + " on Resource '" + resourceKey + "'")); + if (provision.getMapping() == null) { + throw new NotFoundException("Mapping for " + any.getType() + " on Resource '" + resourceKey + "'"); + } + + return (Pair<Any<?>, Provision>) Pair.of(any, provision); + } + + private ConnObjectTO getOnSyncope(final Any<?> any, final Provision provision, final String resourceKey) { + Pair<String, Set<Attribute>> attrs = mappingManager.prepareAttrs(any, null, false, true, provision); + + MappingItem connObjectKey = provision.getMapping().getConnObjectKeyItem().orElseThrow(() + -> new NotFoundException("No RemoteKey set for " + resourceKey)); + + ConnObjectTO connObjectTO = ConnObjectUtils.getConnObjectTO(attrs.getRight()); + if (attrs.getLeft() != null) { + connObjectTO.getAttrs().add(new AttrTO.Builder(). + schema(connObjectKey.getExtAttrName()).value(attrs.getLeft()).build()); + connObjectTO.getAttrs().add(new AttrTO.Builder(). + schema(Uid.NAME).value(attrs.getLeft()).build()); + } + + return connObjectTO; + } + + private ConnObjectTO getOnResource(final Any<?> any, final Provision provision) { + // 1. build connObjectKeyItem + MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision).orElseThrow(() + -> new NotFoundException("ConnObjectKey for " + any.getType() + + " on resource '" + provision.getResource().getKey() + "'")); + String connObjectKeyValue = mappingManager.getConnObjectKeyValue(any, provision).orElseThrow(() + -> new NotFoundException("Value for ConnObjectKey for " + any.getType() + + " on resource '" + provision.getResource().getKey() + "'")); + + // 2. determine attributes to query + Set<MappingItem> linkinMappingItems = virSchemaDAO.findByProvision(provision).stream(). + map(virSchema -> virSchema.asLinkingMappingItem()).collect(Collectors.toSet()); + Iterator<MappingItem> mapItems = new IteratorChain<>( + provision.getMapping().getItems().iterator(), + linkinMappingItems.iterator()); + + // 3. read from the underlying connector + ConnObjectTO connObjectTO = null; + + Connector connector = connFactory.getConnector(provision.getResource()); + ConnectorObject connectorObject = connector.getObject( + provision.getObjectClass(), + AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKeyValue), + MappingUtils.buildOperationOptions(mapItems)); + if (connectorObject != null) { + Set<Attribute> attributes = connectorObject.getAttributes(); + if (AttributeUtil.find(Uid.NAME, attributes) == null) { + attributes.add(connectorObject.getUid()); + } + if (AttributeUtil.find(Name.NAME, attributes) == null) { + attributes.add(connectorObject.getName()); + } + + connObjectTO = ConnObjectUtils.getConnObjectTO(attributes); + } + + return connObjectTO; + } + + @PreAuthorize("hasRole('" + StandardEntitlement.RESOURCE_GET_CONNOBJECT + "')") + public ReconciliationStatus status(final AnyTypeKind anyTypeKind, final String anyKey, final String resourceKey) { + Pair<Any<?>, Provision> init = init(anyTypeKind, anyKey, resourceKey); + + ReconciliationStatus status = new ReconciliationStatus(); + status.setOnSyncope(getOnSyncope(init.getLeft(), init.getRight(), resourceKey)); + status.setOnResource(getOnResource(init.getLeft(), init.getRight())); + + return status; + } + + @PreAuthorize("hasRole('" + StandardEntitlement.TASK_EXECUTE + "')") + public void reconcile(final ReconciliationRequest request) { + Pair<Any<?>, Provision> init = init(request.getAnyTypeKind(), request.getAnyKey(), request.getResourceKey()); + + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Reconciliation); + try { + List<ProvisioningReport> results = null; + switch (request.getAction()) { + case PUSH: + results = singlePushExecutor.push( + init.getRight(), + connFactory.getConnector(init.getRight().getResource()), + init.getLeft(), + request.getActions()); + break; + + case PULL: + results = singlePullExecutor.pull( + init.getRight(), + connFactory.getConnector(init.getRight().getResource()), + init.getRight().getMapping().getConnObjectKeyItem().get().getExtAttrName(), + mappingManager.getConnObjectKeyValue(init.getLeft(), init.getRight()).get(), + realmDAO.findByFullPath(init.getLeft().getRealm().getFullPath()), + request.isRemediation(), + request.getActions()); + break; + + default: + } + + if (results != null && !results.isEmpty() + && results.get(0).getStatus() == ProvisioningReport.Status.FAILURE) { + + sce.getElements().add(results.get(0).getMessage()); + } + } catch (JobExecutionException e) { + sce.getElements().add(e.getMessage()); + } + + if (!sce.isEmpty()) { + throw sce; + } + } + + @Override + protected AbstractBaseBean resolveReference(final Method method, final Object... os) + throws UnresolvedReferenceException { + + throw new UnresolvedReferenceException(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java ---------------------------------------------------------------------- diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java index 09079da..e59350e 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java @@ -22,11 +22,12 @@ import java.util.Set; import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; +import org.apache.syncope.core.persistence.api.dao.AnyDAO; import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource; public interface AnyUtils { - AnyTypeKind getAnyTypeKind(); + AnyTypeKind anyTypeKind(); <T extends Any<?>> Class<T> anyClass(); @@ -45,9 +46,11 @@ public interface AnyUtils { <T extends PlainAttrValue> T newPlainAttrUniqueValue(); <T extends PlainAttrValue> T clonePlainAttrValue(T src); - + <T extends AnyTO> T newAnyTO(); + <A extends Any<?>> AnyDAO<A> dao(); + Set<ExternalResource> getAllResources(Any<?> any); <S extends Schema> AllowedSchemas<S> getAllowedSchemas(Any<?> any, Class<S> reference); http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java index abba39c..64550a7 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java @@ -92,8 +92,8 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj @Transactional(readOnly = true) @Override - public String findKey(final String username) { - return findKey(username, JPAAnyObject.TABLE); + public String findKey(final String name) { + return findKey(name, JPAAnyObject.TABLE); } @Transactional(readOnly = true) http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java ---------------------------------------------------------------------- diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java index b687aa1..15fa17c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java @@ -32,6 +32,7 @@ import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; +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.UserDAO; @@ -113,7 +114,7 @@ public class JPAAnyUtils implements AnyUtils { } @Override - public AnyTypeKind getAnyTypeKind() { + public AnyTypeKind anyTypeKind() { return anyTypeKind; } @@ -337,6 +338,29 @@ public class JPAAnyUtils implements AnyUtils { return result; } + @Override + public <A extends Any<?>> AnyDAO<A> dao() { + AnyDAO<A> result = null; + + switch (anyTypeKind) { + case USER: + result = (AnyDAO<A>) userDAO; + break; + + case GROUP: + result = (AnyDAO<A>) groupDAO; + break; + + case ANY_OBJECT: + result = (AnyDAO<A>) anyObjectDAO; + break; + + default: + } + + return result; + } + @Transactional(readOnly = true) @Override public Set<ExternalResource> getAllResources(final Any<?> any) { http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java new file mode 100644 index 0000000..bbf8430 --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java @@ -0,0 +1,37 @@ +/* + * 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.pushpull; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.provisioning.api.Connector; +import org.quartz.JobExecutionException; + +public interface SyncopeSinglePullExecutor { + + List<ProvisioningReport> pull( + Provision provision, + Connector connector, + String connObjectKey, + String connObjectValue, + Realm realm, + boolean remediation, + List<String> actions) throws JobExecutionException; +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java ---------------------------------------------------------------------- diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java new file mode 100644 index 0000000..9068301 --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java @@ -0,0 +1,34 @@ +/* + * 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.pushpull; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.provisioning.api.Connector; +import org.quartz.JobExecutionException; + +public interface SyncopeSinglePushExecutor { + + List<ProvisioningReport> push( + Provision provision, + Connector connector, + Any<?> any, + List<String> actions) throws JobExecutionException; +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java index 70a8647..83d8e76 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java @@ -674,7 +674,7 @@ public class MappingManagerImpl implements MappingManager { IntAttrName intAttrName; try { - intAttrName = intAttrNameParser.parse(mapItem.getIntAttrName(), anyUtils.getAnyTypeKind()); + intAttrName = intAttrNameParser.parse(mapItem.getIntAttrName(), anyUtils.anyTypeKind()); } catch (ParseException e) { LOG.error("Invalid intAttrName '{}' specified, ignoring", mapItem.getIntAttrName(), e); return; http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/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 3abbd84..35e0356 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 @@ -171,7 +171,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan ProvisioningReport ignoreResult = new ProvisioningReport(); ignoreResult.setOperation(ResourceOperation.NONE); ignoreResult.setAnyType(provision == null - ? getAnyUtils().getAnyTypeKind().name() : provision.getAnyType().getKey()); + ? getAnyUtils().anyTypeKind().name() : provision.getAnyType().getKey()); ignoreResult.setStatus(ProvisioningReport.Status.IGNORE); ignoreResult.setKey(null); ignoreResult.setName(delta.getObject().getName().getNameValue()); @@ -914,7 +914,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan } notificationManager.createTasks(AuditElements.EventCategoryType.PULL, - getAnyUtils().getAnyTypeKind().name().toLowerCase(), + getAnyUtils().anyTypeKind().name().toLowerCase(), profile.getTask().getResource().getKey(), event, result, @@ -924,7 +924,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan furtherInput); auditManager.audit(AuditElements.EventCategoryType.PULL, - getAnyUtils().getAnyTypeKind().name().toLowerCase(), + getAnyUtils().anyTypeKind().name().toLowerCase(), profile.getTask().getResource().getKey(), event, result, http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java index 5f74388..63f397b 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java @@ -256,7 +256,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan : null; LOG.debug("Propagating {} with key {} towards {}", - anyUtils.getAnyTypeKind(), any.getKey(), profile.getTask().getResource()); + anyUtils.anyTypeKind(), any.getKey(), profile.getTask().getResource()); Object output = null; Result resultStatus = null; http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java index 9edfd6b..f8b73a2 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java @@ -80,14 +80,6 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i protected ProvisioningProfile<PullTask, PullActions> profile; - protected RealmPullResultHandler rhandler; - - protected AnyObjectPullResultHandler ahandler; - - protected UserPullResultHandler uhandler; - - protected GroupPullResultHandler ghandler; - @Override public void setLatestSyncToken(final ObjectClass objectClass, final SyncToken latestSyncToken) { latestSyncTokens.put(objectClass, latestSyncToken); @@ -168,30 +160,18 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i } protected RealmPullResultHandler buildRealmHandler() { - RealmPullResultHandler handler = (RealmPullResultHandler) ApplicationContextProvider.getBeanFactory(). + return (RealmPullResultHandler) ApplicationContextProvider.getBeanFactory(). createBean(DefaultRealmPullResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - handler.setProfile(profile); - handler.setPullExecutor(this); - - return handler; } protected AnyObjectPullResultHandler buildAnyObjectHandler() { - AnyObjectPullResultHandler handler = (AnyObjectPullResultHandler) ApplicationContextProvider.getBeanFactory(). + return (AnyObjectPullResultHandler) ApplicationContextProvider.getBeanFactory(). createBean(DefaultAnyObjectPullResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - handler.setProfile(profile); - handler.setPullExecutor(this); - - return handler; } protected UserPullResultHandler buildUserHandler() { - UserPullResultHandler handler = (UserPullResultHandler) ApplicationContextProvider.getBeanFactory(). + return (UserPullResultHandler) ApplicationContextProvider.getBeanFactory(). createBean(DefaultUserPullResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - handler.setProfile(profile); - handler.setPullExecutor(this); - - return handler; } protected GroupPullResultHandler buildGroupHandler() { @@ -245,7 +225,9 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i OperationOptions options = MappingUtils.buildOperationOptions( MappingUtils.getPullItems(orgUnit.getItems()).iterator()); - rhandler = buildRealmHandler(); + RealmPullResultHandler handler = buildRealmHandler(); + handler.setProfile(profile); + handler.setPullExecutor(this); try { switch (pullTask.getPullMode()) { @@ -257,7 +239,7 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i connector.sync( orgUnit.getObjectClass(), orgUnit.getSyncToken(), - rhandler, + handler, options); if (!dryRun) { @@ -271,14 +253,14 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i ImplementationManager.build(pullTask.getReconFilterBuilder()); connector.filteredReconciliation(orgUnit.getObjectClass(), filterBuilder, - rhandler, + handler, options); break; case FULL_RECONCILIATION: default: connector.fullReconciliation(orgUnit.getObjectClass(), - rhandler, + handler, options); break; } @@ -288,18 +270,15 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i } // ...then provisions for any types - ahandler = buildAnyObjectHandler(); - uhandler = buildUserHandler(); - ghandler = buildGroupHandler(); - + SyncopePullResultHandler handler; + GroupPullResultHandler ghandler = buildGroupHandler(); for (Provision provision : pullTask.getResource().getProvisions()) { if (provision.getMapping() != null) { status.set("Pulling " + provision.getObjectClass().getObjectClassValue()); - SyncopePullResultHandler handler; switch (provision.getAnyType().getKind()) { case USER: - handler = uhandler; + handler = buildUserHandler(); break; case GROUP: @@ -308,8 +287,10 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i case ANY_OBJECT: default: - handler = ahandler; + handler = buildAnyObjectHandler(); } + handler.setProfile(profile); + handler.setPullExecutor(this); try { Set<MappingItem> linkingMappingItems = virSchemaDAO.findByProvision(provision).stream(). http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java index 77ca4e1..941e5e4 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java @@ -26,11 +26,10 @@ import java.util.Optional; import java.util.stream.Collectors; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; -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.AnySearchDAO; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.Any; @@ -148,10 +147,10 @@ public class PullUtils { try { List<String> anyKeys = match(connObj, provision.get(), anyUtils); if (anyKeys.isEmpty()) { - LOG.debug("No matching {} found for {}, aborting", anyUtils.getAnyTypeKind(), connObj); + LOG.debug("No matching {} found for {}, aborting", anyUtils.anyTypeKind(), connObj); } else { if (anyKeys.size() > 1) { - LOG.warn("More than one {} found {} - taking first only", anyUtils.getAnyTypeKind(), anyKeys); + LOG.warn("More than one {} found {} - taking first only", anyUtils.anyTypeKind(), anyKeys); } result = Optional.ofNullable(anyKeys.iterator().next()); @@ -164,14 +163,6 @@ public class PullUtils { return result; } - private AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) { - return AnyTypeKind.USER == anyTypeKind - ? userDAO - : AnyTypeKind.ANY_OBJECT == anyTypeKind - ? anyObjectDAO - : groupDAO; - } - private List<String> findByConnObjectKey( final ConnectorObject connObj, final Provision provision, final AnyUtils anyUtils) { @@ -213,7 +204,7 @@ public class PullUtils { if (intAttrName.getField() != null) { switch (intAttrName.getField()) { case "key": - Any<?> any = getAnyDAO(provision.getAnyType().getKind()).find(connObjectKey); + Any<?> any = anyUtils.dao().find(connObjectKey); if (any != null) { result.add(any.getKey()); } @@ -256,13 +247,13 @@ public class PullUtils { } } - result.addAll(getAnyDAO(provision.getAnyType().getKind()). + result.addAll(anyUtils.dao(). findByPlainAttrValue(intAttrName.getSchemaName(), value).stream(). map(Entity::getKey).collect(Collectors.toList())); break; case DERIVED: - result.addAll(getAnyDAO(provision.getAnyType().getKind()). + result.addAll(anyUtils.dao(). findByDerAttrValue(intAttrName.getSchemaName(), connObjectKey).stream(). map(Entity::getKey).collect(Collectors.toList())); break; @@ -312,7 +303,7 @@ public class PullUtils { try { return rule.isPresent() - ? findByCorrelationRule(connObj, provision, rule.get(), anyUtils.getAnyTypeKind()) + ? findByCorrelationRule(connObj, provision, rule.get(), anyUtils.anyTypeKind()) : findByConnObjectKey(connObj, provision, anyUtils); } catch (RuntimeException e) { LOG.error("Could not match {} with any existing {}", connObj, provision.getAnyType(), e); http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java index b813e71..2207f42 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java @@ -27,18 +27,15 @@ import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.spring.ApplicationContextProvider; 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.AnySearchDAO; -import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; -import org.apache.syncope.core.persistence.api.dao.UserDAO; 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.Any; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.Group; @@ -63,41 +60,21 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> { /** - * User DAO. - */ - @Autowired - protected UserDAO userDAO; - - /** * Search DAO. */ @Autowired protected AnySearchDAO searchDAO; - /** - * Group DAO. - */ @Autowired - protected GroupDAO groupDAO; - - @Autowired - protected AnyObjectDAO anyObjectDAO; + protected RealmDAO realmDAO; @Autowired - protected RealmDAO realmDAO; + protected AnyUtilsFactory anyUtilsFactory; protected ProvisioningProfile<PushTask, PushActions> profile; protected final Map<String, MutablePair<Integer, String>> handled = new HashMap<>(); - protected RealmPushResultHandler rhandler; - - protected AnyObjectPushResultHandler ahandler; - - protected UserPushResultHandler uhandler; - - protected GroupPushResultHandler ghandler; - protected void reportHandled(final String anyType, final String key) { MutablePair<Integer, String> pair = handled.get(anyType); if (pair == null) { @@ -125,25 +102,6 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> { return status.get(); } - protected AnyDAO<?> getAnyDAO(final AnyTypeKind anyTypeKind) { - AnyDAO<?> result; - switch (anyTypeKind) { - case USER: - result = userDAO; - break; - - case GROUP: - result = groupDAO; - break; - - case ANY_OBJECT: - default: - result = anyObjectDAO; - } - - return result; - } - protected void doHandle( final List<? extends Any<?>> anys, final SyncopePushResultHandler handler, @@ -168,35 +126,23 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> { } protected RealmPushResultHandler buildRealmHandler() { - RealmPushResultHandler handler = (RealmPushResultHandler) ApplicationContextProvider.getBeanFactory(). + return (RealmPushResultHandler) ApplicationContextProvider.getBeanFactory(). createBean(DefaultRealmPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - handler.setProfile(profile); - - return handler; } protected AnyObjectPushResultHandler buildAnyObjectHandler() { - AnyObjectPushResultHandler handler = (AnyObjectPushResultHandler) ApplicationContextProvider.getBeanFactory(). + return (AnyObjectPushResultHandler) ApplicationContextProvider.getBeanFactory(). createBean(DefaultAnyObjectPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - handler.setProfile(profile); - - return handler; } protected UserPushResultHandler buildUserHandler() { - UserPushResultHandler handler = (UserPushResultHandler) ApplicationContextProvider.getBeanFactory(). + return (UserPushResultHandler) ApplicationContextProvider.getBeanFactory(). createBean(DefaultUserPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - handler.setProfile(profile); - - return handler; } protected GroupPushResultHandler buildGroupHandler() { - GroupPushResultHandler handler = (GroupPushResultHandler) ApplicationContextProvider.getBeanFactory(). + return (GroupPushResultHandler) ApplicationContextProvider.getBeanFactory(). createBean(DefaultGroupPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); - handler.setProfile(profile); - - return handler; } @Override @@ -233,13 +179,14 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> { if (pushTask.getResource().getOrgUnit() != null) { status.set("Pushing realms"); - rhandler = buildRealmHandler(); + RealmPushResultHandler handler = buildRealmHandler(); + handler.setProfile(profile); for (Realm realm : realmDAO.findDescendants(profile.getTask().getSourceRealm())) { // Never push the root realm if (realm.getParent() != null) { try { - rhandler.handle(realm.getKey()); + handler.handle(realm.getKey()); reportHandled(SyncopeConstants.REALM_ANYTYPE, realm.getName()); } catch (Exception e) { LOG.warn("Failure pushing '{}' on '{}'", realm, pushTask.getResource(), e); @@ -250,30 +197,27 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> { } // ...then provisions for any types - ahandler = buildAnyObjectHandler(); - uhandler = buildUserHandler(); - ghandler = buildGroupHandler(); - for (Provision provision : pushTask.getResource().getProvisions()) { if (provision.getMapping() != null) { status.set("Pushing " + provision.getAnyType().getKey()); - AnyDAO<?> anyDAO = getAnyDAO(provision.getAnyType().getKind()); + AnyDAO<?> anyDAO = anyUtilsFactory.getInstance(provision.getAnyType().getKind()).dao(); SyncopePushResultHandler handler; switch (provision.getAnyType().getKind()) { case USER: - handler = uhandler; + handler = buildUserHandler(); break; case GROUP: - handler = ghandler; + handler = buildGroupHandler(); break; case ANY_OBJECT: default: - handler = ahandler; + handler = buildAnyObjectHandler(); } + handler.setProfile(profile); Optional<? extends PushTaskAnyFilter> anyFilter = pushTask.getFilter(provision.getAnyType()); String filter = anyFilter.isPresent() http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java new file mode 100644 index 0000000..f4f57b4 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java @@ -0,0 +1,174 @@ +/* + * 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.pushpull; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.syncope.common.lib.collections.IteratorChain; +import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.common.lib.types.ImplementationType; +import org.apache.syncope.common.lib.types.MatchingRule; +import org.apache.syncope.common.lib.types.PullMode; +import org.apache.syncope.common.lib.types.UnmatchingRule; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; +import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecutor; +import org.apache.syncope.core.provisioning.java.utils.MappingUtils; +import org.apache.syncope.core.spring.ImplementationManager; +import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.OperationOptions; +import org.identityconnectors.framework.common.objects.filter.Filter; +import org.identityconnectors.framework.common.objects.filter.FilterBuilder; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSinglePullExecutor { + + @Autowired + private ImplementationDAO implementationDAO; + + @Override + public List<ProvisioningReport> pull( + final Provision provision, + final Connector connector, + final String connObjectKey, + final String connObjectValue, + final Realm realm, + final boolean remediation, + final List<String> actionKeys) throws JobExecutionException { + + LOG.debug("Executing pull on {}", provision.getResource()); + + List<PullActions> actions = new ArrayList<>(); + actionKeys.forEach(key -> { + Implementation impl = implementationDAO.find(key); + if (impl == null || impl.getType() != ImplementationType.PULL_ACTIONS) { + LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", key); + } else { + try { + actions.add(ImplementationManager.build(impl)); + } catch (Exception e) { + LOG.warn("While building {}", impl, e); + } + } + }); + + try { + Set<MappingItem> linkinMappingItems = virSchemaDAO.findByProvision(provision).stream(). + map(virSchema -> virSchema.asLinkingMappingItem()).collect(Collectors.toSet()); + Iterator<MappingItem> mapItems = new IteratorChain<>( + provision.getMapping().getItems().iterator(), + linkinMappingItems.iterator()); + OperationOptions options = MappingUtils.buildOperationOptions(mapItems); + + PullTask pullTask = entityFactory.newEntity(PullTask.class); + pullTask.setResource(provision.getResource()); + pullTask.setMatchingRule(MatchingRule.UPDATE); + pullTask.setUnmatchingRule(UnmatchingRule.PROVISION); + pullTask.setPullMode(PullMode.FILTERED_RECONCILIATION); + pullTask.setPerformCreate(true); + pullTask.setPerformUpdate(true); + pullTask.setRemediation(remediation); + pullTask.setDestinationRealm(realm); + + profile = new ProvisioningProfile<>(connector, pullTask); + profile.setDryRun(false); + profile.setResAct(ConflictResolutionAction.FIRSTMATCH); + profile.getActions().addAll(actions); + + for (PullActions action : actions) { + action.beforeAll(profile); + } + + SyncopePullResultHandler handler; + GroupPullResultHandler ghandler = buildGroupHandler(); + switch (provision.getAnyType().getKind()) { + case USER: + handler = buildUserHandler(); + break; + + case GROUP: + handler = ghandler; + break; + + case ANY_OBJECT: + default: + handler = buildAnyObjectHandler(); + } + handler.setProfile(profile); + handler.setPullExecutor(this); + + // execute filtered pull + connector.filteredReconciliation( + provision.getObjectClass(), + new AccountReconciliationFilterBuilder(connObjectKey, connObjectValue), + handler, + options); + + try { + setGroupOwners(ghandler); + } catch (Exception e) { + LOG.error("While setting group owners", e); + } + + for (PullActions action : actions) { + action.afterAll(profile); + } + + return profile.getResults(); + } catch (Exception e) { + throw e instanceof JobExecutionException + ? (JobExecutionException) e + : new JobExecutionException("While pulling from connector", e); + } + } + + class AccountReconciliationFilterBuilder implements ReconFilterBuilder { + + private final String key; + + private final String value; + + AccountReconciliationFilterBuilder(final String key, final String value) { + this.key = key; + this.value = value; + } + + @Override + public Filter build() { + return FilterBuilder.equalTo(AttributeBuilder.build(key, value)); + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java new file mode 100644 index 0000000..76bdc16 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java @@ -0,0 +1,117 @@ +/* + * 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.pushpull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.syncope.common.lib.types.ImplementationType; +import org.apache.syncope.common.lib.types.MatchingRule; +import org.apache.syncope.common.lib.types.UnmatchingRule; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.resource.Provision; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; +import org.apache.syncope.core.provisioning.api.pushpull.PushActions; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePushExecutor; +import org.apache.syncope.core.spring.ImplementationManager; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SinglePushJobDelegate extends PushJobDelegate implements SyncopeSinglePushExecutor { + + @Autowired + private ImplementationDAO implementationDAO; + + @Override + public List<ProvisioningReport> push( + final Provision provision, + final Connector connector, + final Any<?> any, + final List<String> actionKeys) throws JobExecutionException { + + LOG.debug("Executing push on {}", provision.getResource()); + + List<PushActions> actions = new ArrayList<>(); + actionKeys.forEach(key -> { + Implementation impl = implementationDAO.find(key); + if (impl == null || impl.getType() != ImplementationType.PUSH_ACTIONS) { + LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", key); + } else { + try { + actions.add(ImplementationManager.build(impl)); + } catch (Exception e) { + LOG.warn("While building {}", impl, e); + } + } + }); + + try { + PushTask pushTask = entityFactory.newEntity(PushTask.class); + pushTask.setResource(provision.getResource()); + pushTask.setMatchingRule(MatchingRule.UPDATE); + pushTask.setUnmatchingRule(UnmatchingRule.PROVISION); + pushTask.setPerformCreate(true); + pushTask.setPerformUpdate(true); + + profile = new ProvisioningProfile<>(connector, pushTask); + profile.getActions().addAll(actions); + profile.setResAct(null); + + for (PushActions action : actions) { + action.beforeAll(profile); + } + + SyncopePushResultHandler handler; + switch (provision.getAnyType().getKind()) { + case USER: + handler = buildUserHandler(); + break; + + case GROUP: + handler = buildGroupHandler(); + break; + + case ANY_OBJECT: + default: + handler = buildAnyObjectHandler(); + } + handler.setProfile(profile); + + doHandle(Arrays.asList(any), handler, pushTask.getResource()); + + for (PushActions action : actions) { + action.afterAll(profile); + } + + return profile.getResults(); + } catch (Exception e) { + throw e instanceof JobExecutionException + ? (JobExecutionException) e + : new JobExecutionException("While pushing to connector", e); + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java index ad9cbed..63e44cb 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java @@ -242,8 +242,8 @@ public class ConnObjectUtils { updated.setKey(key); T anyPatch = null; - if (null != anyUtils.getAnyTypeKind()) { - switch (anyUtils.getAnyTypeKind()) { + if (null != anyUtils.anyTypeKind()) { + switch (anyUtils.anyTypeKind()) { case USER: UserTO originalUser = (UserTO) original; UserTO updatedUser = (UserTO) updated; http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java index 92d2aad..9d028e4 100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java @@ -64,18 +64,6 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch> protected abstract P newPatch(String key); - protected String getActualKey(final String key) { - String actualKey = key; - if (!SyncopeConstants.UUID_PATTERN.matcher(key).matches()) { - actualKey = getAnyDAO().findKey(key); - if (actualKey == null) { - throw new NotFoundException("User, Group or Any Object for " + key); - } - } - - return actualKey; - } - @Override public Set<AttrTO> read(final String key, final SchemaType schemaType) { TO any = read(key); @@ -124,7 +112,7 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch> @Override public TO read(final String key) { - return getAnyLogic().read(getActualKey(key)); + return getAnyLogic().read(getActualKey(getAnyDAO(), key)); } @Override @@ -161,7 +149,7 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch> } protected Response doUpdate(final P anyPatch) { - anyPatch.setKey(getActualKey(anyPatch.getKey())); + anyPatch.setKey(getActualKey(getAnyDAO(), anyPatch.getKey())); Date etagDate = findLastChange(anyPatch.getKey()); checkETag(String.valueOf(etagDate.getTime())); @@ -196,21 +184,23 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch> @Override public Response update(final String key, final SchemaType schemaType, final AttrTO attrTO) { - String actualKey = getActualKey(key); + String actualKey = getActualKey(getAnyDAO(), key); addUpdateOrReplaceAttr(actualKey, schemaType, attrTO, PatchOperation.ADD_REPLACE); return modificationResponse(read(actualKey, schemaType, attrTO.getSchema())); } @Override public void delete(final String key, final SchemaType schemaType, final String schema) { - String actualKey = getActualKey(key); addUpdateOrReplaceAttr( - actualKey, schemaType, new AttrTO.Builder().schema(schema).build(), PatchOperation.DELETE); + getActualKey(getAnyDAO(), key), + schemaType, + new AttrTO.Builder().schema(schema).build(), + PatchOperation.DELETE); } @Override public Response delete(final String key) { - String actualKey = getActualKey(key); + String actualKey = getActualKey(getAnyDAO(), key); Date etagDate = findLastChange(actualKey); checkETag(String.valueOf(etagDate.getTime())); http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java index a040960..caef5d8 100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java @@ -37,12 +37,15 @@ import org.apache.cxf.jaxrs.ext.search.SearchCondition; import org.apache.cxf.jaxrs.ext.search.SearchContext; import org.apache.syncope.common.lib.AbstractBaseBean; import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.PagedResult; import org.apache.syncope.common.lib.to.ProvisioningResult; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.rest.api.service.JAXRSService; import org.apache.syncope.common.rest.api.Preference; import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.core.persistence.api.dao.AnyDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; @@ -64,6 +67,18 @@ abstract class AbstractServiceImpl implements JAXRSService { @Context protected SearchContext searchContext; + protected String getActualKey(final AnyDAO<?> dao, final String pretendingKey) { + String actualKey = pretendingKey; + if (!SyncopeConstants.UUID_PATTERN.matcher(pretendingKey).matches()) { + actualKey = dao.findKey(pretendingKey); + if (actualKey == null) { + throw new NotFoundException("User, Group or Any Object for " + pretendingKey); + } + } + + return actualKey; + } + protected boolean isNullPriorityAsync() { return BooleanUtils.toBoolean(messageContext.getHttpHeaders().getHeaderString(RESTHeaders.NULL_PRIORITY_ASYNC)); } http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java index 961a328..3a7d40b 100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java @@ -71,7 +71,7 @@ public class AnyObjectServiceImpl extends AbstractAnyService<AnyObjectTO, AnyObj @Override public Response update(final AnyObjectTO anyObjectTO) { - anyObjectTO.setKey(getActualKey(anyObjectTO.getKey())); + anyObjectTO.setKey(getActualKey(getAnyDAO(), anyObjectTO.getKey())); AnyObjectTO before = logic.read(anyObjectTO.getKey()); checkETag(before.getETagValue()); http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java index 4e23d22..bc621e9 100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/GroupServiceImpl.java @@ -68,7 +68,7 @@ public class GroupServiceImpl extends AbstractAnyService<GroupTO, GroupPatch> im @Override public Response update(final GroupTO groupTO) { - groupTO.setKey(getActualKey(groupTO.getKey())); + groupTO.setKey(getActualKey(getAnyDAO(), groupTO.getKey())); GroupTO before = logic.read(groupTO.getKey()); checkETag(before.getETagValue()); http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceImpl.java new file mode 100644 index 0000000..80d89d5 --- /dev/null +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceImpl.java @@ -0,0 +1,53 @@ +/* + * 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 org.apache.syncope.common.lib.to.ReconciliationRequest; +import org.apache.syncope.common.lib.to.ReconciliationStatus; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.rest.api.service.ReconciliationService; +import org.apache.syncope.core.logic.ReconciliationLogic; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ReconciliationServiceImpl extends AbstractServiceImpl implements ReconciliationService { + + @Autowired + private ReconciliationLogic logic; + + @Autowired + private AnyUtilsFactory anyUtilsFactory; + + @Override + public ReconciliationStatus status(final AnyTypeKind anyTypeKind, final String anyKey, final String resourceKey) { + return logic.status( + anyTypeKind, + getActualKey(anyUtilsFactory.getInstance(anyTypeKind).dao(), anyKey), + resourceKey); + } + + @Override + public void reconcile(final ReconciliationRequest request) { + request.setAnyKey( + getActualKey(anyUtilsFactory.getInstance(request.getAnyTypeKind()).dao(), request.getAnyKey())); + logic.reconcile(request); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java ---------------------------------------------------------------------- diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java index 4af144b..99dc24d 100644 --- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java +++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserServiceImpl.java @@ -67,7 +67,7 @@ public class UserServiceImpl extends AbstractAnyService<UserTO, UserPatch> imple @Override public Response update(final UserTO userTO) { - userTO.setKey(getActualKey(userTO.getKey())); + userTO.setKey(getActualKey(getAnyDAO(), userTO.getKey())); UserTO before = logic.read(userTO.getKey()); checkETag(before.getETagValue()); http://git-wip-us.apache.org/repos/asf/syncope/blob/e5860a76/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 3151971..759f5e4 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 @@ -85,6 +85,7 @@ import org.apache.syncope.common.rest.api.service.GroupService; 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.ReconciliationService; 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; @@ -228,6 +229,8 @@ public abstract class AbstractITCase { protected static TaskService taskService; + protected static ReconciliationService reconciliationService; + protected static WorkflowService workflowService; protected static MailTemplateService mailTemplateService; @@ -307,6 +310,7 @@ public abstract class AbstractITCase { reportTemplateService = adminClient.getService(ReportTemplateService.class); reportService = adminClient.getService(ReportService.class); taskService = adminClient.getService(TaskService.class); + reconciliationService = adminClient.getService(ReconciliationService.class); policyService = adminClient.getService(PolicyService.class); workflowService = adminClient.getService(WorkflowService.class); mailTemplateService = adminClient.getService(MailTemplateService.class);