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:47 UTC

[syncope] 07/07: [SYNCOPE-160] WA service refactoring

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 109e0fe98a41527cf57f863404c0e0a99e0d27dc
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Thu Apr 9 16:24:19 2020 +0200

    [SYNCOPE-160] WA service refactoring
---
 .../WAClientApp.java}                              |   4 +-
 .../apache/syncope/common/lib/wa/package-info.java |  16 +-
 .../api/service/RegisteredClientAppService.java    | 133 ------------
 .../rest/api/service/wa/WAClientAppService.java    |  78 +++++++
 .../core/logic/RegisteredClientAppLogic.java       | 238 ---------------------
 .../syncope/core/logic/wa/WAClientAppLogic.java    | 160 ++++++++++++++
 .../service/RegisteredClientAppServiceImpl.java    |  96 ---------
 .../cxf/service/wa/WAClientAppServiceImpl.java     |  50 +++++
 .../core/rest/cxf/service/AbstractServiceImpl.java |   2 +-
 .../WAClientAppBinder.java}                        |   9 +-
 .../java/data/RegisteredClientAppBinderImpl.java   |  84 --------
 .../java/data/wa/WAClientAppBinderImpl.java        |  74 +++++++
 .../org/apache/syncope/fit/AbstractITCase.java     |   1 -
 .../fit/core/RegisteredClientAppITCase.java        | 114 ----------
 .../apache/syncope/fit/core/WAClientAppITCase.java |  83 +++++++
 .../syncope/wa/mapper/RegisteredServiceMapper.java | 205 ------------------
 .../wa/starter/RegisteredServiceMapper.java        | 205 ++++++++++++++++++
 .../starter/{rest => }/SyncopeServiceRegistry.java |  82 +++----
 .../syncope/wa/starter/SyncopeWAApplication.java   |  15 +-
 .../syncope/wa/starter/SyncopeWAConfiguration.java |   1 -
 20 files changed, 704 insertions(+), 946 deletions(-)

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/wa/WAClientApp.java
similarity index 95%
rename from common/am/lib/src/main/java/org/apache/syncope/common/lib/to/RegisteredClientAppTO.java
rename to common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/WAClientApp.java
index 6b56357..524ffc9 100644
--- 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/wa/WAClientApp.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.to;
+package org.apache.syncope.common.lib.wa;
 
 import java.io.Serializable;
 import org.apache.syncope.common.lib.policy.AccessPolicyConf;
