You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by di...@apache.org on 2020/03/26 08:42:37 UTC

[syncope] branch SYNCOPE-163-1 updated: [SYNCOPE-160] initial WA service implementation

This is an automated email from the ASF dual-hosted git repository.

dimaayash pushed a commit to branch SYNCOPE-163-1
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/SYNCOPE-163-1 by this push:
     new 3936168  [SYNCOPE-160] initial WA service implementation
3936168 is described below

commit 393616890558afab52981559c81a97f351b19e13
Author: dima.ayash <di...@tirasa.net>
AuthorDate: Thu Mar 26 09:42:08 2020 +0100

    [SYNCOPE-160] initial WA service implementation
---
 .../lib/policy/AbstractAccessPolicyConf.java       |   6 +-
 .../common/lib/policy/AccessPolicyConf.java        |   4 +-
 .../common/lib/to/RegisteredClientAppTO.java       |  70 +++++++++++++
 .../syncope/common/lib/types/AMEntitlement.java    |   4 +
 .../api/service/RegisteredClientAppService.java    |  65 ++++++++++++
 .../core/logic/RegisteredClientAppLogic.java       |  50 ++++++++++
 .../service/RegisteredClientAppServiceImpl.java    |  60 +++++++++++
 .../jpa/inner/AbstractClientAppTest.java           |   3 +-
 .../core/persistence/jpa/inner/PolicyTest.java     |   3 +-
 .../api/data/RegisteredClientAppBinder.java        |  23 +----
 .../java/data/RegisteredClientAppBinderImpl.java   | 110 +++++++++++++++++++++
 .../org/apache/syncope/fit/core/PolicyITCase.java  |   6 +-
 .../org/apache/syncope/fit/core/RealmITCase.java   |   3 +-
 wa/starter/pom.xml                                 |  10 ++
 .../syncope/wa/mapper/RegisteredServiceMapper.java | 109 ++++++++++++++++++++
 .../wa/starter/rest/SyncopeServiceRegistry.java    |  11 ++-
 16 files changed, 506 insertions(+), 31 deletions(-)

diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccessPolicyConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccessPolicyConf.java
index cdf5d8e..653fe11 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccessPolicyConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccessPolicyConf.java
@@ -23,8 +23,8 @@ import javax.xml.bind.annotation.XmlSeeAlso;
 import javax.xml.bind.annotation.XmlType;
 import java.io.Serializable;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@@ -43,7 +43,7 @@ public abstract class AbstractAccessPolicyConf implements Serializable, AccessPo
     private boolean ssoEnabled = true;
 
     @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
