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 2022/06/08 16:06:59 UTC

[syncope] branch master updated: [SYNCOPE-1681] Handle LDAP backend conf for Google Authenticator MFA in subclass (#350)

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


The following commit(s) were added to refs/heads/master by this push:
     new 176506ffdb [SYNCOPE-1681] Handle LDAP backend conf for Google Authenticator MFA in subclass (#350)
176506ffdb is described below

commit 176506ffdb82806d01a1a5b1793faaa1e9b1f454
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Wed Jun 8 18:06:54 2022 +0200

    [SYNCOPE-1681] Handle LDAP backend conf for Google Authenticator MFA in subclass (#350)
---
 .../apache/syncope/client/console/pages/WA.java    |   8 +-
 .../console/panels/AuthModuleDirectoryPanel.java   |  16 +-
 .../console/wizards/AuthModuleWizardBuilder.java   |  63 ++++-
 ...eWizardBuilder$GoogleMfaAuthModuleConfLDAP.html |  24 ++
 ...Builder$GoogleMfaAuthModuleConfLDAP.properties} |   9 +-
 ...r$GoogleMfaAuthModuleConfLDAP_fr_CA.properties} |   9 +-
 ...lder$GoogleMfaAuthModuleConfLDAP_it.properties} |   9 +-
 ...lder$GoogleMfaAuthModuleConfLDAP_ja.properties} |   9 +-
 ...r$GoogleMfaAuthModuleConfLDAP_pt_BR.properties} |   9 +-
 ...lder$GoogleMfaAuthModuleConfLDAP_ru.properties} |  10 +-
 client/idrepo/console/pom.xml                      |   1 +
 .../src/test/resources/console-debug.properties    |   3 +-
 .../common/lib/auth/GoogleMfaAuthModuleConf.java   | 201 ++++++++------
 .../bootstrap/SyncopeWAPropertySourceLocator.java  | 308 +++++++++++----------
 .../wa/starter/config/SyncopeWAConfiguration.java  |  34 ++-
 wa/starter/src/main/resources/wa.properties        |   2 +-
 16 files changed, 389 insertions(+), 326 deletions(-)

diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
index be917182a7..5f40a7f9d5 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/pages/WA.java
@@ -62,12 +62,12 @@ public class WA extends BasePage {
 
     private static final long serialVersionUID = 9200112197134882164L;
 
-    private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build();
+    protected static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build();
 
     @SpringBean
-    private ServiceOps serviceOps;
+    protected ServiceOps serviceOps;
 
-    private String waPrefix = "";
+    protected String waPrefix = "";
 
     public WA(final PageParameters parameters) {
         super(parameters);
@@ -139,7 +139,7 @@ public class WA extends BasePage {
         }
     }
 
-    private List<ITab> buildTabList(final List<NetworkService> instances) {
+    protected List<ITab> buildTabList(final List<NetworkService> instances) {
         List<ITab> tabs = new ArrayList<>();
 
         if (SyncopeConsoleSession.get().owns(AMEntitlement.AUTH_MODULE_LIST)) {
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java
index 20512ed35e..ca8054b4b6 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java
@@ -56,7 +56,7 @@ import org.apache.wicket.model.StringResourceModel;
 public class AuthModuleDirectoryPanel
         extends DirectoryPanel<AuthModuleTO, AuthModuleTO, AuthModuleProvider, AuthModuleRestClient> {
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 1005345990563741296L;
 
     public AuthModuleDirectoryPanel(final String id, final PageReference pageRef) {
         super(id, pageRef);
@@ -100,14 +100,14 @@ public class AuthModuleDirectoryPanel
 
             @Override
             public void populateItem(
-                final Item<ICellPopulator<AuthModuleTO>> item,
-                final String componentId,
-                final IModel<AuthModuleTO> rowModel) {
+                    final Item<ICellPopulator<AuthModuleTO>> item,
+                    final String componentId,
+                    final IModel<AuthModuleTO> rowModel) {
 
                 item.add(new Label(componentId, rowModel.getObject().getConf() == null
-                    ? StringUtils.EMPTY
-                    : StringUtils.substringBefore(
-                    rowModel.getObject().getConf().getClass().getSimpleName(), "AuthModuleConf")));
+                        ? StringUtils.EMPTY
+                        : StringUtils.substringBefore(
+                                rowModel.getObject().getConf().getClass().getSimpleName(), "AuthModuleConf")));
             }
         });
         return columns;
@@ -124,7 +124,7 @@ public class AuthModuleDirectoryPanel
             @Override
             public void onClick(final AjaxRequestTarget target, final AuthModuleTO ignore) {
                 send(AuthModuleDirectoryPanel.this, Broadcast.EXACT, new AjaxWizard.EditItemActionEvent<>(
-                    AuthModuleRestClient.read(model.getObject().getKey()), target));
+                        AuthModuleRestClient.read(model.getObject().getKey()), target));
             }
         }, ActionLink.ActionType.EDIT, AMEntitlement.AUTH_MODULE_UPDATE);
 
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
index 5b7e433075..9ee91caf19 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
@@ -27,16 +27,20 @@ import org.apache.syncope.client.console.panels.BeanPanel;
 import org.apache.syncope.client.console.rest.AuthModuleRestClient;
 import org.apache.syncope.client.console.wizards.mapping.AuthModuleMappingPanel;
 import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf;
 import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxEventBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.wizard.WizardModel;
 import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
