| /* |
| * 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.io.InputStream; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Optional; |
| import java.util.Set; |
| import javax.validation.ValidationException; |
| import javax.ws.rs.core.HttpHeaders; |
| import javax.ws.rs.core.Response; |
| import javax.ws.rs.core.StreamingOutput; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.lang3.exception.ExceptionUtils; |
| import org.apache.commons.lang3.tuple.Pair; |
| import org.apache.cxf.jaxrs.ext.search.SearchBean; |
| import org.apache.cxf.jaxrs.ext.search.SearchCondition; |
| import org.apache.syncope.common.lib.SyncopeClientException; |
| import org.apache.syncope.common.lib.SyncopeConstants; |
| import org.apache.syncope.common.lib.to.ProvisioningReport; |
| import org.apache.syncope.common.lib.to.PullTaskTO; |
| import org.apache.syncope.common.lib.to.PushTaskTO; |
| import org.apache.syncope.common.lib.to.ReconStatus; |
| import org.apache.syncope.common.lib.types.ClientExceptionType; |
| import org.apache.syncope.common.rest.api.RESTHeaders; |
| import org.apache.syncope.common.rest.api.beans.AnyQuery; |
| import org.apache.syncope.common.rest.api.beans.CSVPullSpec; |
| import org.apache.syncope.common.rest.api.beans.CSVPushSpec; |
| import org.apache.syncope.common.rest.api.beans.ReconQuery; |
| import org.apache.syncope.common.rest.api.service.ReconciliationService; |
| import org.apache.syncope.core.logic.ReconciliationLogic; |
| import org.apache.syncope.core.persistence.api.dao.search.SearchCond; |
| import org.apache.syncope.core.persistence.api.search.FilterVisitor; |
| import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; |
| import org.apache.syncope.core.spring.security.AuthContextUtils; |
| import org.identityconnectors.framework.common.objects.filter.Filter; |
| import org.springframework.stereotype.Service; |
| |
| @Service |
| public class ReconciliationServiceImpl extends AbstractSearchService implements ReconciliationService { |
| |
| protected final ReconciliationLogic logic; |
| |
| public ReconciliationServiceImpl(final SearchCondVisitor searchCondVisitor, final ReconciliationLogic logic) { |
| super(searchCondVisitor); |
| this.logic = logic; |
| } |
| |
| private void validate(final ReconQuery reconQuery) { |
| if ((reconQuery.getAnyKey() == null && reconQuery.getFiql() == null) |
| || (reconQuery.getAnyKey() != null && reconQuery.getFiql() != null)) { |
| |
| throw new ValidationException("Either provide anyKey or fiql, not both"); |
| } |
| } |
| |
| private Pair<Filter, Set<String>> buildFromFIQL(final ReconQuery reconQuery) { |
| Filter filter = null; |
| Set<String> moreAttrsToGet = new HashSet<>(); |
| if (reconQuery.getMoreAttrsToGet() != null) { |
| moreAttrsToGet.addAll(reconQuery.getMoreAttrsToGet()); |
| } |
| if (StringUtils.isNotBlank(reconQuery.getFiql())) { |
| try { |
| FilterVisitor visitor = new FilterVisitor(); |
| SearchCondition<SearchBean> sc = searchContext.getCondition(reconQuery.getFiql(), SearchBean.class); |
| sc.accept(visitor); |
| |
| filter = visitor.getQuery(); |
| moreAttrsToGet.addAll(visitor.getAttrs()); |
| } catch (Exception e) { |
| LOG.error("Invalid FIQL expression: {}", reconQuery.getFiql(), e); |
| |
| SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters); |
| sce.getElements().add(reconQuery.getFiql()); |
| sce.getElements().add(ExceptionUtils.getRootCauseMessage(e)); |
| throw sce; |
| } |
| } |
| |
| return Pair.of(filter, moreAttrsToGet); |
| } |
| |
| @Override |
| public ReconStatus status(final ReconQuery query) { |
| validate(query); |
| |
| if (query.getAnyKey() != null) { |
| return logic.status( |
| query.getAnyTypeKey(), |
| query.getResourceKey(), |
| query.getAnyKey(), |
| Optional.ofNullable(query.getMoreAttrsToGet()).orElse(Set.of())); |
| } |
| |
| Pair<Filter, Set<String>> fromFIQL = buildFromFIQL(query); |
| return logic.status(query.getAnyTypeKey(), query.getResourceKey(), fromFIQL.getLeft(), fromFIQL.getRight()); |
| } |
| |
| @Override |
| public List<ProvisioningReport> push(final ReconQuery query, final PushTaskTO pushTask) { |
| validate(query); |
| |
| if (query.getAnyKey() != null) { |
| return logic.push(query.getAnyTypeKey(), query.getResourceKey(), query.getAnyKey(), pushTask); |
| } |
| |
| Pair<Filter, Set<String>> fromFIQL = buildFromFIQL(query); |
| return logic.push( |
| query.getAnyTypeKey(), query.getResourceKey(), fromFIQL.getLeft(), fromFIQL.getRight(), pushTask); |
| } |
| |
| @Override |
| public List<ProvisioningReport> pull(final ReconQuery query, final PullTaskTO pullTask) { |
| validate(query); |
| |
| if (query.getAnyKey() != null) { |
| return logic.pull( |
| query.getAnyTypeKey(), |
| query.getResourceKey(), |
| query.getAnyKey(), |
| Optional.ofNullable(query.getMoreAttrsToGet()).orElse(Set.of()), |
| pullTask); |
| } |
| |
| Pair<Filter, Set<String>> fromFIQL = buildFromFIQL(query); |
| return logic.pull( |
| query.getAnyTypeKey(), query.getResourceKey(), fromFIQL.getLeft(), fromFIQL.getRight(), pullTask); |
| } |
| |
| @Override |
| public Response push(final AnyQuery query, final CSVPushSpec spec) { |
| String realm = StringUtils.prependIfMissing(query.getRealm(), SyncopeConstants.ROOT_REALM); |
| |
| SearchCond searchCond = StringUtils.isBlank(query.getFiql()) |
| ? null |
| : getSearchCond(query.getFiql(), realm); |
| |
| StreamingOutput sout = os -> logic.push( |
| searchCond, |
| query.getPage(), |
| query.getSize(), |
| getOrderByClauses(query.getOrderBy()), |
| realm, |
| spec, |
| os); |
| |
| return Response.ok(sout). |
| type(RESTHeaders.TEXT_CSV). |
| header(HttpHeaders.CONTENT_DISPOSITION, |
| "attachment; filename=" + AuthContextUtils.getDomain() + ".csv"). |
| build(); |
| } |
| |
| @Override |
| public List<ProvisioningReport> pull(final CSVPullSpec spec, final InputStream csv) { |
| return logic.pull(spec, csv); |
| } |
| } |