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/10 08:28:06 UTC

[syncope] 04/08: [SYNCOPE-1545] allow wa to lazy-load application context by scheduling a quartz job to retry/refresh the context after startup

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

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit 9a4a1783e9ddae070ed55ef42eafe6be83ee0056
Author: Misagh Moayyed <mm...@apache.org>
AuthorDate: Thu Apr 9 13:38:20 2020 +0200

    [SYNCOPE-1545] allow wa to lazy-load application context by scheduling a quartz job to retry/refresh the context after startup
---
 docker/wa/src/main/resources/wa.properties         |  27 +++
 fit/wa-reference/pom.xml                           |  24 +--
 fit/wa-reference/src/main/resources/wa.properties  |  27 +++
 wa/bootstrap/pom.xml                               |  10 +-
 .../java/org/apache/syncope/wa/WARestClient.java   | 101 ++++++++++
 .../bootstrap/SyncopeWABootstrapConfiguration.java |  52 ++++++
 ...on.java => SyncopeWAPropertySourceLocator.java} |  44 +++--
 .../src/main/resources/META-INF/spring.factories   |   2 +-
 wa/starter/pom.xml                                 |  49 ++++-
 .../syncope/wa/mapper/RegisteredServiceMapper.java | 205 +++++++++++++++++++++
 .../syncope/wa/starter/SyncopeWAApplication.java   |  70 ++++++-
 .../syncope/wa/starter/SyncopeWAConfiguration.java |  30 ++-
 .../wa/starter/rest/SyncopeServiceRegistry.java    | 148 +++++++++++++++
 .../src/main/resources/application.properties      |   6 +-
 wa/starter/src/main/resources/wa.properties        |  19 ++
 15 files changed, 764 insertions(+), 50 deletions(-)

diff --git a/docker/wa/src/main/resources/wa.properties b/docker/wa/src/main/resources/wa.properties
index c44b04e..c98a6e7 100644
--- a/docker/wa/src/main/resources/wa.properties
+++ b/docker/wa/src/main/resources/wa.properties
@@ -23,3 +23,30 @@ useGZIPCompression=true
 conf.directory=${conf.directory}
 cas.standalone.configurationDirectory=${conf.directory}
 cas.authn.oidc.jwks.jwksFile=file:${conf.directory}/oidc.keystore
+
+cas.server.name=http://localhost:8080
+cas.server.prefix=${cas.server.name}/syncope-wa
+cas.server.scope=syncope.org
+
+cas.authn.samlIdp.entityId=https://syncope.apache.org/saml
+cas.authn.samlIdp.metadata.location=file:${conf.directory}
+
+# Disable access to the login endpoint
+# if no target application is specified.
+cas.sso.allow-missing-service-parameter=true
+
+# Disable the acceptable usage policy
+# by default for now.
+cas.acceptableUsagePolicy.enabled=false
+
+management.endpoints.web.exposure.include=health,loggers,refresh
+management.endpoint.health.show-details=always
+
+management.endpoint.health.enabled=true
+management.endpoint.loggers.enabled=true
+management.endpoint.refresh.enabled=true
+
+cas.monitor.endpoints.endpoint.defaults.access=AUTHENTICATED
+
+spring.security.user.name=${anonymousUser}
+spring.security.user.password=${anonymousKey}
diff --git a/fit/wa-reference/pom.xml b/fit/wa-reference/pom.xml
index fa05bad..e3b4dcb 100644
--- a/fit/wa-reference/pom.xml
+++ b/fit/wa-reference/pom.xml
@@ -143,18 +143,18 @@ under the License.
                 <context>syncope</context>
               </properties>
             </deployable>