@@ -24,7 +24,7 @@ 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 {
+public class WAClientApp implements Serializable {
 
     private static final long serialVersionUID = 6633251825655119506L;
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/package-info.java
similarity index 62%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/package-info.java
index d422534..988e450 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/wa/package-info.java
@@ -16,13 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.provisioning.api.data;
+@XmlSchema(namespace = SyncopeConstants.NS)
+@XmlJavaTypeAdapters({ @XmlJavaTypeAdapter(type = Date.class, value = DateAdapter.class), })
+package org.apache.syncope.common.lib.wa;
 
-import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.jaxb.DateAdapter;
 
-public interface RegisteredClientAppBinder {
+import javax.xml.bind.annotation.XmlSchema;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
 
-    RegisteredClientAppTO getRegisteredClientAppTO(ClientApp clientApp);
-
-}
+import java.util.Date;
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
deleted file mode 100644
index cd55423..0000000
--- a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RegisteredClientAppService.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/WAClientAppService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/WAClientAppService.java
new file mode 100644
index 0000000..16c0521
--- /dev/null
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/WAClientAppService.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.wa;
+
+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 java.util.List;
+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.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.lib.wa.WAClientApp;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
+
+/**
+ * REST operations for WA.
+ */
+@Tag(name = "WA")
+@SecurityRequirements({
+    @SecurityRequirement(name = "BasicAuthentication"),
+    @SecurityRequirement(name = "Bearer") })
+@Path("wa/clientApps")
+public interface WAClientAppService extends JAXRSService {
+
+    /**
+     * Returns a list of all client applications available.
+     *
+     * @return list of all client applications.
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON })
+    List<WAClientApp> list();
+
+    /**
+     * Returns client application with matching type and clientAppId, if found.
+     *
+     * @param clientAppId registered client application ID to be read
+     * @param type client application type
+     * @return client application with matching type and clientAppId
+     */
+    @GET
+    @Path("{clientAppId}")
+    @Produces({ MediaType.APPLICATION_JSON })
+    WAClientApp read(@NotNull @PathParam("clientAppId") Long clientAppId, @QueryParam("type") ClientAppType type);
+
+    /**
+     * Returns client application with matching type and name, if found.
+     *
+     * @param name registered client application name to be read
+     * @param type client application type
+     * @return client application with matching type and name
+     */
+    @GET
+    @Path("byName/{name}")
+    @Produces({ MediaType.APPLICATION_JSON })
+    WAClientApp read(@NotNull @PathParam("name") String name, @QueryParam("type") ClientAppType type);
+}
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
deleted file mode 100644
index f9b162a..0000000
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/RegisteredClientAppLogic.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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/logic/src/main/java/org/apache/syncope/core/logic/wa/WAClientAppLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAClientAppLogic.java
new file mode 100644
index 0000000..1bb6cd5
--- /dev/null
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAClientAppLogic.java
@@ -0,0 +1,160 @@
+/*
+ * 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.wa;
+
+import static org.apache.syncope.common.lib.types.ClientAppType.OIDCRP;
+import static org.apache.syncope.common.lib.types.ClientAppType.SAML2SP;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.syncope.common.lib.wa.WAClientApp;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.apache.syncope.core.provisioning.api.data.wa.WAClientAppBinder;
+
+@Component
+public class WAClientAppLogic {
+
+    @Autowired
+    private WAClientAppBinder binder;
+
+    @Autowired
+    private SAML2SPDAO saml2spDAO;
+
+    @Autowired
+    private OIDCRPDAO oidcrpDAO;
+
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @Transactional(readOnly = true)
+    public List<WAClientApp> list() {
+        List<WAClientApp> clientApps = new ArrayList<>();
+
+        Stream.of(ClientAppType.values()).forEach(type -> {
+            switch (type) {
+                case OIDCRP:
+                    clientApps.addAll(oidcrpDAO.findAll().stream().
+                            map(binder::getWAClientApp).collect(Collectors.toList()));
+                    break;
+
+                case SAML2SP:
+                default:
+                    clientApps.addAll(saml2spDAO.findAll().stream().
+                            map(binder::getWAClientApp).collect(Collectors.toList()));
+            }
+        });
+
+        return clientApps;
+    }
+
+    private WAClientApp doRead(final Long clientAppId, final ClientAppType type) {
+        WAClientApp clientApp = null;
+
+        switch (type) {
+            case OIDCRP:
+                OIDCRP oidcrp = oidcrpDAO.findByClientAppId(clientAppId);
+                if (oidcrp != null) {
+                    clientApp = binder.getWAClientApp(oidcrp);
+                }
+                break;
+
+            case SAML2SP:
+                SAML2SP saml2sp = saml2spDAO.findByClientAppId(clientAppId);
+                if (saml2sp != null) {
+                    clientApp = binder.getWAClientApp(saml2sp);
+                }
+                break;
+
+            default:
+        }
+
+        return clientApp;
+    }
+
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @Transactional(readOnly = true)
+    public WAClientApp read(final Long clientAppId, final ClientAppType type) {
+        WAClientApp clientApp = null;
+        if (type == null) {
+            for (int i = 0; i < ClientAppType.values().length && clientApp == null; i++) {
+                clientApp = doRead(clientAppId, ClientAppType.values()[i]);
+            }
+        } else {
+            clientApp = doRead(clientAppId, type);
+        }
+
+        if (clientApp == null) {
+            throw new NotFoundException(
+                    "Client app with clientApp ID " + clientAppId + " and type " + type + " not found");
+        }
+        return clientApp;
+    }
+
+    private WAClientApp doRead(final String name, final ClientAppType type) {
+        WAClientApp clientApp = null;
+
+        switch (type) {
+            case OIDCRP:
+                OIDCRP oidcrp = oidcrpDAO.findByName(name);
+                if (oidcrp != null) {
+                    clientApp = binder.getWAClientApp(oidcrp);
+                }
+                break;
+
+            case SAML2SP:
+                SAML2SP saml2sp = saml2spDAO.findByName(name);
+                if (saml2sp != null) {
+                    clientApp = binder.getWAClientApp(saml2sp);
+                }
+                break;
+
+            default:
+        }
+
+        return clientApp;
+    }
+
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @Transactional(readOnly = true)
+    public WAClientApp read(final String name, final ClientAppType type) {
+        WAClientApp clientApp = null;
+        if (type == null) {
+            for (int i = 0; i < ClientAppType.values().length && clientApp == null; i++) {
+                clientApp = doRead(name, ClientAppType.values()[i]);
+            }
+        } else {
+            clientApp = doRead(name, type);
+        }
+
+        if (clientApp == null) {
+            throw new NotFoundException("Client app with name " + name + " with type " + type + " not found");
+        }
+        return clientApp;
+    }
+}
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
deleted file mode 100644
index 9a858fd..0000000
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RegisteredClientAppServiceImpl.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/WAClientAppServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/WAClientAppServiceImpl.java
new file mode 100644
index 0000000..fcd9061
--- /dev/null
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/wa/WAClientAppServiceImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.wa;
+
+import java.util.List;
+import org.apache.syncope.common.lib.types.ClientAppType;
+import org.apache.syncope.common.lib.wa.WAClientApp;
+import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
+import org.apache.syncope.core.logic.wa.WAClientAppLogic;
+import org.apache.syncope.core.rest.cxf.service.AbstractServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WAClientAppServiceImpl extends AbstractServiceImpl implements WAClientAppService {
+
+    @Autowired
+    private WAClientAppLogic logic;
+
+    @Override
+    public List<WAClientApp> list() {
+        return logic.list();
+    }
+
+    @Override
+    public WAClientApp read(final Long clientAppId, final ClientAppType type) {
+        return logic.read(clientAppId, type);
+    }
+
+    @Override
+    public WAClientApp read(final String name, final ClientAppType type) {
+        return logic.read(name, type);
+    }
+}
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
index 7fe01c3..f61260d 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
@@ -50,7 +50,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 
-abstract class AbstractServiceImpl implements JAXRSService {
+public abstract class AbstractServiceImpl implements JAXRSService {
 
     protected static final Logger LOG = LoggerFactory.getLogger(AbstractServiceImpl.class);
 
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/wa/WAClientAppBinder.java
similarity index 78%
rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppBinder.java
index d422534..c95f9a6 100644
--- 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/wa/WAClientAppBinder.java
@@ -16,13 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.provisioning.api.data;
+package org.apache.syncope.core.provisioning.api.data.wa;
 
-import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.wa.WAClientApp;
 import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
 
-public interface RegisteredClientAppBinder {
-
-    RegisteredClientAppTO getRegisteredClientAppTO(ClientApp clientApp);
+public interface WAClientAppBinder {
 
+    WAClientApp getWAClientApp(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
deleted file mode 100644
index c8aae5b..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RegisteredClientAppBinderImpl.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppBinderImpl.java
new file mode 100644
index 0000000..5636892
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppBinderImpl.java
@@ -0,0 +1,74 @@
+/*
+ * 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.wa;
+
+import org.apache.syncope.common.lib.wa.WAClientApp;
+import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
+import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
+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;
+import org.apache.syncope.core.provisioning.api.data.wa.WAClientAppBinder;
+
+@Component
+public class WAClientAppBinderImpl implements WAClientAppBinder {
+
+    private static final Logger LOG = LoggerFactory.getLogger(WAClientAppBinder.class);
+
+    @Autowired
+    private ClientAppDataBinder clientAppDataBinder;
+
+    @Override
+    public WAClientApp getWAClientApp(final ClientApp clientApp) {
+        WAClientApp waClientApp = new WAClientApp();
+        waClientApp.setClientAppTO(clientAppDataBinder.getClientAppTO(clientApp));
+
+        try {
+            if (clientApp.getAuthPolicy() != null) {
+                waClientApp.setAuthPolicyConf(
+                        ImplementationManager.build((clientApp.getAuthPolicy()).getConfiguration()));
+            } else if (clientApp.getRealm().getAuthPolicy() != null) {
+                waClientApp.setAuthPolicyConf(
+                        ImplementationManager.build((clientApp.getRealm().getAuthPolicy()).getConfiguration()));
+            }
+
+            if (clientApp.getAccessPolicy() != null) {
+                waClientApp.setAccessPolicyConf(
+                        ImplementationManager.build((clientApp.getAccessPolicy()).getConfiguration()));
+            } else if (clientApp.getRealm().getAccessPolicy() != null) {
+                waClientApp.setAccessPolicyConf(
+                        ImplementationManager.build((clientApp.getRealm().getAccessPolicy()).getConfiguration()));
+            }
+
+            if (clientApp.getAttrReleasePolicy() != null) {
+                waClientApp.setAttrReleasePolicyConf(
+                        ImplementationManager.build((clientApp.getAttrReleasePolicy()).getConfiguration()));
+            } else if (clientApp.getRealm().getAttrReleasePolicy() != null) {
+                waClientApp.setAttrReleasePolicyConf(
+                        ImplementationManager.build((clientApp.getRealm().getAttrReleasePolicy()).getConfiguration()));
+            }
+        } catch (Exception e) {
+            LOG.error("While building the configuration from an application's policy ", e);
+        }
+
+        return waClientApp;
+    }
+}
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 c8f8129..ee19a6b 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
@@ -793,5 +793,4 @@ public abstract class AbstractITCase {
         }
         return (T) getObject(response.getLocation(), ClientAppService.class, clientAppTO.getClass());
     }
-
 }
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
deleted file mode 100644
index 42fe26f..0000000
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RegisteredClientAppITCase.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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()));
-    }
-
-}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAClientAppITCase.java
new file mode 100644
index 0000000..99d4ded
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAClientAppITCase.java
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.List;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.wa.WAClientApp;
+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.fit.AbstractITCase;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
+
+public class WAClientAppITCase extends AbstractITCase {
+
+    private static WAClientAppService waClientAppService;
+
+    @BeforeAll
+    public static void setup() {
+        SyncopeClient anonymous = clientFactory.create(
+                new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
+        waClientAppService = anonymous.getService(WAClientAppService.class);
+    }
+
+    @Test
+    public void list() {
+        createClientApp(ClientAppType.OIDCRP, buildOIDCRP());
+
+        List<WAClientApp> list = waClientAppService.list();
+        assertFalse(list.isEmpty());
+    }
+
+    @Test
+    public void read() {
+        OIDCRPTO oidcrpto = createClientApp(ClientAppType.OIDCRP, buildOIDCRP());
+        WAClientApp waClientApp = waClientAppService.read(oidcrpto.getClientAppId(), null);
+        assertNotNull(waClientApp);
+
+        waClientApp = waClientAppService.read(oidcrpto.getClientAppId(), ClientAppType.OIDCRP);
+        assertNotNull(waClientApp);
+
+        waClientApp = waClientAppService.read(oidcrpto.getName(), null);
+        assertNotNull(waClientApp);
+
+        waClientApp = waClientAppService.read(oidcrpto.getName(), ClientAppType.OIDCRP);
+        assertNotNull(waClientApp);
+
+        SAML2SPTO samlspto = createClientApp(ClientAppType.SAML2SP, buildSAML2SP());
+        WAClientApp registeredSamlClientApp = waClientAppService.read(samlspto.getClientAppId(), null);
+        assertNotNull(registeredSamlClientApp);
+
+        registeredSamlClientApp = waClientAppService.read(samlspto.getClientAppId(), ClientAppType.SAML2SP);
+        assertNotNull(registeredSamlClientApp);
+
+        registeredSamlClientApp = waClientAppService.read(samlspto.getName(), null);
+        assertNotNull(registeredSamlClientApp);
+
+        registeredSamlClientApp = waClientAppService.read(samlspto.getName(), ClientAppType.SAML2SP);
+        assertNotNull(registeredSamlClientApp);
+    }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/mapper/RegisteredServiceMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/mapper/RegisteredServiceMapper.java
deleted file mode 100644
index c81d1a7..0000000
--- a/wa/starter/src/main/java/org/apache/syncope/wa/mapper/RegisteredServiceMapper.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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.wa.mapper;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf;
-import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf;
-import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
-import org.apache.syncope.common.lib.policy.DefaultAuthPolicyCriteriaConf;
-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.OIDCSubjectType;
-import org.apache.syncope.common.lib.types.SAML2SPNameId;
-import org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy;
-import org.apereo.cas.services.DefaultRegisteredServiceAuthenticationPolicy;
-import org.apereo.cas.services.DenyAllAttributeReleasePolicy;
-import org.apereo.cas.services.OidcRegisteredService;
-import org.apereo.cas.services.RegisteredService;
-import org.apereo.cas.services.RegisteredServiceAccessStrategy;
-import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
-import org.apereo.cas.services.AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria;
-import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
-import org.apereo.cas.support.saml.services.SamlRegisteredService;
-
-public class RegisteredServiceMapper {
-
-    public RegisteredService toRegisteredService(final RegisteredClientAppTO clientApp) {
-
-        DefaultRegisteredServiceAuthenticationPolicy authenticationPolicy =
-                new DefaultRegisteredServiceAuthenticationPolicy();
-        AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria criteria =
-                new AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria();
-        criteria.setTryAll(((DefaultAuthPolicyCriteriaConf) clientApp.getAuthPolicyConf().getCriteria()).isAll());
-        authenticationPolicy.setCriteria(criteria);
-
-        RegisteredServiceAccessStrategy accessStrategy =
-                new DefaultRegisteredServiceAccessStrategy(
-                        clientApp.getAccessPolicyConf().isEnabled(), clientApp.getAccessPolicyConf().isSsoEnabled());
-        accessStrategy.getRequiredAttributes().putAll(clientApp.getAccessPolicyConf().getRequiredAttributes());
-
-        RegisteredServiceAttributeReleasePolicy attributeReleasePolicy;
-        if (clientApp.getAttrReleasePolicyConf() != null
-                && clientApp.getAttrReleasePolicyConf() instanceof AllowedAttrReleasePolicyConf
-                && !((AllowedAttrReleasePolicyConf) clientApp.getAttrReleasePolicyConf()).
-                        getAllowedAttributes().isEmpty()) {
-            attributeReleasePolicy = new ReturnAllowedAttributeReleasePolicy();
-            ((AllowedAttrReleasePolicyConf) clientApp.getAttrReleasePolicyConf()).getAllowedAttributes();
-            ((ReturnAllowedAttributeReleasePolicy) attributeReleasePolicy).getAllowedAttributes().addAll(
-                    ((AllowedAttrReleasePolicyConf) clientApp.getAttrReleasePolicyConf()).getAllowedAttributes());
-        } else {
-            attributeReleasePolicy = new DenyAllAttributeReleasePolicy();
-        }
-
-        if (clientApp.getClientAppTO() instanceof OIDCRPTO) {
-            OIDCRPTO rp = OIDCRPTO.class.cast(clientApp.getClientAppTO());
-            OidcRegisteredService registeredService = new OidcRegisteredService();
-
-            String redirectURIs = String.join("|", rp.getRedirectUris());
-            registeredService.setServiceId(redirectURIs);
-            registeredService.setName(rp.getName());
-            registeredService.setDescription(rp.getDescription());
-            registeredService.setAccessStrategy(accessStrategy);
-            registeredService.setAuthenticationPolicy(authenticationPolicy);
-            registeredService.setAttributeReleasePolicy(attributeReleasePolicy);
-
-            registeredService.setClientId(rp.getClientId());
-            registeredService.setClientSecret(rp.getClientSecret());
-            registeredService.setSignIdToken(rp.isSignIdToken());
-            registeredService.setJwks(rp.getJwks());
-            registeredService.setSubjectType(rp.getSubjectType().name());
-            registeredService.setRedirectUrl(redirectURIs);
-            registeredService.setSupportedGrantTypes((HashSet<String>) rp.getSupportedGrantTypes());
-            registeredService.setSupportedResponseTypes((HashSet<String>) rp.getSupportedResponseTypes());
-
-            return registeredService;
-        } else if (clientApp.getClientAppTO() instanceof SAML2SPTO) {
-            SAML2SPTO sp = SAML2SPTO.class.cast(clientApp.getClientAppTO());
-            SamlRegisteredService registeredService = new SamlRegisteredService();
-
-            registeredService.setServiceId(sp.getEntityId());
-            registeredService.setName(sp.getName());
-            registeredService.setDescription(sp.getDescription());
-            registeredService.setAccessStrategy(accessStrategy);
-            registeredService.setAuthenticationPolicy(authenticationPolicy);
-            registeredService.setAttributeReleasePolicy(attributeReleasePolicy);
-
-            registeredService.setMetadataLocation(sp.getMetadataLocation());
-            registeredService.setMetadataSignatureLocation(sp.getMetadataSignatureLocation());
-            registeredService.setSignAssertions(sp.isSignAssertions());
-            registeredService.setSignResponses(sp.isSignResponses());
-            registeredService.setEncryptionOptional(sp.isEncryptionOptional());
-            registeredService.setEncryptAssertions(sp.isEncryptAssertions());
-            registeredService.setRequiredAuthenticationContextClass(sp.getRequiredAuthenticationContextClass());
-            registeredService.setRequiredNameIdFormat(sp.getRequiredNameIdFormat().getNameId());
-            registeredService.setSkewAllowance(sp.getSkewAllowance());
-            registeredService.setNameIdQualifier(sp.getNameIdQualifier());
-            registeredService.setAssertionAudiences(sp.getAssertionAudiences());
-            registeredService.setServiceProviderNameIdQualifier(sp.getServiceProviderNameIdQualifier());
-            return registeredService;
-        }
-        return null;
-    }
-
-    public RegisteredClientAppTO fromRegisteredService(final RegisteredService registeredService) {
-        RegisteredClientAppTO clientApp = new RegisteredClientAppTO();
-
-        if (registeredService.getAuthenticationPolicy() != null) {
-            DefaultAuthPolicyConf authPolicyConf = new DefaultAuthPolicyConf();
-            DefaultAuthPolicyCriteriaConf criteria = new DefaultAuthPolicyCriteriaConf();
-            criteria.setAll(((DefaultAuthPolicyCriteriaConf) registeredService.
-                    getAuthenticationPolicy().getCriteria()).isAll());
-            authPolicyConf.setCriteria(criteria);
-
-            clientApp.setAuthPolicyConf(authPolicyConf);
-        }
-
-        if (registeredService.getAccessStrategy() != null) {
-            DefaultAccessPolicyConf accessPolicyConf = new DefaultAccessPolicyConf();
-            accessPolicyConf.setEnabled(
-                    ((DefaultRegisteredServiceAccessStrategy) registeredService.getAccessStrategy()).
-                            isEnabled());
-            accessPolicyConf.setSsoEnabled(((DefaultRegisteredServiceAccessStrategy) registeredService.
-                    getAccessStrategy()).
-                    isSsoEnabled());
-            accessPolicyConf.getRequiredAttributes().putAll(((DefaultRegisteredServiceAccessStrategy) registeredService.
-                    getAccessStrategy()).getRejectedAttributes());
-
-            clientApp.setAccessPolicyConf(accessPolicyConf);
-        }
-
-        if (registeredService.getAttributeReleasePolicy() != null) {
-
-            if (registeredService.getAttributeReleasePolicy() instanceof ReturnAllowedAttributeReleasePolicy) {
-                ReturnAllowedAttributeReleasePolicy returnAllowedAttributeReleasePolicy =
-                        ReturnAllowedAttributeReleasePolicy.class.cast(registeredService.getAttributeReleasePolicy());
-                AllowedAttrReleasePolicyConf allowedAttrReleasePolicyConf = new AllowedAttrReleasePolicyConf();
-                allowedAttrReleasePolicyConf.getAllowedAttributes().addAll(returnAllowedAttributeReleasePolicy.
-                        getAllowedAttributes());
-
-                clientApp.setAttrReleasePolicyConf(allowedAttrReleasePolicyConf);
-            }
-        }
-
-        if (registeredService instanceof OidcRegisteredService) {
-            OidcRegisteredService oidcRegisteredService = OidcRegisteredService.class.cast(registeredService);
-            OIDCRPTO oidcrpto = new OIDCRPTO();
-
-            Arrays.asList(registeredService.getServiceId().split("|")).forEach(redirectURI
-                    -> oidcrpto.getRedirectUris().add(redirectURI));
-            oidcrpto.setName(oidcRegisteredService.getName());
-            oidcrpto.setDescription(oidcRegisteredService.getDescription());
-            oidcrpto.setClientId(oidcRegisteredService.getClientId());
-            oidcrpto.setClientSecret(oidcRegisteredService.getClientSecret());
-            oidcrpto.setSignIdToken(oidcRegisteredService.isSignIdToken());
-            oidcrpto.setJwks(oidcRegisteredService.getJwks());
-            oidcrpto.setSubjectType(OIDCSubjectType.valueOf(oidcRegisteredService.getSubjectType()));
-            oidcrpto.getSupportedGrantTypes().addAll(oidcRegisteredService.getSupportedGrantTypes());
-            oidcrpto.getSupportedResponseTypes().addAll(oidcRegisteredService.getSupportedResponseTypes());
-
-            clientApp.setClientAppTO(oidcrpto);
-        } else if (registeredService instanceof SamlRegisteredService) {
-            SamlRegisteredService samlRegisteredService = SamlRegisteredService.class.cast(registeredService);
-            SAML2SPTO saml2spto = new SAML2SPTO();
-
-            saml2spto.setEntityId(samlRegisteredService.getServiceId());
-            saml2spto.setName(samlRegisteredService.getName());
-            saml2spto.setDescription(samlRegisteredService.getDescription());
-
-            saml2spto.setMetadataLocation(samlRegisteredService.getMetadataLocation());
-            saml2spto.setMetadataSignatureLocation(samlRegisteredService.getMetadataSignatureLocation());
-            saml2spto.setSignAssertions(samlRegisteredService.isSignAssertions());
-            saml2spto.setSignResponses(samlRegisteredService.isSignResponses());
-            saml2spto.setEncryptionOptional(samlRegisteredService.isEncryptionOptional());
-            saml2spto.setEncryptAssertions(samlRegisteredService.isEncryptAssertions());
-            saml2spto.setRequiredAuthenticationContextClass(samlRegisteredService.
-                    getRequiredAuthenticationContextClass());
-            saml2spto.setRequiredNameIdFormat(SAML2SPNameId.valueOf(samlRegisteredService.getRequiredNameIdFormat()));
-            saml2spto.setSkewAllowance(samlRegisteredService.getSkewAllowance());
-            saml2spto.setNameIdQualifier(samlRegisteredService.getNameIdQualifier());
-            saml2spto.setAssertionAudiences(samlRegisteredService.getAssertionAudiences());
-            saml2spto.setServiceProviderNameIdQualifier(samlRegisteredService.getServiceProviderNameIdQualifier());
-
-            clientApp.setClientAppTO(saml2spto);
-        }
-        return clientApp;
-    }
-}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/RegisteredServiceMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/RegisteredServiceMapper.java
new file mode 100644
index 0000000..2746793
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/RegisteredServiceMapper.java
@@ -0,0 +1,205 @@
+/*
+ * 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.wa.starter;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf;
+import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf;
+import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
+import org.apache.syncope.common.lib.policy.DefaultAuthPolicyCriteriaConf;
+import org.apache.syncope.common.lib.wa.WAClientApp;
+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.OIDCSubjectType;
+import org.apache.syncope.common.lib.types.SAML2SPNameId;
+import org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy;
+import org.apereo.cas.services.DefaultRegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.DenyAllAttributeReleasePolicy;
+import org.apereo.cas.services.OidcRegisteredService;
+import org.apereo.cas.services.RegisteredService;
+import org.apereo.cas.services.RegisteredServiceAccessStrategy;
+import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
+import org.apereo.cas.services.AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria;
+import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
+import org.apereo.cas.support.saml.services.SamlRegisteredService;
+
+public final class RegisteredServiceMapper {
+
+    private RegisteredServiceMapper() {
+        // private constructor for static utility class
+    }
+
+    public static RegisteredService toRegisteredService(final WAClientApp clientApp) {
+        DefaultRegisteredServiceAuthenticationPolicy authPolicy = new DefaultRegisteredServiceAuthenticationPolicy();
+        AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria criteria =
+                new AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria();
+        criteria.setTryAll(((DefaultAuthPolicyCriteriaConf) clientApp.getAuthPolicyConf().getCriteria()).isAll());
+        authPolicy.setCriteria(criteria);
+
+        RegisteredServiceAccessStrategy accessStrategy = new DefaultRegisteredServiceAccessStrategy(
+                clientApp.getAccessPolicyConf().isEnabled(), clientApp.getAccessPolicyConf().isSsoEnabled());
+        accessStrategy.getRequiredAttributes().putAll(clientApp.getAccessPolicyConf().getRequiredAttributes());
+
+        RegisteredServiceAttributeReleasePolicy attributeReleasePolicy;
+        if (clientApp.getAttrReleasePolicyConf() != null
+                && clientApp.getAttrReleasePolicyConf() instanceof AllowedAttrReleasePolicyConf
+                && !((AllowedAttrReleasePolicyConf) clientApp.getAttrReleasePolicyConf()).
+                        getAllowedAttributes().isEmpty()) {
+
+            attributeReleasePolicy = new ReturnAllowedAttributeReleasePolicy();
+            ((AllowedAttrReleasePolicyConf) clientApp.getAttrReleasePolicyConf()).getAllowedAttributes();
+            ((ReturnAllowedAttributeReleasePolicy) attributeReleasePolicy).getAllowedAttributes().addAll(
+                    ((AllowedAttrReleasePolicyConf) clientApp.getAttrReleasePolicyConf()).getAllowedAttributes());
+        } else {
+            attributeReleasePolicy = new DenyAllAttributeReleasePolicy();
+        }
+
+        if (clientApp.getClientAppTO() instanceof OIDCRPTO) {
+            OIDCRPTO rp = OIDCRPTO.class.cast(clientApp.getClientAppTO());
+            OidcRegisteredService service = new OidcRegisteredService();
+
+            String redirectURIs = String.join("|", rp.getRedirectUris());
+            service.setServiceId(redirectURIs);
+            service.setName(rp.getName());
+            service.setDescription(rp.getDescription());
+            service.setAccessStrategy(accessStrategy);
+            service.setAuthenticationPolicy(authPolicy);
+            service.setAttributeReleasePolicy(attributeReleasePolicy);
+
+            service.setClientId(rp.getClientId());
+            service.setClientSecret(rp.getClientSecret());
+            service.setSignIdToken(rp.isSignIdToken());
+            service.setJwks(rp.getJwks());
+            service.setSubjectType(rp.getSubjectType().name());
+            service.setRedirectUrl(redirectURIs);
+            service.setSupportedGrantTypes((HashSet<String>) rp.getSupportedGrantTypes());
+            service.setSupportedResponseTypes((HashSet<String>) rp.getSupportedResponseTypes());
+
+            return service;
+        } else if (clientApp.getClientAppTO() instanceof SAML2SPTO) {
+            SAML2SPTO sp = SAML2SPTO.class.cast(clientApp.getClientAppTO());
+            SamlRegisteredService service = new SamlRegisteredService();
+
+            service.setServiceId(sp.getEntityId());
+            service.setName(sp.getName());
+            service.setDescription(sp.getDescription());
+            service.setAccessStrategy(accessStrategy);
+            service.setAuthenticationPolicy(authPolicy);
+            service.setAttributeReleasePolicy(attributeReleasePolicy);
+
+            service.setMetadataLocation(sp.getMetadataLocation());
+            service.setMetadataSignatureLocation(sp.getMetadataSignatureLocation());
+            service.setSignAssertions(sp.isSignAssertions());
+            service.setSignResponses(sp.isSignResponses());
+            service.setEncryptionOptional(sp.isEncryptionOptional());
+            service.setEncryptAssertions(sp.isEncryptAssertions());
+            service.setRequiredAuthenticationContextClass(sp.getRequiredAuthenticationContextClass());
+            service.setRequiredNameIdFormat(sp.getRequiredNameIdFormat().getNameId());
+            service.setSkewAllowance(sp.getSkewAllowance());
+            service.setNameIdQualifier(sp.getNameIdQualifier());
+            service.setAssertionAudiences(sp.getAssertionAudiences());
+            service.setServiceProviderNameIdQualifier(sp.getServiceProviderNameIdQualifier());
+
+            return service;
+        }
+
+        return null;
+    }
+
+    public static WAClientApp fromRegisteredService(final RegisteredService service) {
+        WAClientApp clientApp = new WAClientApp();
+
+        if (service.getAuthenticationPolicy() != null) {
+            DefaultAuthPolicyConf authPolicyConf = new DefaultAuthPolicyConf();
+            DefaultAuthPolicyCriteriaConf criteria = new DefaultAuthPolicyCriteriaConf();
+            criteria.setAll(((DefaultAuthPolicyCriteriaConf) service.getAuthenticationPolicy().getCriteria()).isAll());
+            authPolicyConf.setCriteria(criteria);
+
+            clientApp.setAuthPolicyConf(authPolicyConf);
+        }
+
+        if (service.getAccessStrategy() != null) {
+            DefaultAccessPolicyConf accessPolicyConf = new DefaultAccessPolicyConf();
+            accessPolicyConf.setEnabled(
+                    ((DefaultRegisteredServiceAccessStrategy) service.getAccessStrategy()).isEnabled());
+            accessPolicyConf.setSsoEnabled(
+                    ((DefaultRegisteredServiceAccessStrategy) service.getAccessStrategy()).isSsoEnabled());
+            accessPolicyConf.getRequiredAttributes().putAll(
+                    ((DefaultRegisteredServiceAccessStrategy) service.getAccessStrategy()).getRejectedAttributes());
+
+            clientApp.setAccessPolicyConf(accessPolicyConf);
+        }
+
+        if (service.getAttributeReleasePolicy() != null) {
+
+            if (service.getAttributeReleasePolicy() instanceof ReturnAllowedAttributeReleasePolicy) {
+                ReturnAllowedAttributeReleasePolicy returnAllowedAttributeReleasePolicy =
+                        ReturnAllowedAttributeReleasePolicy.class.cast(service.getAttributeReleasePolicy());
+                AllowedAttrReleasePolicyConf allowedAttrReleasePolicyConf = new AllowedAttrReleasePolicyConf();
+                allowedAttrReleasePolicyConf.getAllowedAttributes().addAll(returnAllowedAttributeReleasePolicy.
+                        getAllowedAttributes());
+
+                clientApp.setAttrReleasePolicyConf(allowedAttrReleasePolicyConf);
+            }
+        }
+
+        if (service instanceof OidcRegisteredService) {
+            OidcRegisteredService oidc = OidcRegisteredService.class.cast(service);
+            OIDCRPTO oidcrpto = new OIDCRPTO();
+
+            oidcrpto.getRedirectUris().addAll(Arrays.asList(oidc.getServiceId().split("|")));
+            oidcrpto.setName(oidc.getName());
+            oidcrpto.setDescription(oidc.getDescription());
+            oidcrpto.setClientId(oidc.getClientId());
+            oidcrpto.setClientSecret(oidc.getClientSecret());
+            oidcrpto.setSignIdToken(oidc.isSignIdToken());
+            oidcrpto.setJwks(oidc.getJwks());
+            oidcrpto.setSubjectType(OIDCSubjectType.valueOf(oidc.getSubjectType()));
+            oidcrpto.getSupportedGrantTypes().addAll(oidc.getSupportedGrantTypes());
+            oidcrpto.getSupportedResponseTypes().addAll(oidc.getSupportedResponseTypes());
+
+            clientApp.setClientAppTO(oidcrpto);
+        } else if (service instanceof SamlRegisteredService) {
+            SamlRegisteredService saml = SamlRegisteredService.class.cast(service);
+            SAML2SPTO saml2spto = new SAML2SPTO();
+
+            saml2spto.setEntityId(saml.getServiceId());
+            saml2spto.setName(saml.getName());
+            saml2spto.setDescription(saml.getDescription());
+
+            saml2spto.setMetadataLocation(saml.getMetadataLocation());
+            saml2spto.setMetadataSignatureLocation(saml.getMetadataSignatureLocation());
+            saml2spto.setSignAssertions(saml.isSignAssertions());
+            saml2spto.setSignResponses(saml.isSignResponses());
+            saml2spto.setEncryptionOptional(saml.isEncryptionOptional());
+            saml2spto.setEncryptAssertions(saml.isEncryptAssertions());
+            saml2spto.setRequiredAuthenticationContextClass(saml.getRequiredAuthenticationContextClass());
+            saml2spto.setRequiredNameIdFormat(SAML2SPNameId.valueOf(saml.getRequiredNameIdFormat()));
+            saml2spto.setSkewAllowance(saml.getSkewAllowance());
+            saml2spto.setNameIdQualifier(saml.getNameIdQualifier());
+            saml2spto.setAssertionAudiences(saml.getAssertionAudiences());
+            saml2spto.setServiceProviderNameIdQualifier(saml.getServiceProviderNameIdQualifier());
+
+            clientApp.setClientAppTO(saml2spto);
+        }
+
+        return clientApp;
+    }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/rest/SyncopeServiceRegistry.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeServiceRegistry.java
similarity index 61%
rename from wa/starter/src/main/java/org/apache/syncope/wa/starter/rest/SyncopeServiceRegistry.java
rename to wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeServiceRegistry.java
index b042262..1eddd9f 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/rest/SyncopeServiceRegistry.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeServiceRegistry.java
@@ -16,16 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.wa.starter.rest;
+package org.apache.syncope.wa.starter;
 
 import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
-import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.types.ClientAppType;
-import org.apache.syncope.common.rest.api.service.RegisteredClientAppService;
 import org.apache.syncope.wa.WARestClient;
-import org.apache.syncope.wa.mapper.RegisteredServiceMapper;
 import org.apereo.cas.services.AbstractServiceRegistry;
 import org.apereo.cas.services.OidcRegisteredService;
 import org.apereo.cas.services.RegisteredService;
@@ -34,6 +31,7 @@ import org.apereo.cas.support.saml.services.SamlRegisteredService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ConfigurableApplicationContext;
+import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
 
 public class SyncopeServiceRegistry extends AbstractServiceRegistry {
 
@@ -41,49 +39,32 @@ public class SyncopeServiceRegistry extends AbstractServiceRegistry {
 
     private final WARestClient restClient;
 
-    private final RegisteredServiceMapper mapper;
-
-    public SyncopeServiceRegistry(final WARestClient restClient,
+    public SyncopeServiceRegistry(
+            final WARestClient restClient,
             final ConfigurableApplicationContext applicationContext,
             final Collection<ServiceRegistryListener> serviceRegistryListeners) {
 
         super(applicationContext, serviceRegistryListeners);
         this.restClient = restClient;
-        this.mapper = new RegisteredServiceMapper();
     }
 
     @Override
     public RegisteredService save(final RegisteredService registeredService) {
-        if (WARestClient.isReady()) {
-            LOG.info("Create application definitions");
-            Response response =
-                    restClient.getSyncopeClient().getService(RegisteredClientAppService.class).create(mapper.
-                            fromRegisteredService(registeredService));
-            if (response.getStatusInfo().getStatusCode() == Response.Status.CREATED.getStatusCode()) {
-                return registeredService;
-            }
-        }
-        LOG.debug("Syncope client is not yet ready to fetch application definitions");
-        return null;
+        throw new UnsupportedOperationException("Saving registered services from WA is not supported");
     }
 
     @Override
     public boolean delete(final RegisteredService registeredService) {
-        if (WARestClient.isReady()) {
-            LOG.info("Delete application definitions");
-            return restClient.getSyncopeClient().getService(RegisteredClientAppService.class).
-                    delete(registeredService.getName());
-        }
-        LOG.debug("Syncope client is not yet ready to fetch application definitions");
-        return false;
+        throw new UnsupportedOperationException("Deleting registered services from WA is not supported");
     }
 
     @Override
     public Collection<RegisteredService> load() {
         if (WARestClient.isReady()) {
             LOG.info("Loading application definitions");
-            return restClient.getSyncopeClient().getService(RegisteredClientAppService.class).list().stream().
-                    map(clientApp -> mapper.toRegisteredService(clientApp)).collect(Collectors.toList());
+            return restClient.getSyncopeClient().getService(WAClientAppService.class).list().stream().
+                    map(clientApp -> RegisteredServiceMapper.toRegisteredService(clientApp)).
+                    collect(Collectors.toList());
         }
         LOG.debug("Syncope client is not yet ready to fetch application definitions");
         return List.of();
@@ -93,8 +74,8 @@ public class SyncopeServiceRegistry extends AbstractServiceRegistry {
     public RegisteredService findServiceById(final long id) {
         if (WARestClient.isReady()) {
             LOG.info("Searching for application definition by id {}", id);
-            return mapper.toRegisteredService(restClient.getSyncopeClient().
-                    getService(RegisteredClientAppService.class).read(id));
+            return RegisteredServiceMapper.toRegisteredService(restClient.getSyncopeClient().
+                    getService(WAClientAppService.class).read(id, null));
         }
         LOG.debug("Syncope client is not yet ready to fetch application definitions");
         return null;
@@ -102,15 +83,15 @@ public class SyncopeServiceRegistry extends AbstractServiceRegistry {
 
     @Override
     @SuppressWarnings("unchecked")
-    public <T extends RegisteredService> T findServiceByExactServiceName(final String name, final Class<T> clazz) {
+    public <T extends RegisteredService> T findServiceById(final long id, final Class<T> clazz) {
         if (WARestClient.isReady()) {
-            LOG.info("Searching for application definition by name {} and type {}", name, clazz);
+            LOG.info("Searching for application definition by id {} and type {}", id, clazz);
             if (clazz.isInstance(OidcRegisteredService.class)) {
-                return (T) mapper.toRegisteredService(restClient.getSyncopeClient().
-                        getService(RegisteredClientAppService.class).read(name, ClientAppType.OIDCRP));
+                return (T) RegisteredServiceMapper.toRegisteredService(restClient.getSyncopeClient().
+                        getService(WAClientAppService.class).read(id, ClientAppType.OIDCRP));
             } else if (clazz.isInstance(SamlRegisteredService.class)) {
-                return (T) mapper.toRegisteredService(restClient.getSyncopeClient().
-                        getService(RegisteredClientAppService.class).read(name, ClientAppType.SAML2SP));
+                return (T) RegisteredServiceMapper.toRegisteredService(restClient.getSyncopeClient().
+                        getService(WAClientAppService.class).read(id, ClientAppType.SAML2SP));
             }
         }
         LOG.debug("Syncope client is not yet ready to fetch application definitions");
@@ -118,31 +99,30 @@ public class SyncopeServiceRegistry extends AbstractServiceRegistry {
     }
 
     @Override
-    public RegisteredService findServiceByExactServiceName(final String name) {
+    @SuppressWarnings("unchecked")
+    public <T extends RegisteredService> T findServiceByExactServiceName(final String name, final Class<T> clazz) {
         if (WARestClient.isReady()) {
-            LOG.info("Searching for application definition by name {}", name);
-            return mapper.toRegisteredService(restClient.getSyncopeClient().
-                    getService(RegisteredClientAppService.class).read(name));
+            LOG.info("Searching for application definition by name {} and type {}", name, clazz);
+            if (clazz.isInstance(OidcRegisteredService.class)) {
+                return (T) RegisteredServiceMapper.toRegisteredService(restClient.getSyncopeClient().
+                        getService(WAClientAppService.class).read(name, ClientAppType.OIDCRP));
+            } else if (clazz.isInstance(SamlRegisteredService.class)) {
+                return (T) RegisteredServiceMapper.toRegisteredService(restClient.getSyncopeClient().
+                        getService(WAClientAppService.class).read(name, ClientAppType.SAML2SP));
+            }
         }
         LOG.debug("Syncope client is not yet ready to fetch application definitions");
         return null;
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    public <T extends RegisteredService> T findServiceById(final long id, final Class<T> clazz) {
+    public RegisteredService findServiceByExactServiceName(final String name) {
         if (WARestClient.isReady()) {
-            LOG.info("Searching for application definition by id {} and type {}", id, clazz);
-            if (clazz.isInstance(OidcRegisteredService.class)) {
-                return (T) mapper.toRegisteredService(restClient.getSyncopeClient().
-                        getService(RegisteredClientAppService.class).read(id, ClientAppType.OIDCRP));
-            } else if (clazz.isInstance(SamlRegisteredService.class)) {
-                return (T) mapper.toRegisteredService(restClient.getSyncopeClient().
-                        getService(RegisteredClientAppService.class).read(id, ClientAppType.SAML2SP));
-            }
+            LOG.info("Searching for application definition by name {}", name);
+            return RegisteredServiceMapper.toRegisteredService(restClient.getSyncopeClient().
+                    getService(WAClientAppService.class).read(name, null));
         }
         LOG.debug("Syncope client is not yet ready to fetch application definitions");
         return null;
     }
-
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
index 887ad97..1b43386 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
@@ -18,12 +18,14 @@
  */
 package org.apache.syncope.wa.starter;
 
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Date;
+import org.apache.commons.lang.StringUtils;
 import org.apereo.cas.configuration.CasConfigurationProperties;
 import org.apereo.cas.configuration.CasConfigurationPropertiesValidator;
 import org.apereo.cas.util.AsciiArtUtils;
 import org.apereo.cas.util.DateTimeUtils;
-
-import org.apache.commons.lang.StringUtils;
 import org.quartz.Job;
 import org.quartz.JobBuilder;
 import org.quartz.JobDetail;
@@ -62,10 +64,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.quartz.SchedulerFactoryBean;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
 
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.Date;
-
 @PropertySource("classpath:wa.properties")
 @PropertySource(value = "file:${conf.directory}/wa.properties", ignoreResourceNotFound = true)
 @SpringBootApplication(exclude = {
@@ -112,7 +110,7 @@ public class SyncopeWAApplication extends SpringBootServletInitializer {
     private static void validateConfiguration(final ApplicationReadyEvent event) {
         if (!Boolean.getBoolean("SKIP_CONFIG_VALIDATION")) {
             CasConfigurationPropertiesValidator validator =
-                new CasConfigurationPropertiesValidator(event.getApplicationContext());
+                    new CasConfigurationPropertiesValidator(event.getApplicationContext());
             validator.validate();
         }
     }
@@ -132,7 +130,7 @@ public class SyncopeWAApplication extends SpringBootServletInitializer {
     private void scheduleJobToRefreshContext() {
         try {
             Date date = Date.from(LocalDateTime.now().plusSeconds(this.contextRefreshDelay).
-                atZone(ZoneId.systemDefault()).toInstant());
+                    atZone(ZoneId.systemDefault()).toInstant());
             Trigger trigger = TriggerBuilder.newTrigger().startAt(date).build();
             JobKey jobKey = new JobKey(getClass().getSimpleName());
 
@@ -144,6 +142,7 @@ public class SyncopeWAApplication extends SpringBootServletInitializer {
     }
 
     private class RefreshApplicationContextJob implements Job {
+
         @Override
         public void execute(final JobExecutionContext jobExecutionContext) {
             try {
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
index 07661e0..938eb39 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
@@ -23,7 +23,6 @@ import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStart;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop;
 import org.apache.syncope.wa.WARestClient;
-import org.apache.syncope.wa.starter.rest.SyncopeServiceRegistry;
 import org.apereo.cas.services.ServiceRegistryExecutionPlanConfigurer;
 import org.apereo.cas.services.ServiceRegistryListener;
 import org.springframework.beans.factory.annotation.Autowired;