blob: 6629fc08d301a32233e716ae14a0cb111c987d4a [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.logic;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.core.HttpHeaders;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier;
import org.apache.syncope.common.keymaster.client.api.KeymasterException;
import org.apache.syncope.common.keymaster.client.api.ServiceOps;
import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.ClientAppTO;
import org.apache.syncope.common.lib.types.AMEntitlement;
import org.apache.syncope.common.lib.types.ClientAppType;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.auth.CASSPDAO;
import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPDAO;
import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtils;
import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtilsFactory;
import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
import org.apache.syncope.core.spring.security.SecurityProperties;
public class ClientAppLogic extends AbstractTransactionalLogic<ClientAppTO> {
protected final ServiceOps serviceOps;
protected final ClientAppUtilsFactory clientAppUtilsFactory;
protected final ClientAppDataBinder binder;
protected final SAML2SPDAO saml2spDAO;
protected final OIDCRPDAO oidcrpDAO;
protected final CASSPDAO casspDAO;
protected final SecurityProperties securityProperties;
public ClientAppLogic(
final ServiceOps serviceOps,
final ClientAppUtilsFactory clientAppUtilsFactory,
final ClientAppDataBinder binder,
final SAML2SPDAO saml2spDAO,
final OIDCRPDAO oidcrpDAO,
final CASSPDAO casspDAO,
final SecurityProperties securityProperties) {
this.serviceOps = serviceOps;
this.clientAppUtilsFactory = clientAppUtilsFactory;
this.binder = binder;
this.saml2spDAO = saml2spDAO;
this.oidcrpDAO = oidcrpDAO;
this.casspDAO = casspDAO;
this.securityProperties = securityProperties;
}
@PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_LIST + "')")
public <T extends ClientAppTO> List<T> list(final ClientAppType type) {
Stream<T> stream;
switch (type) {
case OIDCRP:
stream = oidcrpDAO.findAll().stream().map(binder::getClientAppTO);
break;
case CASSP:
stream = casspDAO.findAll().stream().map(binder::getClientAppTO);
break;
case SAML2SP:
default:
stream = saml2spDAO.findAll().stream().map(binder::getClientAppTO);
}
return stream.collect(Collectors.toList());
}
protected void checkType(final ClientAppType type, final ClientAppUtils clientAppUtils) {
if (clientAppUtils.getType() != type) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRequest);
sce.getElements().add("Found " + type + ", expected " + clientAppUtils.getType());
throw sce;
}
}
@PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_READ + "')")
@Transactional(readOnly = true)
public <T extends ClientAppTO> T read(final ClientAppType type, final String key) {
switch (type) {
case OIDCRP:
OIDCRPClientApp oidcrp = oidcrpDAO.find(key);
if (oidcrp == null) {
throw new NotFoundException("Client app " + key + " not found");
}
checkType(type, clientAppUtilsFactory.getInstance(oidcrp));
return binder.getClientAppTO(oidcrp);
case CASSP:
CASSPClientApp cassp = casspDAO.find(key);
if (cassp == null) {
throw new NotFoundException("Client app " + key + " not found");
}
checkType(type, clientAppUtilsFactory.getInstance(cassp));
return binder.getClientAppTO(cassp);
case SAML2SP:
default:
SAML2SPClientApp saml2sp = saml2spDAO.find(key);
if (saml2sp == null) {
throw new NotFoundException("Client app " + key + " not found");
}
checkType(type, clientAppUtilsFactory.getInstance(saml2sp));
return binder.getClientAppTO(saml2sp);
}
}
@PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_CREATE + "')")
public ClientAppTO create(final ClientAppType type, final ClientAppTO clientAppTO) {
checkType(type, clientAppUtilsFactory.getInstance(clientAppTO));
switch (type) {
case OIDCRP:
return binder.getClientAppTO(oidcrpDAO.save(binder.create(clientAppTO)));
case CASSP:
return binder.getClientAppTO(casspDAO.save(binder.create(clientAppTO)));
case SAML2SP:
default:
return binder.getClientAppTO(saml2spDAO.save(binder.create(clientAppTO)));
}
}
@PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_UPDATE + "')")
public void update(final ClientAppType type, final ClientAppTO clientAppTO) {
checkType(type, clientAppUtilsFactory.getInstance(clientAppTO));
switch (type) {
case OIDCRP:
OIDCRPClientApp oidcrp = oidcrpDAO.find(clientAppTO.getKey());
if (oidcrp == null) {
throw new NotFoundException("Client app " + clientAppTO.getKey() + " not found");
}
binder.update(oidcrp, clientAppTO);
oidcrpDAO.save(oidcrp);
break;
case CASSP:
CASSPClientApp cassp = casspDAO.find(clientAppTO.getKey());
if (cassp == null) {
throw new NotFoundException("Client app " + clientAppTO.getKey() + " not found");
}
binder.update(cassp, clientAppTO);
casspDAO.save(cassp);
break;
case SAML2SP:
default:
SAML2SPClientApp saml2sp = saml2spDAO.find(clientAppTO.getKey());
if (saml2sp == null) {
throw new NotFoundException("Client app " + clientAppTO.getKey() + " not found");
}
binder.update(saml2sp, clientAppTO);
saml2spDAO.save(saml2sp);
}
}
@PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_DELETE + "')")
public void delete(final ClientAppType type, final String key) {
switch (type) {
case OIDCRP:
OIDCRPClientApp oidcrp = oidcrpDAO.find(key);
if (oidcrp == null) {
throw new NotFoundException("Client app " + key + " not found");
}
oidcrpDAO.delete(oidcrp);
break;
case CASSP:
CASSPClientApp cassp = casspDAO.find(key);
if (cassp == null) {
throw new NotFoundException("Client app " + key + " not found");
}
casspDAO.delete(cassp);
break;
case SAML2SP:
default:
SAML2SPClientApp saml2sp = saml2spDAO.find(key);
if (saml2sp == null) {
throw new NotFoundException("Client app " + key + " not found");
}
saml2spDAO.delete(saml2sp);
}
}
@Override
protected ClientAppTO resolveReference(final Method method, final Object... args)
throws UnresolvedReferenceException {
String key = null;
if (ArrayUtils.isNotEmpty(args)) {
for (int i = 0; key == null && i < args.length; i++) {
if (args[i] instanceof String) {
key = (String) args[i];
} else if (args[i] instanceof ClientAppTO) {
key = ((ClientAppTO) args[i]).getKey();
}
}
}
if (key != null) {
try {
ClientApp clientApp = saml2spDAO.find(key);
if (clientApp == null) {
clientApp = oidcrpDAO.find(key);
}
return binder.getClientAppTO(clientApp);
} catch (Throwable ex) {
LOG.debug("Unresolved reference", ex);
throw new UnresolvedReferenceException(ex);
}
}
throw new UnresolvedReferenceException();
}
@PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_PUSH + "')")
public void pushToWA() {
try {
NetworkService wa = serviceOps.get(NetworkService.Type.WA);
HttpClient.newBuilder().build().send(
HttpRequest.newBuilder(URI.create(
StringUtils.appendIfMissing(wa.getAddress(), "/") + "actuator/registeredServices")).
header(HttpHeaders.AUTHORIZATION, DefaultBasicAuthSupplier.getBasicAuthHeader(
securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey())).
GET().build(),
HttpResponse.BodyHandlers.discarding());
} catch (KeymasterException e) {
throw new NotFoundException("Could not find any WA instance", e);
} catch (IOException | InterruptedException e) {
throw new InternalServerErrorException("Errors while communicating with WA instance", e);
}
}
}