@@ -44,9 +48,9 @@ import org.springframework.util.ClassUtils;
 
 public class AuthModuleWizardBuilder extends BaseAjaxWizardBuilder<AuthModuleTO> {
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = -6163230263062920394L;
 
-    private final LoadableDetachableModel<List<String>> authModuleConfs;
+    protected final LoadableDetachableModel<List<String>> authModuleConfs;
 
     public AuthModuleWizardBuilder(final AuthModuleTO defaultItem, final PageReference pageRef) {
 
@@ -59,7 +63,7 @@ public class AuthModuleWizardBuilder extends BaseAjaxWizardBuilder<AuthModuleTO>
             @Override
             protected List<String> load() {
                 return SyncopeWebApplication.get().getLookup().getClasses(AuthModuleConf.class).stream().
-                    map(Class::getName).sorted().collect(Collectors.toList());
+                        map(Class::getName).sorted().collect(Collectors.toList());
             }
         };
     }
@@ -78,6 +82,11 @@ public class AuthModuleWizardBuilder extends BaseAjaxWizardBuilder<AuthModuleTO>
     protected WizardModel buildModelSteps(final AuthModuleTO modelObject, final WizardModel wizardModel) {
         wizardModel.add(new Profile(modelObject, authModuleConfs));
         wizardModel.add(new Configuration(modelObject));
+
+        if (modelObject.getConf() instanceof GoogleMfaAuthModuleConf) {
+            wizardModel.add(new GoogleMfaAuthModuleConfLDAP((GoogleMfaAuthModuleConf) modelObject.getConf()));
+        }
+
         wizardModel.add(new Mapping(modelObject));
         return wizardModel;
     }
@@ -130,16 +139,58 @@ public class AuthModuleWizardBuilder extends BaseAjaxWizardBuilder<AuthModuleTO>
         }
     }
 
-    private static class Configuration extends WizardStep {
+    protected static class Configuration extends WizardStep {
 
         private static final long serialVersionUID = -785981096328637758L;
 
         Configuration(final AuthModuleTO authModule) {
-            add(new BeanPanel<>("bean", new PropertyModel<>(authModule, "conf")).setRenderBodyOnly(true));
+            add(new BeanPanel<>("bean", new PropertyModel<>(authModule, "conf"), "ldap").setRenderBodyOnly(true));
+        }
+    }
+
+    protected static class GoogleMfaAuthModuleConfLDAP extends WizardStep {
+
+        private static final long serialVersionUID = 5328049907748683944L;
+
+        GoogleMfaAuthModuleConfLDAP(final GoogleMfaAuthModuleConf authModuleConf) {
+            PropertyModel<GoogleMfaAuthModuleConf.LDAP> beanPanelModel = new PropertyModel<>(authModuleConf, "ldap");
+
+            AjaxCheckBoxPanel enable = new AjaxCheckBoxPanel("enable", "enableLDAP", new IModel<Boolean>() {
+
+                private static final long serialVersionUID = -7126718045816207110L;
+
+                @Override
+                public Boolean getObject() {
+                    return beanPanelModel.getObject() != null;
+                }
+
+                @Override
+                public void setObject(final Boolean object) {
+                    // nothing to do
+                }
+            });
+            enable.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    if (beanPanelModel.getObject() == null) {
+                        beanPanelModel.setObject(new GoogleMfaAuthModuleConf.LDAP());
+                    } else {
+                        beanPanelModel.setObject(null);
+                    }
+                    target.add(GoogleMfaAuthModuleConfLDAP.this);
+                }
+            });
+            add(enable);
+
+            add(new BeanPanel<>("bean", beanPanelModel).setRenderBodyOnly(true));
+            setOutputMarkupId(true);
         }
     }
 