-            <deployable>
-              <location>${basedir}/../console-reference/target/syncope-fit-console-reference-${project.version}</location>
-              <properties>
-                <context>syncope-console</context>
-              </properties>
-            </deployable>
-            <deployable>
-              <location>${basedir}/../enduser-reference/target/syncope-fit-enduser-reference-${project.version}</location>
-              <properties>
-                <context>syncope-enduser</context>
-              </properties>
-            </deployable>
+<!--            <deployable>-->
+<!--              <location>${basedir}/../console-reference/target/syncope-fit-console-reference-${project.version}</location>-->
+<!--              <properties>-->
+<!--                <context>syncope-console</context>-->
+<!--              </properties>-->
+<!--            </deployable>-->
+<!--            <deployable>-->
+<!--              <location>${basedir}/../enduser-reference/target/syncope-fit-enduser-reference-${project.version}</location>-->
+<!--              <properties>-->
+<!--                <context>syncope-enduser</context>-->
+<!--              </properties>-->
+<!--            </deployable>-->
             <deployable>
               <location>${project.build.directory}/${project.build.finalName}</location>
               <properties>
diff --git a/fit/wa-reference/src/main/resources/wa.properties b/fit/wa-reference/src/main/resources/wa.properties
index c44b04e..c98a6e7 100644
--- a/fit/wa-reference/src/main/resources/wa.properties
+++ b/fit/wa-reference/src/main/resources/wa.properties
@@ -23,3 +23,30 @@ useGZIPCompression=true
 conf.directory=${conf.directory}
 cas.standalone.configurationDirectory=${conf.directory}
 cas.authn.oidc.jwks.jwksFile=file:${conf.directory}/oidc.keystore
+
+cas.server.name=http://localhost:8080
+cas.server.prefix=${cas.server.name}/syncope-wa
+cas.server.scope=syncope.org
+
+cas.authn.samlIdp.entityId=https://syncope.apache.org/saml
+cas.authn.samlIdp.metadata.location=file:${conf.directory}
+
+# Disable access to the login endpoint
+# if no target application is specified.
+cas.sso.allow-missing-service-parameter=true
+
+# Disable the acceptable usage policy
+# by default for now.
+cas.acceptableUsagePolicy.enabled=false
+
+management.endpoints.web.exposure.include=health,loggers,refresh
+management.endpoint.health.show-details=always
+
+management.endpoint.health.enabled=true
+management.endpoint.loggers.enabled=true
+management.endpoint.refresh.enabled=true
+
+cas.monitor.endpoints.endpoint.defaults.access=AUTHENTICATED
+
+spring.security.user.name=${anonymousUser}
+spring.security.user.password=${anonymousKey}
diff --git a/wa/bootstrap/pom.xml b/wa/bootstrap/pom.xml
index 72b7482..9ea1e97 100644
--- a/wa/bootstrap/pom.xml
+++ b/wa/bootstrap/pom.xml
@@ -43,11 +43,19 @@ under the License.
       <artifactId>syncope-client-am-lib</artifactId>
       <version>${project.version}</version>
     </dependency>