-    private final Map<String, List<String>> requiredAttributes = new LinkedHashMap<>();
+    private final Map<String, Set<String>> requiredAttributes = new LinkedHashMap<>();
 
     public AbstractAccessPolicyConf() {
         setName(getClass().getName());
@@ -80,7 +80,7 @@ public abstract class AbstractAccessPolicyConf implements Serializable, AccessPo
     @XmlElement(name = "requiredAttribute")
     @JsonProperty("requiredAttributes")
     @Override
-    public Map<String, List<String>> getRequiredAttributes() {
+    public Map<String, Set<String>> getRequiredAttributes() {
         return requiredAttributes;
     }
 
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AccessPolicyConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AccessPolicyConf.java
index 698e171..3cb0505 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AccessPolicyConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AccessPolicyConf.java
@@ -20,8 +20,8 @@ package org.apache.syncope.common.lib.policy;
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import java.io.Serializable;
-import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
 public interface AccessPolicyConf extends Serializable {
@@ -37,5 +37,5 @@ public interface AccessPolicyConf extends Serializable {
 
     boolean isSsoEnabled();
 
-    Map<String, List<String>> getRequiredAttributes();
+    Map<String, Set<String>> getRequiredAttributes();
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/RegisteredClientAppTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/RegisteredClientAppTO.java
new file mode 100644
index 0000000..0a33113
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/RegisteredClientAppTO.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.to;
+
+import java.io.Serializable;
+import org.apache.syncope.common.lib.policy.AccessPolicyConf;
+import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+
+public class RegisteredClientAppTO implements Serializable {
+
+    private static final long serialVersionUID = 6633251825655119506L;
+
+    private ClientAppTO clientAppTO;
+
+    private AccessPolicyConf accessPolicyConf;
+
+    private AuthPolicyTO authPolicyTO;
+
+    private AttrReleasePolicyConf attrReleasePolicyConf;
+
+    public ClientAppTO getClientAppTO() {
+        return clientAppTO;
+    }
+
+    public void setClientAppTO(final ClientAppTO clientAppTO) {
+        this.clientAppTO = clientAppTO;
+    }
+
+    public AccessPolicyConf getAccessPolicyConf() {
+        return accessPolicyConf;
+    }
+
+    public void setAccessPolicyConf(final AccessPolicyConf accessPolicyConf) {
+        this.accessPolicyConf = accessPolicyConf;
+    }
+
+    public AuthPolicyTO getAuthPolicyTO() {
+        return authPolicyTO;
+    }
+
+    public void setAuthPolicyTO(final AuthPolicyTO authPolicyTO) {
+        this.authPolicyTO = authPolicyTO;
+    }
+
+    public AttrReleasePolicyConf getAttrReleasePolicyConf() {
+        return attrReleasePolicyConf;
+    }
+
+    public void setAttrReleasePolicyConf(final AttrReleasePolicyConf attrReleasePolicyConf) {
+        this.attrReleasePolicyConf = attrReleasePolicyConf;
+    }
+
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
index 046f61b..16939e6 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
@@ -56,6 +56,10 @@ public final class AMEntitlement {
 
     private static final Set<String> VALUES;
 
+    public static final String REGISTERED_CLIENT_APP_READ = "REGISTERED_CLIENT_APP_READ";
+
+    public static final String REGISTERED_CLIENT_APP_LIST = "REGISTERED_CLIENT_APP_READ";
+
     static {
         Set<String> values = new TreeSet<>();
         for (Field field : AMEntitlement.class.getDeclaredFields()) {
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RegisteredClientAppService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RegisteredClientAppService.java
new file mode 100644
index 0000000..1a4bba6
--- /dev/null
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RegisteredClientAppService.java
@@ -0,0 +1,65 @@
+/*
+ * 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.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 org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+
+/**
+ * REST operations for resgistered client applications.
+ */
+@Tag(name = "RegisteredClientAppss")
+@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 key registered client application key to be read
+     * @return registered client application with matching key
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    RegisteredClientAppTO read(@NotNull @PathParam("key") String key);
+
+}
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/RegisteredClientAppLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/RegisteredClientAppLogic.java
new file mode 100644
index 0000000..a83b63e
--- /dev/null
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/RegisteredClientAppLogic.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.logic;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.core.provisioning.api.data.RegisteredClientAppBinder;
+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 RegisteredClientAppBinder binder;
+
+    @PreAuthorize("hasRole('" + AMEntitlement.REGISTERED_CLIENT_APP_LIST + "')")
+    @Transactional(readOnly = true)
+    public List<RegisteredClientAppTO> list(final List<ClientAppTO> clientApps) {
+        return clientApps.stream().map(binder::getRegisteredClientAppTO).collect(Collectors.toList());
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.REGISTERED_CLIENT_APP_READ + "')")
+    @Transactional(readOnly = true)
+    public RegisteredClientAppTO read(final ClientAppTO clientApp) {
+        return binder.getRegisteredClientAppTO(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
new file mode 100644
index 0000000..5549c8c
--- /dev/null
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RegisteredClientAppServiceImpl.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.core.logic.ClientAppLogic;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.types.ClientAppType;
+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 ClientAppLogic clientAppLogic;
+
+    @Autowired
+    private RegisteredClientAppLogic logic;
+
+    @Override
+    public List<RegisteredClientAppTO> list() {
+        List<ClientAppTO> applications = new ArrayList<>();
+        Arrays.asList(ClientAppType.values()).forEach(type -> applications.addAll(clientAppLogic.list(type)));
+        return logic.list(applications);
+    }
+
+    @Override
+    public RegisteredClientAppTO read(final String key) {
+        try {
+            return logic.read(clientAppLogic.read(ClientAppType.SAML2SP, key));
+        } catch (NotFoundException e) {
+            return logic.read(clientAppLogic.read(ClientAppType.OIDCRP, key));
+        }
+    }
+
+}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AbstractClientAppTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AbstractClientAppTest.java
index 360e076..9c9c17e 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AbstractClientAppTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AbstractClientAppTest.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.jpa.inner;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf;
 import org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
@@ -71,7 +72,7 @@ public class AbstractClientAppTest extends AbstractTest {
         DefaultAccessPolicyConf conf = new DefaultAccessPolicyConf();
         conf.setEnabled(true);
         conf.setName("Example Access Policy for an application");
-        conf.getRequiredAttributes().putAll(Map.of("attribute1", List.of("value1", "value2")));
+        conf.getRequiredAttributes().putAll(Map.of("attribute1", Set.of("value1", "value2")));
         conf.setSsoEnabled(false);
 
         Implementation type = entityFactory.newEntity(Implementation.class);
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
index 304d97a..09b5a67 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
@@ -55,6 +55,7 @@ import java.util.UUID;
 
 import static org.junit.jupiter.api.Assertions.*;
 
+import java.util.Set;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 
 @Transactional("Master")
@@ -207,7 +208,7 @@ public class PolicyTest extends AbstractTest {
         accessPolicy.setDescription("This is a sample attr release policy that releases everything");
 
         DefaultAccessPolicyConf conf = new DefaultAccessPolicyConf();
-        conf.getRequiredAttributes().putAll(Map.of("cn", List.of("syncope")));
+        conf.getRequiredAttributes().putAll(Map.of("cn", Set.of("syncope")));
         conf.setName("AttrReleasePolicyAllowEverything");
 
         Implementation type = entityFactory.newEntity(Implementation.class);
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AccessPolicyConf.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java
similarity index 58%
copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AccessPolicyConf.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java
index 698e171..7a10d03 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/AccessPolicyConf.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/RegisteredClientAppBinder.java
@@ -16,26 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.policy;
+package org.apache.syncope.core.provisioning.api.data;
 
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import java.io.Serializable;
-import java.util.List;
-import java.util.Map;
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
 
-@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
-public interface AccessPolicyConf extends Serializable {
+public interface RegisteredClientAppBinder {
 
-    /**
-     * Give name of related access policy.
-     *
-     * @return name of this access policy
-     */
-    String getName();
+    RegisteredClientAppTO getRegisteredClientAppTO(ClientAppTO clientAppTO);
 
-    boolean isEnabled();
-
-    boolean isSsoEnabled();
-
-    Map<String, List<String>> getRequiredAttributes();
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RegisteredClientAppBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RegisteredClientAppBinderImpl.java
new file mode 100644
index 0000000..151670b
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RegisteredClientAppBinderImpl.java
@@ -0,0 +1,110 @@
+/*
+ * 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.SyncopeClientException;
+import org.apache.syncope.common.lib.to.RegisteredClientAppTO;
+import org.apache.syncope.common.lib.to.client.ClientAppTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
+import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.Policy;
+import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder;
+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 PolicyDAO policyDAO;
+
+    @Autowired
+    private PolicyDataBinder policyDataBinder;
+
+    @Override
+    public RegisteredClientAppTO getRegisteredClientAppTO(final ClientAppTO clientAppTO) {
+        RegisteredClientAppTO registeredClientAppTO = new RegisteredClientAppTO();
+        registeredClientAppTO.setClientAppTO(clientAppTO);
+
+        if (clientAppTO.getAuthPolicy() == null) {
+            clientAppTO.setAuthPolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAuthPolicy());
+            if (policy instanceof AuthPolicy) {
+                registeredClientAppTO.setAuthPolicyTO(policyDataBinder.getPolicyTO(policy));
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AuthPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
+        if (clientAppTO.getAccessPolicy() == null) {
+            clientAppTO.setAccessPolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAccessPolicy());
+            if (policy instanceof AccessPolicy) {
+                try {
+                    registeredClientAppTO.setAccessPolicyConf(ImplementationManager.build(((AccessPolicy) policy).
+                            getConfigurations().get(0)));
+                } catch (Exception e) {
+                    LOG.error("While building {}", ((AccessPolicy) policy).getConfigurations().get(0), e);
+                }
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AccessPolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
+        if (clientAppTO.getAttrReleasePolicy() == null) {
+            clientAppTO.setAttrReleasePolicy(null);
+        } else {
+            Policy policy = policyDAO.find(clientAppTO.getAttrReleasePolicy());
+            if (policy instanceof AttrReleasePolicy) {
+                try {
+                    registeredClientAppTO.setAttrReleasePolicyConf(ImplementationManager.build(
+                            ((AttrReleasePolicy) policy).getConfigurations().get(0)));
+                } catch (Exception e) {
+                    LOG.error("While building {}", ((AttrReleasePolicy) policy).getConfigurations().get(0), e);
+                }
+
+            } else {
+                SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPolicy);
+                sce.getElements().add("Expected " + AttrReleasePolicy.class.getSimpleName()
+                        + ", found " + policy.getClass().getSimpleName());
+                throw sce;
+            }
+        }
+
+        return registeredClientAppTO;
+    }
+
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
index 8aa015b..4b65c75 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
@@ -143,7 +143,7 @@ public class PolicyITCase extends AbstractITCase {
                 DefaultAccessPolicyConf conf = new DefaultAccessPolicyConf();
                 conf.setEnabled(true);
                 conf.setName("TestAccessPolicyConf");
-                conf.getRequiredAttributes().put("cn", List.of("admin", "Admin", "TheAdmin"));
+                conf.getRequiredAttributes().put("cn", Set.of("admin", "Admin", "TheAdmin"));
                 implementationTO.setBody(POJOHelper.serialize(conf));
 
                 Response response = implementationService.create(implementationTO);
@@ -373,8 +373,8 @@ public class PolicyITCase extends AbstractITCase {
         DefaultAccessPolicyConf accessPolicyConf =
                 POJOHelper.deserialize(accessPolicyImplementationTO.getBody(), DefaultAccessPolicyConf.class);
         assertNotNull(accessPolicyConf);
-        accessPolicyConf.getRequiredAttributes().put("ou", List.of("test"));
-        accessPolicyConf.getRequiredAttributes().put("cn", List.of("admin", "Admin"));
+        accessPolicyConf.getRequiredAttributes().put("ou", Set.of("test"));
+        accessPolicyConf.getRequiredAttributes().put("cn", Set.of("admin", "Admin"));
         accessPolicyImplementationTO.setBody(POJOHelper.serialize(accessPolicyConf));
 
         // update new authentication policy
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
index 7c1e252..8a0d9b2 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
@@ -27,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.fail;
 
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.UUID;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.Response;
@@ -265,7 +266,7 @@ public class RealmITCase extends AbstractITCase {
         DefaultAccessPolicyConf ruleConf = new DefaultAccessPolicyConf();
         ruleConf.setEnabled(true);
         ruleConf.setName("TestAccessPolicyConf" + getUUIDString());
-        ruleConf.getRequiredAttributes().put("cn", List.of("admin", "Admin", "TheAdmin"));
+        ruleConf.getRequiredAttributes().put("cn", Set.of("admin", "Admin", "TheAdmin"));
 
         ImplementationTO rule = new ImplementationTO();
         rule.setKey("TestAccessPolicy" + getUUIDString());
diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml
index 2e2c7dc..144ddf9 100644
--- a/wa/starter/pom.xml
+++ b/wa/starter/pom.xml
@@ -201,6 +201,16 @@ under the License.
       <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-webapp-config</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-oidc-services</artifactId>
+      <version>6.2.0-SNAPSHOT</version>
+    </dependency>   
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-core-authentication-attributes</artifactId>
+      <version>6.2.0-SNAPSHOT</version>
+    </dependency>
 
     <dependency>
       <groupId>org.springframework.boot</groupId>
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
new file mode 100644
index 0000000..855629f
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/mapper/RegisteredServiceMapper.java
@@ -0,0 +1,109 @@
+/*
+ * 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.HashSet;
+import org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf;
+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.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.RegisteredServiceAuthenticationPolicy;
+import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
+import org.apereo.cas.support.saml.services.SamlRegisteredService;
+
+public class RegisteredServiceMapper {
+
+    public RegisteredService toRegisteredService(final RegisteredClientAppTO clientApp) {
+
+        RegisteredServiceAuthenticationPolicy authenticationPolicy = new DefaultRegisteredServiceAuthenticationPolicy();
+
+        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) {
+            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) clientApp.getClientAppTO();
+            OidcRegisteredService registeredService = new OidcRegisteredService();
+            registeredService.setServiceId(rp.getKey());
+            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.setSupportedGrantTypes((HashSet<String>) rp.getSupportedGrantTypes());
+            registeredService.setSupportedResponseTypes((HashSet<String>) rp.getSupportedResponseTypes());
+
+            return registeredService;
+        } else if (clientApp.getClientAppTO() instanceof SAML2SPTO) {
+            SAML2SPTO sp = (SAML2SPTO) clientApp.getClientAppTO();
+            SamlRegisteredService registeredService = new SamlRegisteredService();
+            registeredService.setServiceId(sp.getKey());
+            registeredService.setName(sp.getName());
+            registeredService.setDescription(sp.getDescription());
+            registeredService.setAccessStrategy(accessStrategy);
+            registeredService.setAuthenticationPolicy(authenticationPolicy);
+            registeredService.setAttributeReleasePolicy(attributeReleasePolicy);
+
+            registeredService.setIssuerEntityId(sp.getEntityId());
+            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 ClientAppTO fromRegisteredService(final RegisteredService registeredService) {
+        return null;
+    }
+}
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/rest/SyncopeServiceRegistry.java
index 64773eb..2a64794 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/rest/SyncopeServiceRegistry.java
@@ -19,8 +19,10 @@
 package org.apache.syncope.wa.starter.rest;
 
 import java.util.Collection;
-import java.util.List;
+import java.util.stream.Collectors;
+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.RegisteredService;
 import org.apereo.cas.services.ServiceRegistryListener;
@@ -34,11 +36,14 @@ public class SyncopeServiceRegistry extends AbstractServiceRegistry {
 
     private final WARestClient restClient;
 
+    private final RegisteredServiceMapper mapper;
+
     public SyncopeServiceRegistry(final WARestClient restClient,
             final ConfigurableApplicationContext applicationContext,
             final Collection<ServiceRegistryListener> serviceRegistryListeners) {
         super(applicationContext, serviceRegistryListeners);
         this.restClient = restClient;
+        this.mapper = new RegisteredServiceMapper();
     }
 
     @Override
@@ -54,7 +59,9 @@ public class SyncopeServiceRegistry extends AbstractServiceRegistry {
     @Override
     public Collection<RegisteredService> load() {
         LOG.info("Loading application definitions");
-        return List.of();
+
+        return restClient.getSyncopeClient().getService(RegisteredClientAppService.class).list().stream().
+                map(clientApp -> mapper.toRegisteredService(clientApp)).collect(Collectors.toList());
     }
 
     @Override