-    private static final class Mapping extends WizardStep {
+    protected static final class Mapping extends WizardStep {
 
         private static final long serialVersionUID = 3454904947720856253L;
 
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP.html
new file mode 100644
index 0000000000..7972709600
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP.html
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <span wicket:id="enable"/>
+    <span wicket:id="bean"/>
+  </wicket:panel>
+</html>
diff --git a/client/idrepo/console/src/test/resources/console-debug.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP.properties
similarity index 73%
copy from client/idrepo/console/src/test/resources/console-debug.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP.properties
index 990acb05b6..3fb8081829 100644
--- a/client/idrepo/console/src/test/resources/console-debug.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP.properties
@@ -14,11 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
-
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
-
-logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
+enableLDAP=Enable LDAP?
diff --git a/client/idrepo/console/src/test/resources/console-debug.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_fr_CA.properties
similarity index 73%
copy from client/idrepo/console/src/test/resources/console-debug.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_fr_CA.properties
index 990acb05b6..3fb8081829 100644
--- a/client/idrepo/console/src/test/resources/console-debug.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_fr_CA.properties
@@ -14,11 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
-
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
-
-logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
+enableLDAP=Enable LDAP?
diff --git a/client/idrepo/console/src/test/resources/console-debug.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_it.properties
similarity index 73%
copy from client/idrepo/console/src/test/resources/console-debug.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_it.properties
index 990acb05b6..083f98e1b1 100644
--- a/client/idrepo/console/src/test/resources/console-debug.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_it.properties
@@ -14,11 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
-
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
-
-logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
+enableLDAP=Abilitare LDAP?
diff --git a/client/idrepo/console/src/test/resources/console-debug.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_ja.properties
similarity index 73%
copy from client/idrepo/console/src/test/resources/console-debug.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_ja.properties
index 990acb05b6..3fb8081829 100644
--- a/client/idrepo/console/src/test/resources/console-debug.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_ja.properties
@@ -14,11 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
-
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
-
-logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
+enableLDAP=Enable LDAP?
diff --git a/client/idrepo/console/src/test/resources/console-debug.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_pt_BR.properties
similarity index 73%
copy from client/idrepo/console/src/test/resources/console-debug.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_pt_BR.properties
index 990acb05b6..3fb8081829 100644
--- a/client/idrepo/console/src/test/resources/console-debug.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_pt_BR.properties
@@ -14,11 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
-
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
-
-logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
+enableLDAP=Enable LDAP?
diff --git a/client/idrepo/console/src/test/resources/console-debug.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_ru.properties
similarity index 73%
copy from client/idrepo/console/src/test/resources/console-debug.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_ru.properties
index 990acb05b6..b03775c055 100644
--- a/client/idrepo/console/src/test/resources/console-debug.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$GoogleMfaAuthModuleConfLDAP_ru.properties
@@ -14,11 +14,5 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
-
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
-
-logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
+#
+enableLDAP=Enable LDAP?
diff --git a/client/idrepo/console/pom.xml b/client/idrepo/console/pom.xml
index c0ba1d60ec..b38f2c22d6 100644
--- a/client/idrepo/console/pom.xml
+++ b/client/idrepo/console/pom.xml
@@ -276,6 +276,7 @@ under the License.
             <artifactId>spring-boot-maven-plugin</artifactId>
             <configuration>
               <jvmArguments>
+                -Djavax.net.ssl.trustStore=${basedir}/../../../fit/wa-reference/src/test/resources/keystore.jks -Djavax.net.ssl.trustStorePassword=password
                 -Dwicket.core.settings.general.configuration-type=development
                 -XX:HotswapAgent=fatjar
                 -Xdebug -Xrunjdwp:transport=dt_socket,address=8003,server=y,suspend=n
diff --git a/client/idrepo/console/src/test/resources/console-debug.properties b/client/idrepo/console/src/test/resources/console-debug.properties
index 990acb05b6..d41b7337e0 100644
--- a/client/idrepo/console/src/test/resources/console-debug.properties
+++ b/client/idrepo/console/src/test/resources/console-debug.properties
@@ -14,7 +14,8 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
+#keymaster.address=http://localhost:9080/syncope/rest/keymaster
+keymaster.address=https://localhost:9443/syncope/rest/keymaster
 keymaster.username=${anonymousUser}
 keymaster.password=${anonymousKey}
 
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleMfaAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleMfaAuthModuleConf.java
index f104575e35..61fb552554 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleMfaAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/GoogleMfaAuthModuleConf.java
@@ -18,10 +18,110 @@
  */
 package org.apache.syncope.common.lib.auth;
 
+import java.io.Serializable;
+
 public class GoogleMfaAuthModuleConf implements AuthModuleConf {
 
     private static final long serialVersionUID = -7883257599139312426L;
 
+    public static class LDAP implements Serializable {
+
+        private static final long serialVersionUID = -7274446267090678730L;
+
+        /**
+         * Name of LDAP attribute that holds GAuth account/credential as JSON.
+         */
+        private String accountAttributeName = "casGAuthRecord";
+
+        /**
+         * Base DN to use. There may be scenarios where different parts of a single LDAP tree
+         * could be considered as base-dns. Each entry can be specified
+         * and joined together using a special delimiter character.
+         */
+        private String baseDn;
+
+        /**
+         * The bind credential to use when connecting to LDAP.
+         */
+        private String bindCredential;
+
+        /**
+         * The bind DN to use when connecting to LDAP.
+         */
+        private String bindDn;
+
+        /**
+         * The LDAP url to the server. More than one may be specified, separated by space and/or comma.
+         */
+        private String url;
+
+        /**
+         * User filter to use for searching. Syntax is i.e. cn={user} or cn={0}.
+         */
+        private String searchFilter;
+
+        /**
+         * Whether subtree searching is allowed.
+         */
+        private boolean subtreeSearch = true;
+
+        public String getAccountAttributeName() {
+            return accountAttributeName;
+        }
+
+        public void setAccountAttributeName(final String accountAttributeName) {
+            this.accountAttributeName = accountAttributeName;
+        }
+
+        public String getBaseDn() {
+            return baseDn;
+        }
+
+        public void setBaseDn(final String baseDn) {
+            this.baseDn = baseDn;
+        }
+
+        public String getBindCredential() {
+            return bindCredential;
+        }
+
+        public void setBindCredential(final String bindCredential) {
+            this.bindCredential = bindCredential;
+        }
+
+        public String getBindDn() {
+            return bindDn;
+        }
+
+        public void setBindDn(final String bindDn) {
+            this.bindDn = bindDn;
+        }
+
+        public String getUrl() {
+            return url;
+        }
+
+        public void setUrl(final String url) {
+            this.url = url;
+        }
+
+        public String getSearchFilter() {
+            return searchFilter;
+        }
+
+        public void setSearchFilter(final String searchFilter) {
+            this.searchFilter = searchFilter;
+        }
+
+        public boolean isSubtreeSearch() {
+            return subtreeSearch;
+        }
+
+        public void setSubtreeSearch(final boolean subtreeSearch) {
+            this.subtreeSearch = subtreeSearch;
+        }
+    }
+
     /**
      * Issuer used in the barcode when dealing with device registration events.
      * Used in the registration URL to identify CAS.
@@ -52,98 +152,7 @@ public class GoogleMfaAuthModuleConf implements AuthModuleConf {
      */
     private int windowSize = 3;
 
-    /**
-     * Name of LDAP attribute that holds GAuth account/credential as JSON.
-     */
-    private String ldapAccountAttributeName = "casGAuthRecord";
-
-    /**
-     * Base DN to use. There may be scenarios where different parts of a single LDAP tree
-     * could be considered as base-dns. Each entry can be specified
-     * and joined together using a special delimiter character.
-     */
-    private String ldapBaseDn;
-
-    /**
-     * The bind credential to use when connecting to LDAP.
-     */
-    private String ldapBindCredential;
-
-    /**
-     * The bind DN to use when connecting to LDAP.
-     */
-    private String ldapBindDn;
-
-    /**
-     * The LDAP url to the server. More than one may be specified, separated by space and/or comma.
-     */
-    private String ldapUrl;
-
-    /**
-     * User filter to use for searching. Syntax is i.e.  cn={user} or cn={0}.
-     */
-    private String ldapSearchFilter;
-
-    /**
-     * Whether subtree searching is allowed.
-     */
-    private boolean ldapSubtreeSearch = true;
-
-    public String getLdapAccountAttributeName() {
-        return ldapAccountAttributeName;
-    }
-
-    public void setLdapAccountAttributeName(final String ldapAccountAttributeName) {
-        this.ldapAccountAttributeName = ldapAccountAttributeName;
-    }
-
-    public String getLdapBaseDn() {
-        return ldapBaseDn;
-    }
-
-    public void setLdapBaseDn(final String ldapBaseDn) {
-        this.ldapBaseDn = ldapBaseDn;
-    }
-
-    public String getLdapBindCredential() {
-        return ldapBindCredential;
-    }
-
-    public void setLdapBindCredential(final String ldapBindCredential) {
-        this.ldapBindCredential = ldapBindCredential;
-    }
-
-    public String getLdapBindDn() {
-        return ldapBindDn;
-    }
-
-    public void setLdapBindDn(final String ldapBindDn) {
-        this.ldapBindDn = ldapBindDn;
-    }
-
-    public String getLdapUrl() {
-        return ldapUrl;
-    }
-
-    public void setLdapUrl(final String ldapUrl) {
-        this.ldapUrl = ldapUrl;
-    }
-
-    public String getLdapSearchFilter() {
-        return ldapSearchFilter;
-    }
-
-    public void setLdapSearchFilter(final String ldapSearchFilter) {
-        this.ldapSearchFilter = ldapSearchFilter;
-    }
-
-    public boolean isLdapSubtreeSearch() {
-        return ldapSubtreeSearch;
-    }
-
-    public void setLdapSubtreeSearch(final boolean ldapSubtreeSearch) {
-        this.ldapSubtreeSearch = ldapSubtreeSearch;
-    }
+    private LDAP ldap;
 
     public String getIssuer() {
         return issuer;
@@ -184,4 +193,12 @@ public class GoogleMfaAuthModuleConf implements AuthModuleConf {
     public void setWindowSize(final int windowSize) {
         this.windowSize = windowSize;
     }
+
+    public LDAP getLdap() {
+        return ldap;
+    }
+
+    public void setLdap(final LDAP ldap) {
+        this.ldap = ldap;
+    }
 }
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
index c7d514c799..059af19a50 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
@@ -74,35 +74,35 @@ import org.springframework.core.env.PropertySource;
 @Order
 public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAPropertySourceLocator.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(SyncopeWAPropertySourceLocator.class);
 
-    private final WARestClient waRestClient;
+    protected final WARestClient waRestClient;
 
     public SyncopeWAPropertySourceLocator(final WARestClient waRestClient) {
         this.waRestClient = waRestClient;
     }
 
-    private static SimpleFilterProvider getParentCasFilterProvider() {
+    protected SimpleFilterProvider getParentCasFilterProvider() {
         return new SimpleFilterProvider().
-            setFailOnUnknownId(false).
-            addFilter(CasConfigurationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        CasConfigurationProperties.class,
-                        CasConfigurationProperties::getAuthn)));
+                setFailOnUnknownId(false).
+                addFilter(CasConfigurationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        CasConfigurationProperties.class,
+                                        CasConfigurationProperties::getAuthn)));
     }
 
-    private static Map<String, Object> filterCasProperties(
-        final CasConfigurationProperties casProperties,
-        final SimpleFilterProvider filters) {
+    protected Map<String, Object> filterCasProperties(
+            final CasConfigurationProperties casProperties,
+            final SimpleFilterProvider filters) {
 
         return CasCoreConfigurationUtils.asMap(casProperties.withHolder(), filters);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final SyncopeAuthModuleConf conf,
-        final String address) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final SyncopeAuthModuleConf conf,
+            final String address) {
 
         SyncopeAuthenticationProperties syncopeProps = new SyncopeAuthenticationProperties();
         syncopeProps.setName(authModule);
@@ -114,22 +114,22 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
-            SimpleBeanPropertyFilter.filterOutAllExcept(
-                CasCoreConfigurationUtils.getPropertyName(
-                    AuthenticationProperties.class,
-                    AuthenticationProperties::getSyncope)));
+                SimpleBeanPropertyFilter.filterOutAllExcept(
+                        CasCoreConfigurationUtils.getPropertyName(
+                                AuthenticationProperties.class,
+                                AuthenticationProperties::getSyncope)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final StaticAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final StaticAuthModuleConf conf) {
 
         AcceptAuthenticationProperties staticProps = new AcceptAuthenticationProperties();
         staticProps.setName(authModule);
         String users = conf.getUsers().entrySet().stream().
-            map(entry -> entry.getKey() + "::" + entry.getValue()).
-            collect(Collectors.joining(","));
+                map(entry -> entry.getKey() + "::" + entry.getValue()).
+                collect(Collectors.joining(","));
         staticProps.setUsers(users);
 
         CasConfigurationProperties casProperties = new CasConfigurationProperties();
@@ -137,16 +137,16 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
-            SimpleBeanPropertyFilter.filterOutAllExcept(
-                CasCoreConfigurationUtils.getPropertyName(
-                    AuthenticationProperties.class,
-                    AuthenticationProperties::getAccept)));
+                SimpleBeanPropertyFilter.filterOutAllExcept(
+                        CasCoreConfigurationUtils.getPropertyName(
+                                AuthenticationProperties.class,
+                                AuthenticationProperties::getAccept)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final LDAPAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final LDAPAuthModuleConf conf) {
 
         LdapAuthenticationProperties ldapProps = new LdapAuthenticationProperties();
         ldapProps.setName(authModule);
@@ -167,19 +167,18 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.addFilter(
-            AuthenticationProperties.class.getSimpleName(),
-            SimpleBeanPropertyFilter.filterOutAllExcept(
-                CasCoreConfigurationUtils.getPropertyName(
-                    AuthenticationProperties.class,
-                    AuthenticationProperties::getLdap)));
+                AuthenticationProperties.class.getSimpleName(),
+                SimpleBeanPropertyFilter.filterOutAllExcept(
+                        CasCoreConfigurationUtils.getPropertyName(
+                                AuthenticationProperties.class,
+                                AuthenticationProperties::getLdap)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-
     @SuppressWarnings("deprecation")
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final DuoMfaAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final DuoMfaAuthModuleConf conf) {
 
         DuoSecurityMultifactorAuthenticationProperties props = new DuoSecurityMultifactorAuthenticationProperties();
         props.setName(authModule);
@@ -193,23 +192,25 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.
-            addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        AuthenticationProperties.class,
-                        AuthenticationProperties::getMfa))).
-            addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        MultifactorAuthenticationProperties.class,
-                        MultifactorAuthenticationProperties::getDuo)));
+                addFilter(AuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        AuthenticationProperties.class,
+                                        AuthenticationProperties::getMfa))).
+                addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        MultifactorAuthenticationProperties.class,
+                                        MultifactorAuthenticationProperties::getDuo)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(final String authModule,
-                                                     final SimpleMfaAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final SimpleMfaAuthModuleConf conf) {
+
         CasSimpleMultifactorAuthenticationProperties props =
-            new CasSimpleMultifactorAuthenticationProperties();
+                new CasSimpleMultifactorAuthenticationProperties();
 
         props.setName(authModule);
         props.setTokenLength(conf.getTokenLength());
@@ -219,38 +220,37 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
         props.getMail().setSubject(conf.getEmailSubject());
         props.getMail().setText(conf.getEmailText());
 
-        try {
-            if (StringUtils.isNotBlank(conf.getBypassGroovyScript())) {
+        if (StringUtils.isNotBlank(conf.getBypassGroovyScript())) {
+            try {
                 props.getBypass().getGroovy().setLocation(ResourceUtils.getResourceFrom(conf.getBypassGroovyScript()));
+            } catch (Exception e) {
+                LOG.error("Unable to load groovy script for bypass", e);
+                throw new IllegalArgumentException(e);
             }
-        } catch (final Exception e) {
-            LOG.error("Unable to load groovy script for bypass", e);
-            throw new IllegalArgumentException(e);
         }
         CasConfigurationProperties casProperties = new CasConfigurationProperties();
         casProperties.getAuthn().getMfa().setSimple(props);
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.
-            addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        AuthenticationProperties.class,
-                        AuthenticationProperties::getMfa))).
-            addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        MultifactorAuthenticationProperties.class,
-                        MultifactorAuthenticationProperties::getSimple)));
+                addFilter(AuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        AuthenticationProperties.class,
+                                        AuthenticationProperties::getMfa))).
+                addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        MultifactorAuthenticationProperties.class,
+                                        MultifactorAuthenticationProperties::getSimple)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final GoogleMfaAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final GoogleMfaAuthModuleConf conf) {
 
-        GoogleAuthenticatorMultifactorProperties gauthProps =
-            new GoogleAuthenticatorMultifactorProperties();
+        GoogleAuthenticatorMultifactorProperties gauthProps = new GoogleAuthenticatorMultifactorProperties();
         gauthProps.setName(authModule);
         gauthProps.getCore().setIssuer(conf.getIssuer());
         gauthProps.getCore().setCodeDigits(conf.getCodeDigits());
@@ -258,14 +258,16 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
         gauthProps.getCore().setTimeStepSize(conf.getTimeStepSize());
         gauthProps.getCore().setWindowSize(conf.getWindowSize());
 
-        LdapGoogleAuthenticatorMultifactorProperties ldapProps = new LdapGoogleAuthenticatorMultifactorProperties();
-        ldapProps.setAccountAttributeName(conf.getLdapAccountAttributeName());
-        ldapProps.setBaseDn(conf.getLdapBaseDn());
-        ldapProps.setBindCredential(conf.getLdapBindCredential());
-        ldapProps.setBindDn(conf.getLdapBindDn());
-        ldapProps.setSearchFilter(conf.getLdapSearchFilter());
-        ldapProps.setLdapUrl(conf.getLdapUrl());
-        gauthProps.setLdap(ldapProps);
+        if (conf.getLdap() != null) {
+            LdapGoogleAuthenticatorMultifactorProperties ldapProps = new LdapGoogleAuthenticatorMultifactorProperties();
+            ldapProps.setAccountAttributeName(conf.getLdap().getAccountAttributeName());
+            ldapProps.setBaseDn(conf.getLdap().getBaseDn());
+            ldapProps.setBindCredential(conf.getLdap().getBindCredential());
+            ldapProps.setBindDn(conf.getLdap().getBindDn());
+            ldapProps.setSearchFilter(conf.getLdap().getSearchFilter());
+            ldapProps.setLdapUrl(conf.getLdap().getUrl());
+            gauthProps.setLdap(ldapProps);
+        }
 
         CasConfigurationProperties casProperties = new CasConfigurationProperties();
         casProperties.getAuthn().getMfa().setGauth(gauthProps);
@@ -274,20 +276,20 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
         filterProvider.addFilter(
                 AuthenticationProperties.class.getSimpleName(),
                 SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        AuthenticationProperties.class,
-                        AuthenticationProperties::getMfa))).
-            addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        MultifactorAuthenticationProperties.class,
-                        MultifactorAuthenticationProperties::getGauth)));
+                        CasCoreConfigurationUtils.getPropertyName(
+                                AuthenticationProperties.class,
+                                AuthenticationProperties::getMfa))).
+                addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        MultifactorAuthenticationProperties.class,
+                                        MultifactorAuthenticationProperties::getGauth)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final U2FAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final U2FAuthModuleConf conf) {
 
         U2FMultifactorAuthenticationProperties props = new U2FMultifactorAuthenticationProperties();
         props.setName(authModule);
@@ -301,22 +303,22 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.
-            addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        AuthenticationProperties.class,
-                        AuthenticationProperties::getMfa))).
-            addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        MultifactorAuthenticationProperties.class,
-                        MultifactorAuthenticationProperties::getU2f)));
+                addFilter(AuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        AuthenticationProperties.class,
+                                        AuthenticationProperties::getMfa))).
+                addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        MultifactorAuthenticationProperties.class,
+                                        MultifactorAuthenticationProperties::getU2f)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final JaasAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final JaasAuthModuleConf conf) {
 
         JaasAuthenticationProperties props = new JaasAuthenticationProperties();
         props.setName(authModule);
@@ -331,16 +333,16 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
-            SimpleBeanPropertyFilter.filterOutAllExcept(
-                CasCoreConfigurationUtils.getPropertyName(
-                    AuthenticationProperties.class,
-                    AuthenticationProperties::getJaas)));
+                SimpleBeanPropertyFilter.filterOutAllExcept(
+                        CasCoreConfigurationUtils.getPropertyName(
+                                AuthenticationProperties.class,
+                                AuthenticationProperties::getJaas)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final JDBCAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final JDBCAuthModuleConf conf) {
 
         QueryJdbcAuthenticationProperties props = new QueryJdbcAuthenticationProperties();
         props.setName(authModule);
@@ -360,22 +362,22 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.
-            addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        AuthenticationProperties.class,
-                        AuthenticationProperties::getJdbc))).
-            addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        JdbcAuthenticationProperties.class,
-                        JdbcAuthenticationProperties::getQuery)));
+                addFilter(AuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        AuthenticationProperties.class,
+                                        AuthenticationProperties::getJdbc))).
+                addFilter(MultifactorAuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        JdbcAuthenticationProperties.class,
+                                        JdbcAuthenticationProperties::getQuery)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final OIDCAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final OIDCAuthModuleConf conf) {
 
         Pac4jGenericOidcClientProperties props = new Pac4jGenericOidcClientProperties();
         props.setId(conf.getId());
@@ -397,22 +399,22 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.
-            addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        AuthenticationProperties.class,
-                        AuthenticationProperties::getPac4j))).
-            addFilter(Pac4jDelegatedAuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        Pac4jDelegatedAuthenticationProperties.class,
-                        Pac4jDelegatedAuthenticationProperties::getOidc)));
+                addFilter(AuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        AuthenticationProperties.class,
+                                        AuthenticationProperties::getPac4j))).
+                addFilter(Pac4jDelegatedAuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        Pac4jDelegatedAuthenticationProperties.class,
+                                        Pac4jDelegatedAuthenticationProperties::getOidc)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
-    private static Map<String, Object> mapAuthModule(
-        final String authModule,
-        final SAML2IdPAuthModuleConf conf) {
+    protected Map<String, Object> mapAuthModule(
+            final String authModule,
+            final SAML2IdPAuthModuleConf conf) {
 
         Pac4jSamlClientProperties props = new Pac4jSamlClientProperties();
         props.setClientName(authModule);
@@ -436,24 +438,24 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
         props.setSignatureReferenceDigestMethods(conf.getSignatureReferenceDigestMethods());
         props.setPrincipalAttributeId(conf.getUserIdAttribute());
         props.setNameIdPolicyAllowCreate(StringUtils.isBlank(conf.getNameIdPolicyAllowCreate())
-            ? TriStateBoolean.UNDEFINED
-            : TriStateBoolean.valueOf(conf.getNameIdPolicyAllowCreate().toUpperCase()));
+                ? TriStateBoolean.UNDEFINED
+                : TriStateBoolean.valueOf(conf.getNameIdPolicyAllowCreate().toUpperCase()));
 
         CasConfigurationProperties casProperties = new CasConfigurationProperties();
         casProperties.getAuthn().getPac4j().getSaml().add(props);
 
         SimpleFilterProvider filterProvider = getParentCasFilterProvider();
         filterProvider.
-            addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        AuthenticationProperties.class,
-                        AuthenticationProperties::getPac4j))).
-            addFilter(Pac4jDelegatedAuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                    CasCoreConfigurationUtils.getPropertyName(
-                        Pac4jDelegatedAuthenticationProperties.class,
-                        Pac4jDelegatedAuthenticationProperties::getSaml)));
+                addFilter(AuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        AuthenticationProperties.class,
+                                        AuthenticationProperties::getPac4j))).
+                addFilter(Pac4jDelegatedAuthenticationProperties.class.getSimpleName(),
+                        SimpleBeanPropertyFilter.filterOutAllExcept(
+                                CasCoreConfigurationUtils.getPropertyName(
+                                        Pac4jDelegatedAuthenticationProperties.class,
+                                        Pac4jDelegatedAuthenticationProperties::getSaml)));
         return filterCasProperties(casProperties, filterProvider);
     }
 
