You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2020/04/09 14:24:46 UTC
[syncope] 06/07: [SYNCOPE-160] RegisteredClientApp service for WA
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch SYNCOPE-160
in repository https://gitbox.apache.org/repos/asf/syncope.git
commit abd8829d89449025cd96df2589cd61ca4e36853a
Author: Dima Ayash <di...@apache.org>
AuthorDate: Thu Apr 9 13:46:23 2020 +0200
[SYNCOPE-160] RegisteredClientApp service for WA
---
.../common/lib/to/RegisteredClientAppTO.java | 71 ++++++
.../api/service/RegisteredClientAppService.java | 133 ++++++++++++
.../core/logic/RegisteredClientAppLogic.java | 238 +++++++++++++++++++++
.../service/RegisteredClientAppServiceImpl.java | 96 +++++++++
.../api/data/RegisteredClientAppBinder.java | 28 +++
.../java/data/RegisteredClientAppBinderImpl.java | 84 ++++++++
.../fit/core/RegisteredClientAppITCase.java | 114 ++++++++++
7 files changed, 764 insertions(+)
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/RegisteredClientAppTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/RegisteredClientAppTO.java
new file mode 100644
index 0000000..6b56357
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/RegisteredClientAppTO.java
@@ -0,0 +1,71 @@
+/*
+ * 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 java.io.Serializable;
+import org.apache.syncope.common.lib.policy.AccessPolicyConf;
+import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf;
+import org.apache.syncope.common.lib.policy.AuthPolicyConf;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+
+public class RegisteredClientAppTO implements Serializable {
+
+ private static final long serialVersionUID = 6633251825655119506L;
+
+ private ClientAppTO clientAppTO;
+
+ private AccessPolicyConf accessPolicyConf;
+
+ private AuthPolicyConf authPolicyConf;
+
+ private AttrReleasePolicyConf attrReleasePolicyConf;
+
+ public ClientAppTO getClientAppTO() {
+ return clientAppTO;
+ }
+
+ public void setClientAppTO(final ClientAppTO clientAppTO) {
+ this.clientAppTO = clientAppTO;
+ }
+
+ public AccessPolicyConf getAccessPolicyConf() {
+ return accessPolicyConf;
+ }
+
+ public void setAccessPolicyConf(final AccessPolicyConf accessPolicyConf) {
+ this.accessPolicyConf = accessPolicyConf;
+ }
+
+ public AuthPolicyConf getAuthPolicyConf() {
+ return authPolicyConf;
+ }
+
+ public void setAuthPolicyConf(final AuthPolicyConf authPolicyConf) {
+ this.authPolicyConf = authPolicyConf;
+ }
+
+ public AttrReleasePolicyConf getAttrReleasePolicyConf() {
+ return attrReleasePolicyConf;
+ }
+
+ public void setAttrReleasePolicyConf(final AttrReleasePolicyConf attrReleasePolicyConf) {
+ this.attrReleasePolicyConf = attrReleasePolicyConf;
+ }
+
+}
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RegisteredClientAppService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RegisteredClientAppService.java
new file mode 100644
index 0000000..cd55423
--- /dev/null
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RegisteredClientAppService.java
@@ -0,0 +1,133 @@
+/*
+ * 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.headers.Header;
+import io.swagger.v3.oas.annotations.media.Schema;
+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 org.apache.syncope.common.rest.api.RESTHeaders;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import java.util.List;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
+
+/**
+ * REST operations for resgistered client applications.
+ */
+@Tag(name = "RegisteredClientApps")
+@SecurityRequirements({
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer") })
+@Path("registeredClientApps")
+public interface RegisteredClientAppService extends JAXRSService {
+
+ /**
+ * Returns a list of all client applications to be registered.
+ *
+ * @return list of all client applications.
+ */
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ List<RegisteredClientAppTO> list();
+
+ /**
+ * Returns a client application with matching key.
+ *
+ * @param clientAppId registered client application ID to be read
+ * @return registered client application with matching id
+ */
+ @GET
+ @Path("{clientAppId}")
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ RegisteredClientAppTO read(@NotNull @PathParam("clientAppId") Long clientAppId);
+
+ @GET
+ @Path("{clientAppId}/{type}")
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ RegisteredClientAppTO read(
+ @NotNull @PathParam("clientAppId") Long clientAppId,
+ @NotNull @PathParam("type") ClientAppType type);
+
+ /**
+ * Returns a client application with matching key.
+ *
+ * @param name registered client application name to be read
+ * @return registered client application with matching name
+ */
+ @GET
+ @Path("/name/{name}")
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ RegisteredClientAppTO read(@NotNull @PathParam("name") String name);
+
+ @GET
+ @Path("/name/{name}/{type}")
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ RegisteredClientAppTO read(
+ @NotNull @PathParam("name") String name,
+ @NotNull @PathParam("type") ClientAppType type);
+
+ /**
+ * Create a new client app.
+ *
+ * @param registeredClientAppTO
+ * @return Response object featuring Location header of created registered client app
+ */
+ @ApiResponses(
+ @ApiResponse(responseCode = "201",
+ description = "ClientApp successfully created", headers = {
+ @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+ @Schema(type = "string"),
+ description = "UUID generated for the entity created"),
+ @Header(name = HttpHeaders.LOCATION, schema =
+ @Schema(type = "string"),
+ description = "URL of the entity created") }))
+ @POST
+ @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ Response create(@NotNull RegisteredClientAppTO registeredClientAppTO);
+
+ /**
+ * Delete client app matching the given key.
+ *
+ * @param name name of registered client application to be deleted
+ * @return
+ */
+ @ApiResponses(
+ @ApiResponse(responseCode = "204", description = "Operation was successful"))
+ @DELETE
+ @Path("{name}")
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ boolean delete(@NotNull @PathParam("name") String name);
+
+}
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/RegisteredClientAppLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/RegisteredClientAppLogic.java
new file mode 100644
index 0000000..f9b162a
--- /dev/null
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/RegisteredClientAppLogic.java
@@ -0,0 +1,238 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.to.AccessPolicyTO;
+import org.apache.syncope.common.lib.to.AttrReleasePolicyTO;
+import org.apache.syncope.common.lib.to.AuthPolicyTO;
+import org.apache.syncope.common.lib.to.ImplementationTO;
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.to.client.OIDCRPTO;
+import org.apache.syncope.common.lib.to.client.SAML2SPTO;
+import org.apache.syncope.common.lib.types.AMImplementationType;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.PolicyType;
+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.OIDCRP;
+import org.apache.syncope.core.persistence.api.entity.auth.SAML2SP;
+import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
+import org.apache.syncope.core.provisioning.api.data.RegisteredClientAppBinder;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class RegisteredClientAppLogic {
+
+ @Autowired
+ private ImplementationLogic implementationLogic;
+
+ @Autowired
+ private PolicyLogic policyLogic;
+
+ @Autowired
+ private ClientAppDataBinder clientAppDataBinder;
+
+ @Autowired
+ private RegisteredClientAppBinder binder;
+
+ @Autowired
+ private SAML2SPDAO saml2spDAO;
+
+ @Autowired
+ private OIDCRPDAO oidcrpDAO;
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public List<RegisteredClientAppTO> list() {
+ List<RegisteredClientAppTO> registeredApplications = new ArrayList<>();
+ Arrays.asList(ClientAppType.values()).forEach(type -> {
+ switch (type) {
+ case OIDCRP:
+ registeredApplications.addAll(oidcrpDAO.findAll().stream().map(binder::getRegisteredClientAppTO).
+ collect(Collectors.toList()));
+ break;
+
+ case SAML2SP:
+ default:
+ registeredApplications.addAll(saml2spDAO.findAll().stream().map(binder::getRegisteredClientAppTO).
+ collect(Collectors.toList()));
+ }
+ });
+
+ return registeredApplications;
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public RegisteredClientAppTO read(final Long clientAppId, final ClientAppType type) {
+ switch (type) {
+ case OIDCRP:
+ OIDCRP oidcrp = oidcrpDAO.findByClientAppId(clientAppId);
+ if (oidcrp != null) {
+ return binder.getRegisteredClientAppTO(oidcrp);
+ }
+ case SAML2SP:
+ SAML2SP saml2sp = saml2spDAO.findByClientAppId(clientAppId);
+ if (saml2sp != null) {
+ return binder.getRegisteredClientAppTO(saml2sp);
+ }
+ default:
+ return null;
+ }
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public RegisteredClientAppTO read(final Long clientAppId) {
+ for (ClientAppType type : ClientAppType.values()) {
+ RegisteredClientAppTO registeredClientAppTO = read(clientAppId, type);
+ if (registeredClientAppTO != null) {
+ return registeredClientAppTO;
+ }
+ }
+
+ return null;
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public RegisteredClientAppTO read(final String name, final ClientAppType type) {
+ switch (type) {
+ case OIDCRP:
+ OIDCRP oidcrp = oidcrpDAO.findByName(name);
+ if (oidcrp != null) {
+ return binder.getRegisteredClientAppTO(oidcrp);
+ }
+ case SAML2SP:
+ SAML2SP saml2sp = saml2spDAO.findByName(name);
+ if (saml2sp != null) {
+ return binder.getRegisteredClientAppTO(saml2sp);
+ }
+ default:
+ return null;
+ }
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional(readOnly = true)
+ public RegisteredClientAppTO read(final String name) {
+ for (ClientAppType type : ClientAppType.values()) {
+ RegisteredClientAppTO registeredClientAppTO = read(name, type);
+ if (registeredClientAppTO != null) {
+ return registeredClientAppTO;
+ }
+ }
+ return null;
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional
+ public RegisteredClientAppTO create(final RegisteredClientAppTO registeredClientAppTO) {
+
+ AuthPolicyTO authPolicyTO = new AuthPolicyTO();
+ if (registeredClientAppTO.getAuthPolicyConf() != null) {
+ String policyName = registeredClientAppTO.getClientAppTO().getName() + "AuthPolicy";
+ ImplementationTO implementationTO = new ImplementationTO();
+ implementationTO.setKey(policyName);
+ implementationTO.setEngine(ImplementationEngine.JAVA);
+ implementationTO.setType(AMImplementationType.AUTH_POLICY_CONFIGURATIONS);
+ implementationTO.setBody(POJOHelper.serialize(registeredClientAppTO.getAuthPolicyConf()));
+
+ ImplementationTO conf = implementationLogic.create(implementationTO);
+
+ authPolicyTO.setConfiguration(conf.getKey());
+ authPolicyTO = policyLogic.create(PolicyType.AUTH, authPolicyTO);
+ }
+
+ AccessPolicyTO accessPolicyTO = new AccessPolicyTO();
+ if (registeredClientAppTO.getAccessPolicyConf() != null) {
+
+ String policyName = registeredClientAppTO.getClientAppTO().getName() + "AccessPolicy";
+ ImplementationTO implementationTO = new ImplementationTO();
+ implementationTO.setKey(policyName);
+ implementationTO.setEngine(ImplementationEngine.JAVA);
+ implementationTO.setType(AMImplementationType.ACCESS_POLICY_CONFIGURATIONS);
+ implementationTO.setBody(POJOHelper.serialize(registeredClientAppTO.getAuthPolicyConf()));
+
+ ImplementationTO conf = implementationLogic.create(implementationTO);
+
+ accessPolicyTO.setConfiguration(conf.getKey());
+ accessPolicyTO = policyLogic.create(PolicyType.ACCESS, accessPolicyTO);
+ }
+
+ AttrReleasePolicyTO attrReleasePolicyTO = new AttrReleasePolicyTO();
+ if (registeredClientAppTO.getAttrReleasePolicyConf() != null) {
+
+ String policyName = registeredClientAppTO.getClientAppTO().getName() + "AttrReleasePolicy";
+ ImplementationTO implementationTO = new ImplementationTO();
+ implementationTO.setKey(policyName);
+ implementationTO.setEngine(ImplementationEngine.JAVA);
+ implementationTO.setType(AMImplementationType.ATTR_RELEASE_POLICY_CONFIGURATIONS);
+ implementationTO.setBody(POJOHelper.serialize(registeredClientAppTO.getAttrReleasePolicyConf()));
+
+ ImplementationTO conf = implementationLogic.create(implementationTO);
+
+ attrReleasePolicyTO.setConfiguration(conf.getKey());
+ attrReleasePolicyTO = policyLogic.create(PolicyType.ATTR_RELEASE, attrReleasePolicyTO);
+ }
+
+ if (registeredClientAppTO.getClientAppTO() instanceof OIDCRPTO) {
+ OIDCRPTO oidcrpto = OIDCRPTO.class.cast(registeredClientAppTO.getClientAppTO());
+ oidcrpto.setAccessPolicy(accessPolicyTO.getKey());
+ oidcrpto.setAttrReleasePolicy(attrReleasePolicyTO.getKey());
+ oidcrpto.setAuthPolicy(authPolicyTO.getKey());
+ return binder.getRegisteredClientAppTO(oidcrpDAO.save(clientAppDataBinder.create(oidcrpto)));
+
+ } else if (registeredClientAppTO.getClientAppTO() instanceof SAML2SPTO) {
+ SAML2SPTO saml2spto = SAML2SPTO.class.cast(registeredClientAppTO.getClientAppTO());
+ saml2spto.setAccessPolicy(accessPolicyTO.getKey());
+ saml2spto.setAttrReleasePolicy(attrReleasePolicyTO.getKey());
+ saml2spto.setAuthPolicy(authPolicyTO.getKey());
+ return binder.getRegisteredClientAppTO(saml2spDAO.save(clientAppDataBinder.create(saml2spto)));
+ }
+
+ return null;
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+ @Transactional
+ public boolean delete(final String name) {
+ ClientAppTO clientAppTO = read(name).getClientAppTO();
+ if (clientAppTO != null) {
+ if (clientAppTO instanceof OIDCRPTO) {
+ oidcrpDAO.delete(clientAppTO.getKey());
+ } else if (clientAppTO instanceof SAML2SPTO) {
+ saml2spDAO.delete(clientAppTO.getKey());
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RegisteredClientAppServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RegisteredClientAppServiceImpl.java
new file mode 100644
index 0000000..9a858fd
--- /dev/null
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RegisteredClientAppServiceImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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.net.URI;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.RegisteredClientAppService;
+import org.apache.syncope.core.logic.RegisteredClientAppLogic;
+
+@Service
+public class RegisteredClientAppServiceImpl extends AbstractServiceImpl implements RegisteredClientAppService {
+
+ @Autowired
+ private RegisteredClientAppLogic logic;
+
+ @Override
+ public List<RegisteredClientAppTO> list() {
+ return logic.list();
+ }
+
+ @Override
+ public RegisteredClientAppTO read(final Long clientAppId) {
+ RegisteredClientAppTO registeredClientAppTO = logic.read(clientAppId);
+ if (registeredClientAppTO == null) {
+ throw new NotFoundException("Client app with clientApp ID " + clientAppId + " not found");
+ }
+ return registeredClientAppTO;
+ }
+
+ @Override
+ public RegisteredClientAppTO read(final Long clientAppId, final ClientAppType type) {
+ RegisteredClientAppTO registeredClientAppTO = logic.read(clientAppId, type);
+ if (registeredClientAppTO == null) {
+ throw new NotFoundException("Client app with clientApp ID " + clientAppId
+ + " with type " + type + " not found");
+ }
+ return registeredClientAppTO;
+ }
+
+ @Override
+ public RegisteredClientAppTO read(final String name) {
+ RegisteredClientAppTO registeredClientAppTO = logic.read(name);
+ if (registeredClientAppTO == null) {
+ throw new NotFoundException("Client app with name " + name + " not found");
+ }
+ return registeredClientAppTO;
+ }
+
+ @Override
+ public RegisteredClientAppTO read(final String name, final ClientAppType type) {
+ RegisteredClientAppTO registeredClientAppTO = logic.read(name, type);
+ if (registeredClientAppTO == null) {
+ throw new NotFoundException("Client app with name " + name + " with type " + type + " not found");
+ }
+ return registeredClientAppTO;
+ }
+
+ @Override
+ public Response create(final RegisteredClientAppTO registeredClientAppTO) {
+ RegisteredClientAppTO appTO = logic.create(registeredClientAppTO);
+ URI location = uriInfo.getAbsolutePathBuilder().path(appTO.getClientAppTO().getKey()).build();
+ return Response.created(location).
+ header(RESTHeaders.RESOURCE_KEY, appTO.getClientAppTO().getKey()).
+ build();
+ }
+
+ @Override
+ public boolean delete(final String name) {
+ return logic.delete(name);
+ }
+
+}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java
new file mode 100644
index 0000000..d422534
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
+
+public interface RegisteredClientAppBinder {
+
+ RegisteredClientAppTO getRegisteredClientAppTO(ClientApp clientApp);
+
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RegisteredClientAppBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RegisteredClientAppBinderImpl.java
new file mode 100644
index 0000000..c8aae5b
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RegisteredClientAppBinderImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
+import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
+import org.apache.syncope.core.provisioning.api.data.RegisteredClientAppBinder;
+import org.apache.syncope.core.spring.ImplementationManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RegisteredClientAppBinderImpl implements RegisteredClientAppBinder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RegisteredClientAppBinder.class);
+
+ @Autowired
+ private ClientAppDataBinder clientAppDataBinder;
+
+ @Override
+ public RegisteredClientAppTO getRegisteredClientAppTO(final ClientApp clientApp) {
+ RegisteredClientAppTO registeredClientAppTO = new RegisteredClientAppTO();
+ registeredClientAppTO.setClientAppTO(clientAppDataBinder.getClientAppTO(clientApp));
+
+ try {
+ if (clientApp.getAuthPolicy() != null) {
+ registeredClientAppTO.setAuthPolicyConf(build((clientApp.getAuthPolicy()).getConfiguration()));
+ } else if (clientApp.getRealm().getAuthPolicy() != null) {
+ registeredClientAppTO.
+ setAuthPolicyConf(build((clientApp.getRealm().getAuthPolicy()).getConfiguration()));
+ } else {
+ registeredClientAppTO.setAuthPolicyConf(null);
+ }
+
+ if (clientApp.getAccessPolicy() != null) {
+ registeredClientAppTO.setAccessPolicyConf(build((clientApp.getAccessPolicy()).getConfiguration()));
+ } else if (clientApp.getRealm().getAccessPolicy() != null) {
+ registeredClientAppTO.setAccessPolicyConf(build((clientApp.getRealm().getAccessPolicy()).
+ getConfiguration()));
+ } else {
+ registeredClientAppTO.setAccessPolicyConf(null);
+ }
+
+ if (clientApp.getAttrReleasePolicy() != null) {
+ registeredClientAppTO.setAttrReleasePolicyConf(build((clientApp.getAttrReleasePolicy()).
+ getConfiguration()));
+ } else if (clientApp.getRealm().getAttrReleasePolicy() != null) {
+ registeredClientAppTO.setAttrReleasePolicyConf(build((clientApp.getRealm().getAttrReleasePolicy()).
+ getConfiguration()));
+ } else {
+ registeredClientAppTO.setAttrReleasePolicyConf(null);
+ }
+ } catch (Exception e) {
+ LOG.error("While building the configuration from an application's policy ", e);
+ }
+
+ return registeredClientAppTO;
+ }
+
+ private <T> T build(final Implementation impl) throws InstantiationException, IllegalAccessException,
+ ClassNotFoundException {
+ return ImplementationManager.build(impl);
+ }
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RegisteredClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RegisteredClientAppITCase.java
new file mode 100644
index 0000000..42fe26f
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RegisteredClientAppITCase.java
@@ -0,0 +1,114 @@
+/*
+ * 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.fit.core;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.List;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.to.client.OIDCRPTO;
+import org.apache.syncope.common.lib.to.client.SAML2SPTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.rest.api.service.RegisteredClientAppService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class RegisteredClientAppITCase extends AbstractITCase {
+
+ protected static RegisteredClientAppService registeredClientAppService;
+
+ @BeforeAll
+ public static void setup() {
+ SyncopeClient anonymous = clientFactory.create(
+ new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
+ registeredClientAppService = anonymous.getService(RegisteredClientAppService.class);
+ }
+
+ @Test
+ public void list() {
+ createClientApp(ClientAppType.OIDCRP, buildOIDCRP());
+
+ List<RegisteredClientAppTO> list = registeredClientAppService.list();
+ assertFalse(list.isEmpty());
+ }
+
+ @Test
+ public void read() {
+ OIDCRPTO oidcrpto = createClientApp(ClientAppType.OIDCRP, buildOIDCRP());
+ RegisteredClientAppTO registeredOidcClientApp = registeredClientAppService.read(oidcrpto.getClientAppId());
+ assertNotNull(registeredOidcClientApp);
+
+ registeredOidcClientApp = registeredClientAppService.read(oidcrpto.getClientAppId(),
+ ClientAppType.OIDCRP);
+ assertNotNull(registeredOidcClientApp);
+
+ registeredOidcClientApp = registeredClientAppService.read(oidcrpto.getName());
+ assertNotNull(registeredOidcClientApp);
+
+ registeredOidcClientApp = registeredClientAppService.read(oidcrpto.getName(), ClientAppType.OIDCRP);
+ assertNotNull(registeredOidcClientApp);
+
+
+ SAML2SPTO samlspto = createClientApp(ClientAppType.SAML2SP, buildSAML2SP());
+ RegisteredClientAppTO registeredSamlClientApp= registeredClientAppService.read(samlspto.getClientAppId());
+ assertNotNull(registeredSamlClientApp);
+
+ registeredSamlClientApp = registeredClientAppService.read(samlspto.getClientAppId(),
+ ClientAppType.SAML2SP);
+ assertNotNull(registeredSamlClientApp);
+
+ registeredSamlClientApp = registeredClientAppService.read(samlspto.getName());
+ assertNotNull(registeredSamlClientApp);
+
+ registeredSamlClientApp = registeredClientAppService.read(samlspto.getName(), ClientAppType.SAML2SP);
+ assertNotNull(registeredSamlClientApp);
+ }
+
+
+ @Test
+ public void delete() {
+ SAML2SPTO samlspto = createClientApp(ClientAppType.SAML2SP, buildSAML2SP());
+
+ assertTrue(registeredClientAppService.delete(samlspto.getName()));
+ try {
+ clientAppService.read(ClientAppType.SAML2SP, samlspto.getKey());
+ fail("This should not happen");
+ } catch (SyncopeClientException e) {
+ assertNotNull(e);
+ }
+ }
+
+ @Test
+ public void create() {
+ OIDCRPTO oidcrpto = buildOIDCRP();
+ RegisteredClientAppTO appTO = new RegisteredClientAppTO();
+ appTO.setClientAppTO(oidcrpto);
+
+ registeredClientAppService.create(appTO);
+ assertNotNull(registeredClientAppService.read(oidcrpto.getClientAppId()));
+ }
+
+}