-
+    <dependency>
+      <groupId>org.apache.syncope.common.keymaster</groupId>
+      <artifactId>syncope-common-keymaster-client-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-core-configuration-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-core-util-api</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/WARestClient.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/WARestClient.java
new file mode 100644
index 0000000..ca2d048
--- /dev/null
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/WARestClient.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import org.apereo.cas.util.spring.ApplicationContextProvider;
+
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.keymaster.client.api.KeymasterException;
+import org.apache.syncope.common.keymaster.client.api.ServiceOps;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+
+import java.util.Collection;
+
+public class WARestClient {
+
+    private static final Logger LOG = LoggerFactory.getLogger(WARestClient.class);
+
+    private final String anonymousUser;
+
+    private final String anonymousKey;
+
+    private final boolean useGZIPCompression;
+
+    private SyncopeClient client;
+
+    public WARestClient(
+        final String anonymousUser,
+        final String anonymousKey,
+        final boolean useGZIPCompression) {
+
+        this.anonymousUser = anonymousUser;
+        this.anonymousKey = anonymousKey;
+        this.useGZIPCompression = useGZIPCompression;
+    }
+
+    public SyncopeClient getSyncopeClient() {
+        synchronized (this) {
+            if (client == null && isReady()) {
+                try {
+                    client = new SyncopeClientFactoryBean().
+                        setAddress(getCore().getAddress()).
+                        setUseCompression(useGZIPCompression).
+                        create(new AnonymousAuthenticationHandler(anonymousUser, anonymousKey));
+                } catch (Exception e) {
+                    LOG.error("Could not init SyncopeClient", e);
+                }
+            }
+        }
+
+        return client;
+    }
+
+    private static NetworkService getCore() {
+        try {
+            final ApplicationContext context = ApplicationContextProvider.getApplicationContext();
+            if (context == null) {
+                return null;
+            }
+
+            Collection<ServiceOps> serviceOpsList = context.getBeansOfType(ServiceOps.class).values();
+            if (serviceOpsList.isEmpty()) {
+                return null;
+            }
+            ServiceOps serviceOps = serviceOpsList.iterator().next();
+            return serviceOps.get(NetworkService.Type.CORE);
+        } catch (KeymasterException e) {
+            LOG.trace(e.getMessage());
+        }
+        return null;
+    }
+
+    public static boolean isReady() {
+        try {
+            return getCore() != null;
+        } catch (Exception e) {
+            LOG.trace(e.getMessage());
+        }
+        return false;
+    }
+}
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java
new file mode 100644
index 0000000..2330c3f
--- /dev/null
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java
@@ -0,0 +1,52 @@
+/*
+ * 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.bootstrap;
+
+import org.apache.syncope.wa.WARestClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+@Configuration(proxyBeanMethods = false)
+@PropertySource("classpath:wa.properties")
+@PropertySource(value = "file:${conf.directory}/wa.properties", ignoreResourceNotFound = true)
+public class SyncopeWABootstrapConfiguration {
+    @Value("${anonymousUser}")
+    private String anonymousUser;
+
+    @Value("${anonymousKey}")
+    private String anonymousKey;
+
+    @Value("${useGZIPCompression}")
+    private boolean useGZIPCompression;
+
+    @Bean
+    public WARestClient waRestClient() {
+        return new WARestClient(anonymousUser, anonymousKey, useGZIPCompression);
+    }
+
+    @Autowired
+    @Bean
+    public PropertySourceLocator configPropertySourceLocator(final WARestClient waRestClient) {
+        return new SyncopeWAPropertySourceLocator(waRestClient);
+    }
+}
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/RestfulCloudConfigBootstrapConfiguration.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
similarity index 55%
rename from wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/RestfulCloudConfigBootstrapConfiguration.java
rename to wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
index 262931f..22f3669 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/RestfulCloudConfigBootstrapConfiguration.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
@@ -7,7 +7,7 @@
  * "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
+ *    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
@@ -16,36 +16,42 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.syncope.wa.bootstrap;
 
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import java.net.URI;
-import java.util.Map;
-import javax.ws.rs.core.MediaType;
-import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.wa.WARestClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
-import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
 import org.springframework.core.env.Environment;
 import org.springframework.core.env.MapPropertySource;
 import org.springframework.core.env.PropertySource;
 
-@Configuration(value = "restfulCloudConfigBootstrapConfiguration", proxyBeanMethods = false)
-public class RestfulCloudConfigBootstrapConfiguration implements PropertySourceLocator {
+import java.util.HashMap;
+import java.util.Map;
+
+@Order
+public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
+    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWABootstrapConfiguration.class);
 
-    private static final ObjectMapper MAPPER = new ObjectMapper().findAndRegisterModules();
+    private final WARestClient waRestClient;
+
+    public SyncopeWAPropertySourceLocator(final WARestClient waRestClient) {
+        this.waRestClient = waRestClient;
+    }
 
     @Override
     public PropertySource<?> locate(final Environment environment) {
         try {
-            String content = WebClient.create(URI.create("https://demo5926981.mockable.io/casproperties")).
-                    accept(MediaType.APPLICATION_JSON_TYPE).
-                    get().
-                    readEntity(String.class);
-
-            Map<String, Object> payload = MAPPER.readValue(content, new TypeReference<Map<String, Object>>() {
-            });
-            return new MapPropertySource(getClass().getName(), payload);
+            Map<String, Object> properties = new HashMap<>();
+            if (WARestClient.isReady()) {
+                LOG.info("Bootstrapping WA configuration");
+                return new MapPropertySource(getClass().getName(), properties);
+            }
+
+            LOG.warn("Application context is not ready to bootstrap WA configuration");
+            return null;
         } catch (Exception e) {
             throw new IllegalArgumentException("Unable to fetch settings", e);
         }
diff --git a/wa/bootstrap/src/main/resources/META-INF/spring.factories b/wa/bootstrap/src/main/resources/META-INF/spring.factories
index 4cd20b8..0729129 100644
--- a/wa/bootstrap/src/main/resources/META-INF/spring.factories
+++ b/wa/bootstrap/src/main/resources/META-INF/spring.factories
@@ -16,4 +16,4 @@
 # under the License.
 
 org.springframework.cloud.bootstrap.BootstrapConfiguration=\
- org.apache.syncope.wa.bootstrap.RestfulCloudConfigBootstrapConfiguration
+ org.apache.syncope.wa.bootstrap.SyncopeWABootstrapConfiguration
diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml
index 3fa2b65..afd48ac 100644
--- a/wa/starter/pom.xml
+++ b/wa/starter/pom.xml
@@ -90,6 +90,14 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-core-services-registry</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-core-services-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-core-tickets</artifactId>
     </dependency>
     <dependency>
@@ -146,10 +154,30 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-radius</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-radius-mfa</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-saml-idp</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-saml-idp-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-support-oidc</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-oauth-services</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-support-validation</artifactId>
     </dependency>
     <dependency>
@@ -176,6 +204,21 @@ 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.apereo.cas</groupId>
+      <artifactId>cas-server-core-services-authentication</artifactId>
+      <version>6.2.0-SNAPSHOT</version>
+    </dependency>
 
     <dependency>
       <groupId>org.springframework.boot</groupId>
@@ -197,6 +240,11 @@ under the License.
     </dependency>
 
     <dependency>
+      <groupId>org.bouncycastle</groupId>
+      <artifactId>bcpkix-jdk15on</artifactId>
+    </dependency>
+    
+    <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
       <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
       <version>${project.version}</version>
@@ -273,7 +321,6 @@ under the License.
           <groupId>org.apache.syncope.common.keymaster</groupId>
           <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
           <version>${project.version}</version>
-          <scope>compile</scope>
         </dependency>
         <dependency>
           <groupId>org.apache.curator</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..c81d1a7
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/mapper/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.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/SyncopeWAApplication.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
index 4d5d43a..887ad97 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
@@ -24,8 +24,18 @@ 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;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobKey;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
 import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
@@ -43,13 +53,19 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
 import org.springframework.boot.context.event.ApplicationReadyEvent;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.cloud.context.refresh.ContextRefresher;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.context.annotation.PropertySource;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.EnableAsync;
 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 = {
@@ -75,10 +91,32 @@ public class SyncopeWAApplication extends SpringBootServletInitializer {
 
     private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAApplication.class);
 
+    @Autowired
+    private ContextRefresher contextRefresher;
+
+    @Autowired
+    private SchedulerFactoryBean scheduler;
+
+    @Value("${contextRefreshDelay:15}")
+    private long contextRefreshDelay;
+
     public static void main(final String[] args) {
         new SpringApplicationBuilder(SyncopeWAApplication.class).run(args);
     }
 
+    private static void advertiseReady(final ApplicationReadyEvent event) {
+        AsciiArtUtils.printAsciiArtReady(LOG, StringUtils.EMPTY);
+        LOG.info("Ready to process requests @ [{}]", DateTimeUtils.zonedDateTimeOf(event.getTimestamp()));
+    }
+
+    private static void validateConfiguration(final ApplicationReadyEvent event) {
+        if (!Boolean.getBoolean("SKIP_CONFIG_VALIDATION")) {
+            CasConfigurationPropertiesValidator validator =
+                new CasConfigurationPropertiesValidator(event.getApplicationContext());
+            validator.validate();
+        }
+    }
+
     /**
      * Handle application ready event.
      *
@@ -86,13 +124,33 @@ public class SyncopeWAApplication extends SpringBootServletInitializer {
      */
     @EventListener
     public void handleApplicationReadyEvent(final ApplicationReadyEvent event) {
-        if (!Boolean.getBoolean("SKIP_CONFIG_VALIDATION")) {
-            CasConfigurationPropertiesValidator validator =
-                new CasConfigurationPropertiesValidator(event.getApplicationContext());
-            validator.validate();
+        validateConfiguration(event);
+        scheduleJobToRefreshContext();
+        advertiseReady(event);
+    }
+
+    private void scheduleJobToRefreshContext() {
+        try {
+            Date date = Date.from(LocalDateTime.now().plusSeconds(this.contextRefreshDelay).
+                atZone(ZoneId.systemDefault()).toInstant());
+            Trigger trigger = TriggerBuilder.newTrigger().startAt(date).build();
+            JobKey jobKey = new JobKey(getClass().getSimpleName());
+
+            JobDetail job = JobBuilder.newJob(RefreshApplicationContextJob.class).withIdentity(jobKey).build();
+            scheduler.getScheduler().scheduleJob(job, trigger);
+        } catch (SchedulerException e) {
+            throw new RuntimeException("Could not schedule refresh job", e);
         }
+    }
 
-        AsciiArtUtils.printAsciiArtReady(LOG, StringUtils.EMPTY);
-        LOG.info("Ready to process requests @ [{}]", DateTimeUtils.zonedDateTimeOf(event.getTimestamp()));
+    private class RefreshApplicationContextJob implements Job {
+        @Override
+        public void execute(final JobExecutionContext jobExecutionContext) {
+            try {
+                LOG.debug("Refreshed context: {}", contextRefresher.refresh());
+            } catch (final Exception e) {
+                LOG.error(e.getMessage(), e);
+            }
+        }
     }
 }
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 16375c3..07661e0 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
@@ -18,24 +18,36 @@
  */
 package org.apache.syncope.wa.starter;
 