@@ -478,7 +480,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
                 properties.putAll(mapAuthModule(authModuleTO.getKey(), (StaticAuthModuleConf) authConf));
             } else if (authConf instanceof SyncopeAuthModuleConf) {
                 properties.putAll(mapAuthModule(authModuleTO.getKey(),
-                    (SyncopeAuthModuleConf) authConf, syncopeClient.getAddress()));
+                        (SyncopeAuthModuleConf) authConf, syncopeClient.getAddress()));
             } else if (authConf instanceof GoogleMfaAuthModuleConf) {
                 properties.putAll(mapAuthModule(authModuleTO.getKey(), (GoogleMfaAuthModuleConf) authConf));
             } else if (authConf instanceof SimpleMfaAuthModuleConf) {
@@ -499,7 +501,7 @@ public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
         });
 
         syncopeClient.getService(WAConfigService.class).list().
-            forEach(attr -> properties.put(attr.getSchema(), attr.getValues()));
+                forEach(attr -> properties.put(attr.getSchema(), attr.getValues()));
         LOG.debug("Collected WA properties: {}", properties);
         return new MapPropertySource(getClass().getName(), properties);
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
index bc70fc2508..2d8e595166 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
@@ -89,7 +89,6 @@ import org.apereo.cas.util.DateTimeUtils;
 import org.apereo.cas.util.LdapUtils;
 import org.apereo.cas.util.crypto.CipherExecutor;
 import org.apereo.cas.webauthn.storage.WebAuthnCredentialRepository;
