blob: 4cb2c10e7607cbef742883d9ba0b437c4bd3dc9a [file] [log] [blame]
/*
* 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);
}
}