-import java.io.Serializable;
-import org.apereo.cas.services.DefaultRegisteredServiceEntityMapper;
-import org.apereo.cas.services.RegisteredService;
-import org.apereo.cas.services.RegisteredServiceEntityMapper;
+import java.util.Collection;
 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.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+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;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
-@Configuration(proxyBeanMethods = false)
+@Configuration
 public class SyncopeWAConfiguration {
 
+    @Autowired
+    private ConfigurableApplicationContext applicationContext;
+
+    @Autowired
+    @Qualifier("serviceRegistryListeners")
+    private Collection<ServiceRegistryListener> serviceRegistryListeners;
+
+    @Autowired
     @Bean
-    @ConditionalOnProperty(name = "cas.serviceRegistry.rest.url")
-    public RegisteredServiceEntityMapper<RegisteredService, Serializable> registeredServiceEntityMapper() {
-        return new DefaultRegisteredServiceEntityMapper();
+    public ServiceRegistryExecutionPlanConfigurer syncopeServiceRegistryConfigurer(final WARestClient restClient) {
+        SyncopeServiceRegistry registry =
+                new SyncopeServiceRegistry(restClient, applicationContext, serviceRegistryListeners);
+        return plan -> plan.registerServiceRegistry(registry);
     }
 
     @Bean
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
new file mode 100644
index 0000000..b042262
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/rest/SyncopeServiceRegistry.java
@@ -0,0 +1,148 @@
+/*
+ * 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.rest;
+
+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;
+import org.apereo.cas.services.ServiceRegistryListener;
+import org.apereo.cas.support.saml.services.SamlRegisteredService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public class SyncopeServiceRegistry extends AbstractServiceRegistry {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SyncopeServiceRegistry.class);
+
+    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
+    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;
+    }
+
+    @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;
+    }
+
+    @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());
+        }
+        LOG.debug("Syncope client is not yet ready to fetch application definitions");
+        return List.of();
+    }
+
+    @Override
+    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));
+        }
+        LOG.debug("Syncope client is not yet ready to fetch application definitions");
+        return null;
+    }
+
+    @Override
+    @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 {} and type {}", name, clazz);
+            if (clazz.isInstance(OidcRegisteredService.class)) {
+                return (T) mapper.toRegisteredService(restClient.getSyncopeClient().
+                        getService(RegisteredClientAppService.class).read(name, ClientAppType.OIDCRP));
+            } else if (clazz.isInstance(SamlRegisteredService.class)) {
+                return (T) mapper.toRegisteredService(restClient.getSyncopeClient().
+                        getService(RegisteredClientAppService.class).read(name, ClientAppType.SAML2SP));
+            }
+        }
+        LOG.debug("Syncope client is not yet ready to fetch application definitions");
+        return null;
+    }
+
+    @Override
+    public RegisteredService findServiceByExactServiceName(final String name) {
+        if (WARestClient.isReady()) {
+            LOG.info("Searching for application definition by name {}", name);
+            return mapper.toRegisteredService(restClient.getSyncopeClient().
+                    getService(RegisteredClientAppService.class).read(name));
+        }
+        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) {
+        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.debug("Syncope client is not yet ready to fetch application definitions");
+        return null;
+    }
+
+}
diff --git a/wa/starter/src/main/resources/application.properties b/wa/starter/src/main/resources/application.properties
index 5ad1d51..1104f29 100644
--- a/wa/starter/src/main/resources/application.properties
+++ b/wa/starter/src/main/resources/application.properties
@@ -28,9 +28,13 @@ server.servlet.contextPath=/syncope-wa
 
 spring.resources.static-locations=classpath:/thymeleaf/static,classpath:/static
 
-management.endpoints.web.exposure.include=health,loggers
+management.endpoints.web.exposure.include=health,loggers,refresh
 management.endpoint.health.show-details=always
 
+management.endpoint.health.enabled=true
+management.endpoint.loggers.enabled=true
+management.endpoint.refresh.enabled=true
+
 ##
 # Allow configuration classes to override bean definitions from Spring Boot
 #
diff --git a/wa/starter/src/main/resources/wa.properties b/wa/starter/src/main/resources/wa.properties
index 5d1ef1c..c98a6e7 100644
--- a/wa/starter/src/main/resources/wa.properties
+++ b/wa/starter/src/main/resources/wa.properties
@@ -24,6 +24,13 @@ conf.directory=${conf.directory}
 cas.standalone.configurationDirectory=${conf.directory}
 cas.authn.oidc.jwks.jwksFile=file:${conf.directory}/oidc.keystore
 
+cas.server.name=http://localhost:8080
+cas.server.prefix=${cas.server.name}/syncope-wa
+cas.server.scope=syncope.org
+
+cas.authn.samlIdp.entityId=https://syncope.apache.org/saml
+cas.authn.samlIdp.metadata.location=file:${conf.directory}
+
 # Disable access to the login endpoint
 # if no target application is specified.
 cas.sso.allow-missing-service-parameter=true
@@ -31,3 +38,15 @@ cas.sso.allow-missing-service-parameter=true
 # Disable the acceptable usage policy
 # by default for now.
 cas.acceptableUsagePolicy.enabled=false
+
+management.endpoints.web.exposure.include=health,loggers,refresh
+management.endpoint.health.show-details=always
+
+management.endpoint.health.enabled=true
+management.endpoint.loggers.enabled=true
+management.endpoint.refresh.enabled=true
+
+cas.monitor.endpoints.endpoint.defaults.access=AUTHENTICATED
+
+spring.security.user.name=${anonymousUser}
+spring.security.user.password=${anonymousKey}