-
 import org.ldaptive.ConnectionFactory;
 import org.pac4j.core.client.Client;
 import org.springframework.beans.factory.ObjectProvider;
@@ -246,9 +245,11 @@ public class SyncopeWAConfiguration {
     }
 
     @Bean
-    public CasEventRepository casEventRepository(final WARestClient restClient,
+    public CasEventRepository casEventRepository(
+            final WARestClient restClient,
             @Qualifier("syncopeWAEventRepositoryFilter")
             final CasEventRepositoryFilter syncopeWAEventRepositoryFilter) {
+
         return new SyncopeWAEventRepository(syncopeWAEventRepositoryFilter, restClient);
     }
 
@@ -261,6 +262,7 @@ public class SyncopeWAConfiguration {
     public OneTimeTokenRepository oneTimeTokenAuthenticatorTokenRepository(
             final CasConfigurationProperties casProperties,
             final WARestClient restClient) {
+
         return new SyncopeWAGoogleMfaAuthTokenRepository(
                 restClient, casProperties.getAuthn().getMfa().getGauth().getCore().getTimeStepSize());
     }
@@ -269,22 +271,26 @@ public class SyncopeWAConfiguration {
     @Bean
     public OneTimeTokenCredentialRepository googleAuthenticatorAccountRegistry(
             final CasConfigurationProperties casProperties,
-            @Qualifier("googleAuthenticatorAccountCipherExecutor") final CipherExecutor cipherExecutor,
-            final IGoogleAuthenticator googleAuthenticatorInstance, final WARestClient restClient) {
+            @Qualifier("googleAuthenticatorAccountCipherExecutor")
+            final CipherExecutor<String, String> cipherExecutor,
+            final IGoogleAuthenticator googleAuthenticatorInstance,
+            final WARestClient restClient) {
 
         /*
-        Declaring the LDAP-based repository as a Spring bean that would be conditionally activated
-        via properties using annotations is not possible; conditionally-created spring beans cannot be
-        refreshed, which means the settings ever change and the context is refreshed, the repository
-        option can not be re-created. This could be revisited later in CAS 6.6.x using the {@code BeanSupplier}
-        API construct to recreate the same bean in a more conventional way.
+         * Declaring the LDAP-based repository as a Spring bean that would be conditionally activated
+         * via properties using annotations is not possible; conditionally-created spring beans cannot be
+         * refreshed, which means the settings ever change and the context is refreshed, the repository
+         * option can not be re-created. This could be revisited later in CAS 6.6.x using the {@code BeanSupplier}
+         * API construct to recreate the same bean in a more conventional way.
          */
         LdapGoogleAuthenticatorMultifactorProperties ldap = casProperties.getAuthn().getMfa().getGauth().getLdap();
-        if (StringUtils.isNotBlank(ldap.getBaseDn()) && StringUtils.isNotBlank(ldap.getLdapUrl())
-            && StringUtils.isNotBlank(ldap.getSearchFilter())) {
+        if (StringUtils.isNotBlank(ldap.getBaseDn())
+                && StringUtils.isNotBlank(ldap.getLdapUrl())
+                && StringUtils.isNotBlank(ldap.getSearchFilter())) {
+
             ConnectionFactory connectionFactory = LdapUtils.newLdaptiveConnectionFactory(ldap);
-            return new LdapGoogleAuthenticatorTokenCredentialRepository(cipherExecutor,
-                googleAuthenticatorInstance, connectionFactory, ldap);
+            return new LdapGoogleAuthenticatorTokenCredentialRepository(
+                    cipherExecutor, googleAuthenticatorInstance, connectionFactory, ldap);
         }
         return new SyncopeWAGoogleMfaAuthCredentialRepository(restClient, googleAuthenticatorInstance);
     }
@@ -293,6 +299,7 @@ public class SyncopeWAConfiguration {
     public OidcJsonWebKeystoreGeneratorService oidcJsonWebKeystoreGeneratorService(
             final ConfigurableApplicationContext ctx,
             final WARestClient restClient) {
+
         int size = ctx.getEnvironment().
                 getProperty("cas.authn.oidc.jwks.size", int.class, 2048);
         JWSAlgorithm algorithm = ctx.getEnvironment().
@@ -304,6 +311,7 @@ public class SyncopeWAConfiguration {
     public WebAuthnCredentialRepository webAuthnCredentialRepository(
             final CasConfigurationProperties casProperties,
             final WARestClient restClient) {
+
         return new SyncopeWAWebAuthnCredentialRepository(casProperties, restClient);
     }
 
diff --git a/wa/starter/src/main/resources/wa.properties b/wa/starter/src/main/resources/wa.properties
index f456a429db..c45f82a376 100644
--- a/wa/starter/src/main/resources/wa.properties
+++ b/wa/starter/src/main/resources/wa.properties
@@ -37,7 +37,7 @@ spring.web.resources.static-locations=classpath:/thymeleaf/static,classpath:/syn
 
 cas.monitor.endpoints.endpoint.defaults.access=AUTHENTICATED
 management.endpoints.enabled-by-default=true
-management.endpoints.web.exposure.include=info,health,env,loggers,ssoSessions,registeredServices,refresh
+management.endpoints.web.exposure.include=info,health,env,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies
 management.endpoint.health.show-details=ALWAYS
 spring.cloud.discovery.client.health-indicator.enabled=false