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/24 15:41:30 UTC

[syncope] branch master updated: [SYNCOPE-1682] Introducing AttrRepo entity for AttrReleasePolicy (#355)

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 c7e7c1b4ce [SYNCOPE-1682] Introducing AttrRepo entity for AttrReleasePolicy (#355)
c7e7c1b4ce is described below

commit c7e7c1b4cee0ca69866989f47797d5cec6232287
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Fri Jun 24 17:41:25 2022 +0200

    [SYNCOPE-1682] Introducing AttrRepo entity for AttrReleasePolicy (#355)
---
 .../AMClassPathScanImplementationContributor.java  |   5 +
 .../apache/syncope/client/console/pages/WA.java    |  13 +
 ...ctoryPanel.java => AttrRepoDirectoryPanel.java} |  66 +--
 .../console/panels/AuthModuleDirectoryPanel.java   |   2 +
 .../policies/AttrReleasePolicyModalPanel.java      |  58 +++
 .../console/policies/AuthPolicyModalPanel.java     |   2 +-
 .../client/console/rest/AttrRepoRestClient.java    |  48 +++
 .../console/wizards/AttrRepoWizardBuilder.java     | 177 ++++++++
 .../console/wizards/AuthModuleWizardBuilder.java   |  15 +
 ...MappingPanel.java => AttrRepoMappingPanel.java} |  10 +-
 .../wizards/mapping/AuthModuleMappingPanel.java    |   2 +-
 .../syncope/client/console/pages/WA.properties     |   1 +
 .../client/console/pages/WA_fr_CA.properties       |   1 +
 .../syncope/client/console/pages/WA_it.properties  |   1 +
 .../syncope/client/console/pages/WA_ja.properties  |   1 +
 .../client/console/pages/WA_pt_BR.properties       |   1 +
 .../syncope/client/console/pages/WA_ru.properties  |   1 +
 ...roperties => AttrRepoDirectoryPanel.properties} |   6 +-
 ...ies => AttrRepoDirectoryPanel_fr_CA.properties} |   6 +-
 ...erties => AttrRepoDirectoryPanel_it.properties} |   6 +-
 ...erties => AttrRepoDirectoryPanel_ja.properties} |   6 +-
 ...ies => AttrRepoDirectoryPanel_pt_BR.properties} |   6 +-
 ...erties => AttrRepoDirectoryPanel_ru.properties} |   6 +-
 .../panels/AuthModuleDirectoryPanel.properties     |   2 +
 .../AuthModuleDirectoryPanel_fr_CA.properties      |   2 +
 .../panels/AuthModuleDirectoryPanel_it.properties  |   2 +
 .../panels/AuthModuleDirectoryPanel_ja.properties  |   2 +
 .../AuthModuleDirectoryPanel_pt_BR.properties      |   2 +
 .../panels/AuthModuleDirectoryPanel_ru.properties  |   2 +
 .../policies/AttrReleasePolicyModalPanel.html      |  18 +
 ...ml => AttrRepoWizardBuilder$Configuration.html} |   4 +-
 ...AttrRepoWizardBuilder$Configuration.properties} |  15 +-
 ...poWizardBuilder$Configuration_fr_CA.properties} |  15 +-
 ...rRepoWizardBuilder$Configuration_it.properties} |  15 +-
 ...rRepoWizardBuilder$Configuration_ja.properties} |  15 +-
 ...poWizardBuilder$Configuration_pt_BR.properties} |  15 +-
 ...trRepoWizardBuilder$Configuration_ru.properties |  25 ++
 ...ile.html => AttrRepoWizardBuilder$Mapping.html} |   4 +-
 ...ile.html => AttrRepoWizardBuilder$Profile.html} |   2 +
 ...es => AttrRepoWizardBuilder$Profile.properties} |   2 +
 ...AttrRepoWizardBuilder$Profile_fr_CA.properties} |   2 +
 ...=> AttrRepoWizardBuilder$Profile_it.properties} |   2 +
 ...=> AttrRepoWizardBuilder$Profile_ja.properties} |   2 +
 ...AttrRepoWizardBuilder$Profile_pt_BR.properties} |   2 +
 ...=> AttrRepoWizardBuilder$Profile_ru.properties} |   2 +
 .../wizards/AuthModuleWizardBuilder$Profile.html   |   2 +
 .../AuthModuleWizardBuilder$Profile.properties     |   1 +
 ...uthModuleWizardBuilder$Profile_fr_CA.properties |   1 +
 .../AuthModuleWizardBuilder$Profile_it.properties  |   1 +
 .../AuthModuleWizardBuilder$Profile_ja.properties  |   1 +
 ...uthModuleWizardBuilder$Profile_pt_BR.properties |   1 +
 .../AuthModuleWizardBuilder$Profile_ru.properties  |   1 +
 .../syncope/client/console/panels/BeanPanel.java   |  25 ++
 .../syncope/common/lib/AbstractJDBCConf.java       | 264 ++++++++++++
 .../syncope/common/lib/AbstractLDAPConf.java       | 453 ++++++++++++++++++++
 .../AuthModuleConf.java => attr/AttrRepoConf.java} |  18 +-
 .../syncope/common/lib/attr/JDBCAttrRepoConf.java  | 157 +++++++
 .../syncope/common/lib/attr/LDAPAttrRepoConf.java  |  73 ++++
 .../StubAttrRepoConf.java}                         |  22 +-
 .../common/lib/attr/SyncopeAttrRepoConf.java       |  94 +++++
 .../syncope/common/lib/auth/AuthModuleConf.java    |  28 ++
 .../common/lib/auth/DuoMfaAuthModuleConf.java      |   7 +
 .../common/lib/auth/GoogleMfaAuthModuleConf.java   |  89 +---
 .../common/lib/auth/JDBCAuthModuleConf.java        |  87 +---
 .../common/lib/auth/JaasAuthModuleConf.java        |   7 +
 .../common/lib/auth/LDAPAuthModuleConf.java        |  90 +---
 .../common/lib/auth/OIDCAuthModuleConf.java        |   5 +
 .../common/lib/auth/SAML2IdPAuthModuleConf.java    |   6 +
 .../common/lib/auth/SimpleMfaAuthModuleConf.java   |   7 +
 .../common/lib/auth/StaticAuthModuleConf.java      |   5 +
 .../common/lib/auth/SyncopeAuthModuleConf.java     |   6 +
 .../syncope/common/lib/auth/U2FAuthModuleConf.java |   7 +
 .../lib/policy/DefaultAttrReleasePolicyConf.java   |  94 +++++
 .../lib/to/{AuthModuleTO.java => AttrRepoTO.java}  |  42 +-
 .../apache/syncope/common/lib/to/AuthModuleTO.java |  34 +-
 .../syncope/common/lib/types/AMEntitlement.java    |  10 +
 .../syncope/common/lib/types/AttrRepoState.java    |  33 +-
 .../syncope/common/lib/types/AuthModuleState.java  |  22 +-
 .../common/rest/api/service/AttrRepoService.java   | 122 ++++++
 .../common/lib/jackson/SyncopeXmlMapper.java       |   1 +
 .../apache/syncope/core/logic/AMLogicContext.java  |  87 ++--
 .../core/logic/AbstractAuthProfileLogic.java       |   2 +-
 .../apache/syncope/core/logic/AttrRepoLogic.java   | 118 ++++++
 .../apache/syncope/core/logic/AuthModuleLogic.java |   4 +-
 .../syncope/core/logic/AuthProfileLogic.java       |   4 +-
 .../apache/syncope/core/logic/ClientAppLogic.java  |  32 +-
 .../apache/syncope/core/logic/OIDCJWKSLogic.java   |   4 +-
 .../syncope/core/logic/SAML2IdPEntityLogic.java    |   4 +-
 .../syncope/core/logic/SAML2SPEntityLogic.java     |   4 +-
 .../apache/syncope/core/logic/SRARouteLogic.java   |  14 +-
 .../core/logic/wa/GoogleMfaAuthAccountLogic.java   |   4 +-
 .../core/logic/wa/GoogleMfaAuthTokenLogic.java     |   4 +-
 .../syncope/core/logic/wa/ImpersonationLogic.java  |   4 +-
 .../core/logic/wa/U2FRegistrationLogic.java        |   4 +-
 .../syncope/core/logic/wa/WAClientAppLogic.java    |  12 +-
 .../syncope/core/logic/wa/WAConfigLogic.java       |  15 +-
 .../core/logic/wa/WebAuthnRegistrationLogic.java   |   4 +-
 .../syncope/core/rest/cxf/AMRESTCXFContext.java    |   9 +
 .../core/rest/cxf/service/AttrRepoServiceImpl.java |  67 +++
 .../core/rest/cxf/SyncopeOpenApiCustomizer.java    |   6 +-
 .../WAConfigEntry.java => dao/AttrRepoDAO.java}    |  16 +-
 .../api/dao/{auth => }/AuthModuleDAO.java          |   5 +-
 .../api/dao/{auth => }/AuthProfileDAO.java         |   5 +-
 .../persistence/api/dao/{auth => }/CASSPDAO.java   |   5 +-
 .../api/dao/{auth => }/OIDCJWKSDAO.java            |   5 +-
 .../persistence/api/dao/{auth => }/OIDCRPDAO.java  |   5 +-
 .../api/dao/{auth => }/SAML2IdPEntityDAO.java      |   5 +-
 .../persistence/api/dao/{auth => }/SAML2SPDAO.java |   5 +-
 .../api/dao/{auth => }/SAML2SPEntityDAO.java       |   5 +-
 .../api/dao/{auth => }/WAConfigDAO.java            |   5 +-
 .../{auth/AuthModule.java => am/AttrRepo.java}     |  23 +-
 .../AuthModuleItem.java => am/AttrRepoItem.java}   |   8 +-
 .../api/entity/{auth => am}/AuthModule.java        |  13 +-
 .../api/entity/{auth => am}/AuthModuleItem.java    |   2 +-
 .../api/entity/{auth => am}/AuthProfile.java       |   2 +-
 .../api/entity/{auth => am}/CASSPClientApp.java    |   2 +-
 .../api/entity/{auth => am}/ClientApp.java         |   2 +-
 .../api/entity/{auth => am}/ClientAppUtils.java    |   2 +-
 .../entity/{auth => am}/ClientAppUtilsFactory.java |   2 +-
 .../api/entity/{auth => am}/OIDCJWKS.java          |   2 +-
 .../api/entity/{auth => am}/OIDCRPClientApp.java   |   2 +-
 .../api/entity/{auth => am}/SAML2IdPEntity.java    |   2 +-
 .../api/entity/{auth => am}/SAML2SPClientApp.java  |   2 +-
 .../api/entity/{auth => am}/SAML2SPEntity.java     |   2 +-
 .../api/entity/{auth => am}/WAConfigEntry.java     |   2 +-
 .../src/test/resources/domains/MasterContent.xml   |  36 +-
 .../core/persistence/jpa/PersistenceContext.java   |  48 ++-
 .../JPAWAConfigDAO.java => JPAAttrRepoDAO.java}    |  37 +-
 .../jpa/dao/{auth => }/JPAAuthModuleDAO.java       |   9 +-
 .../jpa/dao/{auth => }/JPAAuthProfileDAO.java      |   9 +-
 .../jpa/dao/{auth => }/JPACASSPDAO.java            |   9 +-
 .../jpa/dao/{auth => }/JPAOIDCJWKSDAO.java         |   9 +-
 .../jpa/dao/{auth => }/JPAOIDCRPDAO.java           |   9 +-
 .../jpa/dao/{auth => }/JPASAML2IdPEntityDAO.java   |   9 +-
 .../jpa/dao/{auth => }/JPASAML2SPDAO.java          |   9 +-
 .../jpa/dao/{auth => }/JPASAML2SPEntityDAO.java    |   9 +-
 .../jpa/dao/{auth => }/JPAWAConfigDAO.java         |   9 +-
 .../persistence/jpa/entity/JPAEntityFactory.java   |  48 ++-
 .../jpa/entity/{auth => am}/AbstractClientApp.java |   6 +-
 .../JPAAuthModule.java => am/JPAAttrRepo.java}     |  68 ++-
 .../JPAAttrRepoItem.java}                          |  26 +-
 .../jpa/entity/{auth => am}/JPAAuthModule.java     |  38 +-
 .../jpa/entity/{auth => am}/JPAAuthModuleItem.java |   6 +-
 .../jpa/entity/{auth => am}/JPAAuthProfile.java    |   4 +-
 .../jpa/entity/{auth => am}/JPACASSPClientApp.java |   4 +-
 .../jpa/entity/{auth => am}/JPAClientAppUtils.java |  12 +-
 .../{auth => am}/JPAClientAppUtilsFactory.java     |  14 +-
 .../jpa/entity/{auth => am}/JPAOIDCJWKS.java       |   4 +-
 .../entity/{auth => am}/JPAOIDCRPClientApp.java    |   4 +-
 .../jpa/entity/{auth => am}/JPASAML2IdPEntity.java |   4 +-
 .../entity/{auth => am}/JPASAML2SPClientApp.java   |   4 +-
 .../jpa/entity/{auth => am}/JPASAML2SPEntity.java  |   4 +-
 .../jpa/entity/{auth => am}/JPAWAConfigEntry.java  |   4 +-
 .../core/persistence/jpa/inner/AttrRepoTest.java   | 244 +++++++++++
 .../core/persistence/jpa/inner/AuthModuleTest.java |   8 +-
 .../persistence/jpa/inner/AuthProfileTest.java     |   6 +-
 .../core/persistence/jpa/inner/CASSPTest.java      |   7 +-
 .../core/persistence/jpa/inner/OIDCJWKSTest.java   |  10 +-
 .../core/persistence/jpa/inner/OIDCRPTest.java     |   4 +-
 .../persistence/jpa/inner/SAML2IdPEntityTest.java  |   4 +-
 .../persistence/jpa/inner/SAML2SPEntityTest.java   |   4 +-
 .../core/persistence/jpa/inner/SAML2SPTest.java    |   4 +-
 .../core/persistence/jpa/inner/WAConfigTest.java   |   6 +-
 .../core/persistence/jpa/outer/PolicyTest.java     |   4 +-
 .../src/test/resources/domains/MasterContent.xml   |  35 +-
 ...nfigDataBinder.java => AttrRepoDataBinder.java} |  12 +-
 .../api/data/AuthModuleDataBinder.java             |   3 +-
 .../api/data/AuthProfileDataBinder.java            |   2 +-
 .../provisioning/api/data/ClientAppDataBinder.java |   2 +-
 .../provisioning/api/data/OIDCJWKSDataBinder.java  |   2 +-
 .../api/data/SAML2IdPEntityDataBinder.java         |   2 +-
 .../api/data/SAML2SPEntityDataBinder.java          |   2 +-
 .../provisioning/api/data/WAConfigDataBinder.java  |   2 +-
 .../api/data/wa/WAClientAppDataBinder.java         |   2 +-
 .../provisioning/java/ProvisioningContext.java     |  18 +-
 ...BinderImpl.java => AttrRepoDataBinderImpl.java} |  68 +--
 .../java/data/AuthModuleDataBinderImpl.java        |  12 +-
 .../java/data/AuthProfileDataBinderImpl.java       |   2 +-
 .../java/data/ClientAppDataBinderImpl.java         |   8 +-
 .../java/data/OIDCJWKSDataBinderImpl.java          |   2 +-
 .../java/data/SAML2IdPEntityDataBinderImpl.java    |   2 +-
 .../java/data/SAML2SPEntityDataBinderImpl.java     |   2 +-
 .../java/data/WAConfigDataBinderImpl.java          |   4 +-
 .../java/data/wa/WAClientAppDataBinderImpl.java    |  42 +-
 .../org/apache/syncope/fit/AbstractITCase.java     | 101 +++--
 .../apache/syncope/fit/core/AttrRepoITCase.java    | 306 ++++++++++++++
 .../apache/syncope/fit/core/AuthModuleITCase.java  |  17 +-
 .../syncope/fit/core/wa/WAClientAppITCase.java     |   4 +-
 fit/wa-reference/pom.xml                           |   6 +
 .../org/apache/syncope/fit/sra/SAML2SRAITCase.java |   1 -
 .../apache/syncope/fit/ui/AbstractUIITCase.java    |  36 +-
 .../org/apache/syncope/fit/ui/OIDC4UIITCase.java   |   8 +-
 .../apache/syncope/fit/ui/SAML2SP4UIITCase.java    |   8 +-
 pom.xml                                            |   7 +-
 .../concepts/attributerepositories.adoc            |  52 +++
 .../reference-guide/concepts/concepts.adoc         |   2 +
 .../wa/bootstrap/AttrRepoPropertySourceMapper.java | 108 +++++
 .../bootstrap/AuthModulePropertySourceMapper.java  | 264 ++++++++++++
 .../syncope/wa/bootstrap/PropertySourceMapper.java |  92 +++++
 .../wa/bootstrap/WABootstrapConfiguration.java     |   2 +
 .../wa/bootstrap/WAPropertySourceLocator.java      | 460 ++-------------------
 wa/starter/pom.xml                                 |  10 +
 .../wa/starter/mapping/AttrReleaseMapper.java      |   3 +-
 .../wa/starter/mapping/CASSPClientAppTOMapper.java |   2 +
 .../wa/starter/mapping/ClientAppMapper.java        |   2 +
 .../starter/mapping/DefaultAttrReleaseMapper.java  |  67 ++-
 .../starter/mapping/OIDCRPClientAppTOMapper.java   |  49 +--
 .../starter/mapping/RegisteredServiceMapper.java   |  33 +-
 .../starter/mapping/SAML2SPClientAppTOMapper.java  |   2 +
 wa/starter/src/main/resources/wa.properties        |   2 +-
 .../syncope/wa/starter/WAServiceRegistryTest.java  |   5 +-
 .../src/test/resources/debug/wa-debug.properties   |   3 +-
 212 files changed, 4173 insertions(+), 1395 deletions(-)

diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/init/AMClassPathScanImplementationContributor.java b/client/am/console/src/main/java/org/apache/syncope/client/console/init/AMClassPathScanImplementationContributor.java
index 25acc7dbf2..e7ca032553 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/init/AMClassPathScanImplementationContributor.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/init/AMClassPathScanImplementationContributor.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.client.console.init;
 
 import java.util.Optional;
+import org.apache.syncope.common.lib.attr.AttrRepoConf;
 import org.apache.syncope.common.lib.auth.AuthModuleConf;
 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
 import org.springframework.core.type.filter.AssignableTypeFilter;
@@ -28,6 +29,7 @@ public class AMClassPathScanImplementationContributor implements ClassPathScanIm
     @Override
     public void extend(final ClassPathScanningCandidateComponentProvider scanner) {
         scanner.addIncludeFilter(new AssignableTypeFilter(AuthModuleConf.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(AttrRepoConf.class));
     }
 
     @Override
@@ -35,6 +37,9 @@ public class AMClassPathScanImplementationContributor implements ClassPathScanIm
         if (AuthModuleConf.class.isAssignableFrom(clazz)) {
             return Optional.of(AuthModuleConf.class.getName());
         }
+        if (AttrRepoConf.class.isAssignableFrom(clazz)) {
+            return Optional.of(AttrRepoConf.class.getName());
+        }
         return Optional.empty();
     }
 }
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 3771ea5118..f46871dd49 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
@@ -35,6 +35,7 @@ import org.apache.syncope.client.console.annotations.AMPage;
 import org.apache.syncope.client.console.authprofiles.AuthProfileDirectoryPanel;
 import org.apache.syncope.client.console.clientapps.ClientApps;
 import org.apache.syncope.client.console.panels.AMSessionPanel;
+import org.apache.syncope.client.console.panels.AttrRepoDirectoryPanel;
 import org.apache.syncope.client.console.panels.AuthModuleDirectoryPanel;
 import org.apache.syncope.client.console.panels.OIDC;
 import org.apache.syncope.client.console.panels.SAML2;
@@ -154,6 +155,18 @@ public class WA extends BasePage {
             });
         }
 
+        if (SyncopeConsoleSession.get().owns(AMEntitlement.ATTR_REPO_LIST)) {
+            tabs.add(new AbstractTab(new ResourceModel("attrRepos")) {
+
+                private static final long serialVersionUID = 5211692813425391144L;
+
+                @Override
+                public Panel getPanel(final String panelId) {
+                    return new AttrRepoDirectoryPanel(panelId, getPageReference());
+                }
+            });
+        }
+
         if (SyncopeConsoleSession.get().owns(AMEntitlement.CLIENTAPP_LIST)) {
             tabs.add(new AbstractTab(new ResourceModel("clientApps")) {
 
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/AttrRepoDirectoryPanel.java
similarity index 70%
copy from client/am/console/src/main/java/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.java
copy to client/am/console/src/main/java/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel.java
index ca8054b4b6..b8bd852c07 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/AttrRepoDirectoryPanel.java
@@ -27,16 +27,16 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.commons.AMConstants;
 import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
-import org.apache.syncope.client.console.panels.AuthModuleDirectoryPanel.AuthModuleProvider;
-import org.apache.syncope.client.console.rest.AuthModuleRestClient;
+import org.apache.syncope.client.console.panels.AttrRepoDirectoryPanel.AttrRepoProvider;
+import org.apache.syncope.client.console.rest.AttrRepoRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
-import org.apache.syncope.client.console.wizards.AuthModuleWizardBuilder;
+import org.apache.syncope.client.console.wizards.AttrRepoWizardBuilder;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
-import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
 import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -53,27 +53,27 @@ import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.model.StringResourceModel;
 
-public class AuthModuleDirectoryPanel
-        extends DirectoryPanel<AuthModuleTO, AuthModuleTO, AuthModuleProvider, AuthModuleRestClient> {
+public class AttrRepoDirectoryPanel
+        extends DirectoryPanel<AttrRepoTO, AttrRepoTO, AttrRepoProvider, AttrRepoRestClient> {
 
     private static final long serialVersionUID = 1005345990563741296L;
 
-    public AuthModuleDirectoryPanel(final String id, final PageReference pageRef) {
+    public AttrRepoDirectoryPanel(final String id, final PageReference pageRef) {
         super(id, pageRef);
 
         disableCheckBoxes();
 
-        this.addNewItemPanelBuilder(new AuthModuleWizardBuilder(new AuthModuleTO(), pageRef), true);
+        this.addNewItemPanelBuilder(new AttrRepoWizardBuilder(new AttrRepoTO(), pageRef), true);
 
-        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, AMEntitlement.AUTH_MODULE_CREATE);
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, AMEntitlement.ATTR_REPO_CREATE);
 
         modal.size(Modal.Size.Extra_large);
         initResultTable();
     }
 
     @Override
-    protected AuthModuleProvider dataProvider() {
-        return new AuthModuleProvider(rows);
+    protected AttrRepoProvider dataProvider() {
+        return new AttrRepoProvider(rows);
     }
 
     @Override
@@ -87,8 +87,8 @@ public class AuthModuleDirectoryPanel
     }
 
     @Override
-    protected List<IColumn<AuthModuleTO, String>> getColumns() {
-        List<IColumn<AuthModuleTO, String>> columns = new ArrayList<>();
+    protected List<IColumn<AttrRepoTO, String>> getColumns() {
+        List<IColumn<AttrRepoTO, String>> columns = new ArrayList<>();
         columns.add(new PropertyColumn<>(
                 new StringResourceModel(Constants.KEY_FIELD_NAME, this),
                 Constants.KEY_FIELD_NAME, Constants.KEY_FIELD_NAME));
@@ -100,42 +100,44 @@ public class AuthModuleDirectoryPanel
 
             @Override
             public void populateItem(
-                    final Item<ICellPopulator<AuthModuleTO>> item,
+                    final Item<ICellPopulator<AttrRepoTO>> item,
                     final String componentId,
-                    final IModel<AuthModuleTO> rowModel) {
+                    final IModel<AttrRepoTO> rowModel) {
 
                 item.add(new Label(componentId, rowModel.getObject().getConf() == null
                         ? StringUtils.EMPTY
                         : StringUtils.substringBefore(
-                                rowModel.getObject().getConf().getClass().getSimpleName(), "AuthModuleConf")));
+                                rowModel.getObject().getConf().getClass().getSimpleName(), "AttrRepoConf")));
             }
         });
+        columns.add(new PropertyColumn<>(new ResourceModel("state"), "state", "state"));
+        columns.add(new PropertyColumn<>(new ResourceModel("order"), "order", "order"));
         return columns;
     }
 
     @Override
-    public ActionsPanel<AuthModuleTO> getActions(final IModel<AuthModuleTO> model) {
-        ActionsPanel<AuthModuleTO> panel = super.getActions(model);
+    public ActionsPanel<AttrRepoTO> getActions(final IModel<AttrRepoTO> model) {
+        ActionsPanel<AttrRepoTO> panel = super.getActions(model);
 
         panel.add(new ActionLink<>() {
 
             private static final long serialVersionUID = -3722207913631435501L;
 
             @Override
-            public void onClick(final AjaxRequestTarget target, final AuthModuleTO ignore) {
-                send(AuthModuleDirectoryPanel.this, Broadcast.EXACT, new AjaxWizard.EditItemActionEvent<>(
-                        AuthModuleRestClient.read(model.getObject().getKey()), target));
+            public void onClick(final AjaxRequestTarget target, final AttrRepoTO ignore) {
+                send(AttrRepoDirectoryPanel.this, Broadcast.EXACT, new AjaxWizard.EditItemActionEvent<>(
+                        AttrRepoRestClient.read(model.getObject().getKey()), target));
             }
-        }, ActionLink.ActionType.EDIT, AMEntitlement.AUTH_MODULE_UPDATE);
+        }, ActionLink.ActionType.EDIT, AMEntitlement.ATTR_REPO_UPDATE);
 
         panel.add(new ActionLink<>() {
 
             private static final long serialVersionUID = -3722207913631435501L;
 
             @Override
-            public void onClick(final AjaxRequestTarget target, final AuthModuleTO ignore) {
+            public void onClick(final AjaxRequestTarget target, final AttrRepoTO ignore) {
                 try {
-                    AuthModuleRestClient.delete(model.getObject().getKey());
+                    AttrRepoRestClient.delete(model.getObject().getKey());
 
                     SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
                     target.add(container);
@@ -145,37 +147,37 @@ public class AuthModuleDirectoryPanel
                 }
                 ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
             }
-        }, ActionLink.ActionType.DELETE, AMEntitlement.AUTH_MODULE_DELETE, true);
+        }, ActionLink.ActionType.DELETE, AMEntitlement.ATTR_REPO_DELETE, true);
 
         return panel;
     }
 
-    protected static final class AuthModuleProvider extends DirectoryDataProvider<AuthModuleTO> {
+    protected static final class AttrRepoProvider extends DirectoryDataProvider<AttrRepoTO> {
 
         private static final long serialVersionUID = -185944053385660794L;
 
-        private final SortableDataProviderComparator<AuthModuleTO> comparator;
+        private final SortableDataProviderComparator<AttrRepoTO> comparator;
 
-        private AuthModuleProvider(final int paginatorRows) {
+        private AttrRepoProvider(final int paginatorRows) {
             super(paginatorRows);
             setSort(Constants.KEY_FIELD_NAME, SortOrder.ASCENDING);
             comparator = new SortableDataProviderComparator<>(this);
         }
 
         @Override
-        public Iterator<AuthModuleTO> iterator(final long first, final long count) {
-            List<AuthModuleTO> result = AuthModuleRestClient.list();
+        public Iterator<AttrRepoTO> iterator(final long first, final long count) {
+            List<AttrRepoTO> result = AttrRepoRestClient.list();
             result.sort(comparator);
             return result.subList((int) first, (int) first + (int) count).iterator();
         }
 
         @Override
         public long size() {
-            return AuthModuleRestClient.list().size();
+            return AttrRepoRestClient.list().size();
         }
 
         @Override
-        public IModel<AuthModuleTO> model(final AuthModuleTO object) {
+        public IModel<AttrRepoTO> model(final AttrRepoTO object) {
             return new CompoundPropertyModel<>(object);
         }
     }
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 ca8054b4b6..a196cbc317 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
@@ -110,6 +110,8 @@ public class AuthModuleDirectoryPanel
                                 rowModel.getObject().getConf().getClass().getSimpleName(), "AuthModuleConf")));
             }
         });
+        columns.add(new PropertyColumn<>(new ResourceModel("state"), "state", "state"));
+        columns.add(new PropertyColumn<>(new ResourceModel("order"), "order", "order"));
         return columns;
     }
 
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java
index 6b08190cd4..fcc98316b4 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java
@@ -18,19 +18,30 @@
  */
 package org.apache.syncope.client.console.policies;
 
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.rest.AttrRepoRestClient;
 import org.apache.syncope.client.console.rest.PolicyRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.Constants;
+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.AjaxPalettePanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
+import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf.PrincipalAttrRepoMergingStrategy;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
 
@@ -38,6 +49,16 @@ public class AttrReleasePolicyModalPanel extends AbstractModalPanel<AttrReleaseP
 
     private static final long serialVersionUID = 1L;
 
+    private final IModel<List<String>> allAttrRepos = new LoadableDetachableModel<>() {
+
+        private static final long serialVersionUID = -2012833443695917883L;
+
+        @Override
+        protected List<String> load() {
+            return AttrRepoRestClient.list().stream().map(AttrRepoTO::getKey).sorted().collect(Collectors.toList());
+        }
+    };
+
     private final IModel<AttrReleasePolicyTO> model;
 
     public AttrReleasePolicyModalPanel(
@@ -68,6 +89,43 @@ public class AttrReleasePolicyModalPanel extends AbstractModalPanel<AttrReleaseP
                 "includeOnlyAttrs",
                 "includeOnlyAttrs",
                 includeOnlyAttr));
+
+        add(new AjaxTextFieldPanel(
+                "principalIdAttr", "principalIdAttr",
+                new PropertyModel<>(model.getObject().getConf(), "principalIdAttr")));
+
+        AjaxDropDownChoicePanel<PrincipalAttrRepoMergingStrategy> mergingStrategy = new AjaxDropDownChoicePanel<>(
+                "mergingStrategy", "mergingStrategy",
+                new PropertyModel<>(model.getObject().getConf(), "principalAttrRepoConf.mergingStrategy"));
+        mergingStrategy.setChoices(List.of(PrincipalAttrRepoMergingStrategy.values()));
+        mergingStrategy.addRequiredLabel();
+        mergingStrategy.setNullValid(false);
+        add(mergingStrategy);
+
+        add(new AjaxCheckBoxPanel(
+                "ignoreResolvedAttributes",
+                "ignoreResolvedAttributes",
+                new PropertyModel<>(model.getObject().getConf(), "principalAttrRepoConf.ignoreResolvedAttributes"),
+                false));
+
+        add(new AjaxSpinnerFieldPanel.Builder<Long>().build(
+                "expiration",
+                "expiration",
+                Long.class,
+                new PropertyModel<>(model.getObject().getConf(), "principalAttrRepoConf.expiration")));
+
+        AjaxDropDownChoicePanel<TimeUnit> timeUnit = new AjaxDropDownChoicePanel<>(
+                "timeUnit", "timeUnit",
+                new PropertyModel<>(model.getObject().getConf(), "principalAttrRepoConf.timeUnit"));
+        timeUnit.setChoices(List.of(TimeUnit.values()));
+        timeUnit.addRequiredLabel();
+        timeUnit.setNullValid(false);
+        add(timeUnit);
+
+        add(new AjaxPalettePanel.Builder<String>().setName("attrRepos").build(
+                "attrRepos",
+                new PropertyModel<>(model.getObject().getConf(), "principalAttrRepoConf.attrRepos"),
+                allAttrRepos));
     }
 
     @Override
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.java
index 98ffa462a0..3f70f08363 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AuthPolicyModalPanel.java
@@ -40,7 +40,7 @@ import org.apache.wicket.model.PropertyModel;
 
 public class AuthPolicyModalPanel extends AbstractModalPanel<AuthPolicyTO> {
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = -7210166323800567306L;
 
     private final IModel<List<String>> allAuthModules = new LoadableDetachableModel<>() {
 
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AttrRepoRestClient.java b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AttrRepoRestClient.java
new file mode 100644
index 0000000000..318c1e38ea
--- /dev/null
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/rest/AttrRepoRestClient.java
@@ -0,0 +1,48 @@
+/*
+ * 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.client.console.rest;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
+import org.apache.syncope.common.rest.api.service.AttrRepoService;
+
+public class AttrRepoRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -7379778542101161274L;
+
+    public static List<AttrRepoTO> list() {
+        return getService(AttrRepoService.class).list();
+    }
+
+    public static void create(final AttrRepoTO attrRepoTO) {
+        getService(AttrRepoService.class).create(attrRepoTO);
+    }
+
+    public static AttrRepoTO read(final String key) {
+        return getService(AttrRepoService.class).read(key);
+    }
+
+    public static void update(final AttrRepoTO attrRepoTO) {
+        getService(AttrRepoService.class).update(attrRepoTO);
+    }
+
+    public static void delete(final String key) {
+        getService(AttrRepoService.class).delete(key);
+    }
+}
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder.java
new file mode 100644
index 0000000000..6bc14cb222
--- /dev/null
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder.java
@@ -0,0 +1,177 @@
+/*
+ * 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.client.console.wizards;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeWebApplication;
+import org.apache.syncope.client.console.panels.BeanPanel;
+import org.apache.syncope.client.console.rest.AttrRepoRestClient;
+import org.apache.syncope.client.console.wizards.mapping.AttrRepoMappingPanel;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
+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.attr.AttrRepoConf;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
+import org.apache.syncope.common.lib.types.AttrRepoState;
+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.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.springframework.util.ClassUtils;
+
+public class AttrRepoWizardBuilder extends BaseAjaxWizardBuilder<AttrRepoTO> {
+
+    private static final long serialVersionUID = -6163230263062920394L;
+
+    protected final LoadableDetachableModel<List<String>> attrRepoConfs;
+
+    protected Model<Class<? extends AttrRepoConf>> attrRepoConfClass = Model.of();
+
+    public AttrRepoWizardBuilder(final AttrRepoTO defaultItem, final PageReference pageRef) {
+
+        super(defaultItem, pageRef);
+
+        attrRepoConfs = new LoadableDetachableModel<>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<String> load() {
+                return SyncopeWebApplication.get().getLookup().getClasses(AttrRepoConf.class).stream().
+                        map(Class::getName).sorted().collect(Collectors.toList());
+            }
+        };
+    }
+
+    @Override
+    protected Serializable onApplyInternal(final AttrRepoTO modelObject) {
+        if (mode == AjaxWizard.Mode.CREATE) {
+            AttrRepoRestClient.create(modelObject);
+        } else {
+            AttrRepoRestClient.update(modelObject);
+        }
+        return modelObject;
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final AttrRepoTO modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new Profile(modelObject, attrRepoConfs, attrRepoConfClass));
+        wizardModel.add(new Configuration(modelObject));
+        wizardModel.add(new Mapping(modelObject));
+        return wizardModel;
+    }
+
+    protected static class Profile extends WizardStep {
+
+        private static final long serialVersionUID = -3043839139187792810L;
+
+        Profile(
+                final AttrRepoTO attrRepo,
+                final LoadableDetachableModel<List<String>> attrRepoConfs,
+                final Model<Class<? extends AttrRepoConf>> attrRepoConfClass) {
+
+            boolean isNew = attrRepo.getConf() == null;
+            if (!isNew) {
+                attrRepoConfClass.setObject(attrRepo.getConf().getClass());
+            }
+
+            AjaxTextFieldPanel key = new AjaxTextFieldPanel(
+                    Constants.KEY_FIELD_NAME, Constants.KEY_FIELD_NAME,
+                    new PropertyModel<>(attrRepo, Constants.KEY_FIELD_NAME));
+            key.addRequiredLabel();
+            key.setEnabled(isNew);
+            add(key);
+
+            AjaxTextFieldPanel description = new AjaxTextFieldPanel(
+                    Constants.DESCRIPTION_FIELD_NAME, getString(Constants.DESCRIPTION_FIELD_NAME),
+                    new PropertyModel<>(attrRepo, Constants.DESCRIPTION_FIELD_NAME));
+            add(description);
+
+            AjaxDropDownChoicePanel<AttrRepoState> state = new AjaxDropDownChoicePanel<>(
+                    "state", getString("state"), new PropertyModel<>(attrRepo, "state"));
+            state.setChoices(List.of(AttrRepoState.values()));
+            state.addRequiredLabel();
+            state.setNullValid(false);
+            add(state);
+
+            add(new AjaxSpinnerFieldPanel.Builder<Integer>().build(
+                    "order",
+                    "order",
+                    Integer.class,
+                    new PropertyModel<>(attrRepo, "order")).addRequiredLabel());
+
+            AjaxDropDownChoicePanel<String> conf = new AjaxDropDownChoicePanel<>("conf", getString("type"), isNew
+                    ? Model.of()
+                    : Model.of(attrRepo.getConf().getClass().getName()));
+            conf.setChoices(attrRepoConfs.getObject());
+            conf.addRequiredLabel();
+            conf.setNullValid(false);
+            conf.setEnabled(isNew);
+            conf.add(new AjaxEventBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -7133385027739964990L;
+
+                @SuppressWarnings("unchecked")
+                @Override
+                protected void onEvent(final AjaxRequestTarget target) {
+                    try {
+                        Class<? extends AttrRepoConf> clazz =
+                                (Class<? extends AttrRepoConf>) ClassUtils.resolveClassName(
+                                        conf.getModelObject(), ClassUtils.getDefaultClassLoader());
+
+                        attrRepo.setConf(clazz.getConstructor().newInstance());
+                        attrRepoConfClass.setObject(clazz);
+                    } catch (Exception e) {
+                        LOG.error("During deserialization", e);
+                    }
+                }
+            });
+            add(conf);
+        }
+    }
+
+    protected static class Configuration extends WizardStep {
+
+        private static final long serialVersionUID = -785981096328637758L;
+
+        Configuration(final AttrRepoTO attrRepo) {
+            add(new BeanPanel<>("bean", new PropertyModel<>(attrRepo, "conf")).setRenderBodyOnly(true));
+        }
+    }
+
+    protected static final class Mapping extends WizardStep {
+
+        private static final long serialVersionUID = 3454904947720856253L;
+
+        Mapping(final AttrRepoTO attrRepo) {
+            setTitleModel(Model.of("Mapping"));
+            setSummaryModel(Model.of(StringUtils.EMPTY));
+            add(new AttrRepoMappingPanel("mapping", attrRepo));
+        }
+    }
+}
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 28eb7544b1..da50eda9a0 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
@@ -30,11 +30,13 @@ 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.AjaxSpinnerFieldPanel;
 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.syncope.common.lib.types.AuthModuleState;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxEventBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -115,6 +117,19 @@ public class AuthModuleWizardBuilder extends BaseAjaxWizardBuilder<AuthModuleTO>
                     new PropertyModel<>(authModule, Constants.DESCRIPTION_FIELD_NAME));
             add(description);
 
+            AjaxDropDownChoicePanel<AuthModuleState> state = new AjaxDropDownChoicePanel<>(
+                    "state", getString("state"), new PropertyModel<>(authModule, "state"));
+            state.setChoices(List.of(AuthModuleState.values()));
+            state.addRequiredLabel();
+            state.setNullValid(false);
+            add(state);
+
+            add(new AjaxSpinnerFieldPanel.Builder<Integer>().build(
+                    "order",
+                    "order",
+                    Integer.class,
+                    new PropertyModel<>(authModule, "order")).addRequiredLabel());
+
             AjaxDropDownChoicePanel<String> conf = new AjaxDropDownChoicePanel<>("conf", getString("type"), isNew
                     ? Model.of()
                     : Model.of(authModule.getConf().getClass().getName()));
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AttrRepoMappingPanel.java
similarity index 85%
copy from client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java
copy to client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AttrRepoMappingPanel.java
index 58c63a57b3..3d4ba91e69 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AttrRepoMappingPanel.java
@@ -20,21 +20,21 @@ package org.apache.syncope.client.console.wizards.mapping;
 
 import java.util.List;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
-import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.util.ListModel;
 
-public class AuthModuleMappingPanel extends AbstractMappingPanel {
+public class AttrRepoMappingPanel extends AbstractMappingPanel {
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = -8940651851569691064L;
 
-    public AuthModuleMappingPanel(final String id, final AuthModuleTO authModule) {
+    public AttrRepoMappingPanel(final String id, final AttrRepoTO attrRepo) {
         super(id,
                 null,
                 null,
-                new ListModel<>(authModule.getItems()),
+                new ListModel<>(attrRepo.getItems()),
                 true,
                 MappingPurpose.NONE);
 
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java
index 58c63a57b3..b565c67d6e 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AuthModuleMappingPanel.java
@@ -28,7 +28,7 @@ import org.apache.wicket.model.util.ListModel;
 
 public class AuthModuleMappingPanel extends AbstractMappingPanel {
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = -8940651851569691064L;
 
     public AuthModuleMappingPanel(final String id, final AuthModuleTO authModule) {
         super(id,
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
index 4b5a308dc7..10acef8689 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
@@ -21,3 +21,4 @@ config=Parameters
 sessions=Sessions
 authProfiles=Profiles
 push=Push
+attrRepos=Attribute Repositories
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_fr_CA.properties
index 4b5a308dc7..10acef8689 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_fr_CA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_fr_CA.properties
@@ -21,3 +21,4 @@ config=Parameters
 sessions=Sessions
 authProfiles=Profiles
 push=Push
+attrRepos=Attribute Repositories
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_it.properties
index e27e0cdae8..7cb10b56f3 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_it.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_it.properties
@@ -21,3 +21,4 @@ config=Parametri
 sessions=Sessioni
 authProfiles=Profili
 push=Applica
+attrRepos=Depositi di Attributi
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_ja.properties
index 4b5a308dc7..10acef8689 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_ja.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_ja.properties
@@ -21,3 +21,4 @@ config=Parameters
 sessions=Sessions
 authProfiles=Profiles
 push=Push
+attrRepos=Attribute Repositories
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_pt_BR.properties
index 4b5a308dc7..10acef8689 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_pt_BR.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_pt_BR.properties
@@ -21,3 +21,4 @@ config=Parameters
 sessions=Sessions
 authProfiles=Profiles
 push=Push
+attrRepos=Attribute Repositories
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_ru.properties
index 4b5a308dc7..10acef8689 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_ru.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA_ru.properties
@@ -21,3 +21,4 @@ config=Parameters
 sessions=Sessions
 authProfiles=Profiles
 push=Push
+attrRepos=Attribute Repositories
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel.properties
similarity index 92%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel.properties
index 9b5b243ec9..e819d53fc0 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel.properties
@@ -15,7 +15,9 @@
 # specific language governing permissions and limitations
 # under the License.
 any.edit=Edit Parameter ${key}
-any.new=New Auth Module
-any.edit=Edit Auth Module
+any.new=New Attr Repo
+any.edit=Edit Attr Repo
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_fr_CA.properties
similarity index 92%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_fr_CA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_fr_CA.properties
index d7674d0be4..e0e06db5c0 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_fr_CA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_fr_CA.properties
@@ -15,7 +15,9 @@
 # specific language governing permissions and limitations
 # under the License.
 any.edit=Modifier param\u00e8tre ${key}
-any.new=New Auth Module
-any.edit=Edit Auth Module
+any.new=New Attr Repo
+any.edit=Edit Attr Repo
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_it.properties
similarity index 88%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_it.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_it.properties
index 117bd05135..26c3438fd6 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_it.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_it.properties
@@ -15,7 +15,9 @@
 # specific language governing permissions and limitations
 # under the License.
 any.edit=Modifica parametro ${key}
-any.new=Nuovo Modulo di Autenticazione
-any.edit=Aggiorna Modulo di Autenticazione
+any.new=Nuovo Deposito di Attributi
+any.edit=Aggiorna Deposito di Attributi
 description=Descrizione
 type=Tipo
+state=Stato
+order=Ordinamento
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_ja.properties
similarity index 92%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ja.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_ja.properties
index d8622d8959..3fe7748112 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ja.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_ja.properties
@@ -15,7 +15,9 @@
 # specific language governing permissions and limitations
 # under the License.
 any.edit=\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc ${key} \u3092\u7de8\u96c6
-any.new=New Auth Module
-any.edit=Edit Auth Module
+any.new=New Attr Repo
+any.edit=Edit Attr Repo
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_pt_BR.properties
similarity index 92%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_pt_BR.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_pt_BR.properties
index 3ba3742397..ad1a3f9640 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_pt_BR.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_pt_BR.properties
@@ -15,7 +15,9 @@
 # specific language governing permissions and limitations
 # under the License.
 any.edit=Editar par\u00e2metro
-any.new=New Auth Module
-any.edit=Edit Auth Module
+any.new=New Attr Repo
+any.edit=Edit Attr Repo
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_ru.properties
similarity index 93%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ru.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_ru.properties
index a6fd78698a..63cbbc9cfc 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ru.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AttrRepoDirectoryPanel_ru.properties
@@ -16,7 +16,9 @@
 # under the License.
 #
 any.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 ${key}
-any.new=New Auth Module
-any.edit=Edit Auth Module
+any.new=New Attr Repo
+any.edit=Edit Attr Repo
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.properties
index 9b5b243ec9..b687db478f 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel.properties
@@ -19,3 +19,5 @@ any.new=New Auth Module
 any.edit=Edit Auth Module
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_fr_CA.properties
index d7674d0be4..8d286a176d 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_fr_CA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_fr_CA.properties
@@ -19,3 +19,5 @@ any.new=New Auth Module
 any.edit=Edit Auth Module
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_it.properties
index 117bd05135..c122cc5d98 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_it.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_it.properties
@@ -19,3 +19,5 @@ any.new=Nuovo Modulo di Autenticazione
 any.edit=Aggiorna Modulo di Autenticazione
 description=Descrizione
 type=Tipo
+state=Stato
+order=Ordinamento
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ja.properties
index d8622d8959..60da8670c2 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ja.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ja.properties
@@ -19,3 +19,5 @@ any.new=New Auth Module
 any.edit=Edit Auth Module
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_pt_BR.properties
index 3ba3742397..0f1790ab08 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_pt_BR.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_pt_BR.properties
@@ -19,3 +19,5 @@ any.new=New Auth Module
 any.edit=Edit Auth Module
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ru.properties
index a6fd78698a..f87847bcab 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ru.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/panels/AuthModuleDirectoryPanel_ru.properties
@@ -20,3 +20,5 @@ any.new=New Auth Module
 any.edit=Edit Auth Module
 description=Description
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.html
index 7f978cc39f..aa0da3f6f6 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.html
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.html
@@ -27,5 +27,23 @@ under the License.
     <div class="form-group">
       <span wicket:id="includeOnlyAttrs"/>
     </div>
+    <div class="form-group">
+      <span wicket:id="principalIdAttr"/>
+    </div>
+    <div class="form-group">
+      <span wicket:id="mergingStrategy"/>
+    </div>
+    <div class="form-group">
+      <span wicket:id="ignoreResolvedAttributes"/>
+    </div>
+    <div class="form-group">
+      <span wicket:id="expiration"/>
+    </div>
+    <div class="form-group">
+      <span wicket:id="timeUnit"/>
+    </div>
+    <div class="form-group">
+      <span wicket:id="attrRepos"/>
+    </div>
   </wicket:extend>
 </html>
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.html
similarity index 79%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.html
index beba307fed..7772c93b7b 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.html
@@ -18,8 +18,6 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
-    <div class="form-group"><span wicket:id="key">[key]</span></div>
-    <div class="form-group"><span wicket:id="description">[description]</span></div>
-    <div class="form-group"><span wicket:id="conf">[conf]</span></div>
+    <span wicket:id="bean"/>
   </wicket:panel>
 </html>
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.properties
similarity index 74%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.properties
index 4b5a308dc7..8d2c500e11 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration.properties
@@ -14,10 +14,11 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-wa=WA
-authModules=Authentication Modules
-clientApps=Client Applications
-config=Parameters
-sessions=Sessions
-authProfiles=Profiles
-push=Push
+plainAttrs=Plain Attributes
+derAttrs=Derived Attributes
+virAttrs=Virtual Attributes
+features=Features
+matchingCond=Matching Condition
+userMatchingCond=User Matching Condition
+groupMatchingCond=Group Matching Condition
+anyObjectMatchingCond=AnyObject Matching Condition
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_fr_CA.properties
similarity index 70%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_fr_CA.properties
index 4b5a308dc7..e791574a14 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_fr_CA.properties
@@ -14,10 +14,11 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-wa=WA
-authModules=Authentication Modules
-clientApps=Client Applications
-config=Parameters
-sessions=Sessions
-authProfiles=Profiles
-push=Push
+plainAttrs=Attributs simples
+derAttrs=Attributs d�riv�s
+virAttrs=Attributs virtuels
+features=Caract�ristiques
+matchingCond=Condition d'appariement
+userMatchingCond=Condition d'appariement des utilisateurs
+groupMatchingCond=Condition d'appariement de groupe
+anyObjectMatchingCond=Condition d'appariement de n'importe quel objet
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_it.properties
similarity index 74%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_it.properties
index 4b5a308dc7..6688f852c2 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_it.properties
@@ -14,10 +14,11 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-wa=WA
-authModules=Authentication Modules
-clientApps=Client Applications
-config=Parameters
-sessions=Sessions
-authProfiles=Profiles
-push=Push
+plainAttrs=Attributi
+derAttrs=Attributi Derivati
+virAttrs=Attributi Virtuali
+features=Features
+matchingCond=Condizione di matching
+userMatchingCond=Condizione di matching utente
+groupMatchingCond=Condizione di matching gruppo
+anyObjectMatchingCond=Condizione di matching any
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ja.properties
similarity index 66%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ja.properties
index 4b5a308dc7..ba85e50448 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ja.properties
@@ -14,10 +14,11 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-wa=WA
-authModules=Authentication Modules
-clientApps=Client Applications
-config=Parameters
-sessions=Sessions
-authProfiles=Profiles
-push=Push
+plainAttrs=\u30d7\u30ec\u30fc\u30f3\u5c5e\u6027
+derAttrs=\u6d3e\u751f\u5c5e\u6027
+virAttrs=\u4eee\u60f3\u5c5e\u6027
+features=\u6a5f\u80fd
+matchingCond=\u4e00\u81f4\u6761\u4ef6
+userMatchingCond=\u30e6\u30fc\u30b6\u2015\u4e00\u81f4\u6761\u4ef6
+groupMatchingCond=\u30b0\u30eb\u30fc\u30d7\u4e00\u81f4\u6761\u4ef6
+anyObjectMatchingCond=\u4efb\u610f\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u4e00\u81f4\u6761\u4ef6
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_pt_BR.properties
similarity index 74%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_pt_BR.properties
index 4b5a308dc7..8d2c500e11 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/pages/WA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_pt_BR.properties
@@ -14,10 +14,11 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-wa=WA
-authModules=Authentication Modules
-clientApps=Client Applications
-config=Parameters
-sessions=Sessions
-authProfiles=Profiles
-push=Push
+plainAttrs=Plain Attributes
+derAttrs=Derived Attributes
+virAttrs=Virtual Attributes
+features=Features
+matchingCond=Matching Condition
+userMatchingCond=User Matching Condition
+groupMatchingCond=Group Matching Condition
+anyObjectMatchingCond=AnyObject Matching Condition
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ru.properties
new file mode 100644
index 0000000000..721b3e531a
--- /dev/null
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Configuration_ru.properties
@@ -0,0 +1,25 @@
+# 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.
+#
+plainAttrs=\u041f\u0440\u043e\u0441\u0442\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b
+derAttrs=\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b
+virAttrs=\u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b
+features=\u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430
+matchingCond=\u041a\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f
+userMatchingCond=\u041a\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439
+groupMatchingCond=\u041a\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f \u0413\u0440\u0443\u043f\u043f
+anyObjectMatchingCond=\u041a\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f \u041e\u0431\u044a\u0435\u043a\u0442\u043e\u0432
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Mapping.html
similarity index 79%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Mapping.html
index beba307fed..272967359a 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Mapping.html
@@ -18,8 +18,6 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
-    <div class="form-group"><span wicket:id="key">[key]</span></div>
-    <div class="form-group"><span wicket:id="description">[description]</span></div>
-    <div class="form-group"><span wicket:id="conf">[conf]</span></div>
+    <span wicket:id="mapping"/>
   </wicket:panel>
 </html>
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile.html
similarity index 88%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile.html
index beba307fed..ec6f0108f6 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile.html
@@ -20,6 +20,8 @@ under the License.
   <wicket:panel>
     <div class="form-group"><span wicket:id="key">[key]</span></div>
     <div class="form-group"><span wicket:id="description">[description]</span></div>
+    <div class="form-group"><span wicket:id="state">[state]</span></div>
+    <div class="form-group"><span wicket:id="order">[order]</span></div>
     <div class="form-group"><span wicket:id="conf">[conf]</span></div>
   </wicket:panel>
 </html>
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile.properties
similarity index 97%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile.properties
index 18cf102c42..daa0fa0db7 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile.properties
@@ -17,3 +17,5 @@
 description=Description
 configuration=Configuration
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_fr_CA.properties
similarity index 97%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_fr_CA.properties
index 18cf102c42..daa0fa0db7 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_fr_CA.properties
@@ -17,3 +17,5 @@
 description=Description
 configuration=Configuration
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_it.properties
similarity index 96%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_it.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_it.properties
index 98514ae0b5..cd2b9511c3 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_it.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_it.properties
@@ -17,3 +17,5 @@
 description=Descrizione
 configuration=Configurazione
 type=Tipo
+state=Stato
+order=Ordinamento
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_ja.properties
similarity index 97%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ja.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_ja.properties
index 82b26f285a..fc9ca8bc2e 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ja.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_ja.properties
@@ -17,3 +17,5 @@
 description=Description
 configuration=\u8a2d\u5b9a
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_pt_BR.properties
similarity index 97%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_pt_BR.properties
index 18cf102c42..daa0fa0db7 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_pt_BR.properties
@@ -17,3 +17,5 @@
 description=Description
 configuration=Configuration
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_ru.properties
similarity index 97%
copy from client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ru.properties
copy to client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_ru.properties
index 9b168647f2..d5039c7170 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ru.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder$Profile_ru.properties
@@ -18,3 +18,5 @@
 description=Description
 configuration=\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f
 type=Type
+state=State
+order=Order
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
index beba307fed..ec6f0108f6 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.html
@@ -20,6 +20,8 @@ under the License.
   <wicket:panel>
     <div class="form-group"><span wicket:id="key">[key]</span></div>
     <div class="form-group"><span wicket:id="description">[description]</span></div>
+    <div class="form-group"><span wicket:id="state">[state]</span></div>
+    <div class="form-group"><span wicket:id="order">[order]</span></div>
     <div class="form-group"><span wicket:id="conf">[conf]</span></div>
   </wicket:panel>
 </html>
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.properties
index 18cf102c42..84ccc058a9 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile.properties
@@ -17,3 +17,4 @@
 description=Description
 configuration=Configuration
 type=Type
+state=State
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
index 18cf102c42..84ccc058a9 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_fr_CA.properties
@@ -17,3 +17,4 @@
 description=Description
 configuration=Configuration
 type=Type
+state=State
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_it.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_it.properties
index 98514ae0b5..ba404e074d 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_it.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_it.properties
@@ -17,3 +17,4 @@
 description=Descrizione
 configuration=Configurazione
 type=Tipo
+state=Stato
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ja.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ja.properties
index 82b26f285a..768e1a5cc4 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ja.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ja.properties
@@ -17,3 +17,4 @@
 description=Description
 configuration=\u8a2d\u5b9a
 type=Type
+state=State
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_pt_BR.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_pt_BR.properties
index 18cf102c42..84ccc058a9 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_pt_BR.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_pt_BR.properties
@@ -17,3 +17,4 @@
 description=Description
 configuration=Configuration
 type=Type
+state=State
diff --git a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ru.properties b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ru.properties
index 9b168647f2..783c5e67a9 100644
--- a/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ru.properties
+++ b/client/am/console/src/main/resources/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder$Profile_ru.properties
@@ -18,3 +18,4 @@
 description=Description
 configuration=\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f
 type=Type
+state=State
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
index f4649f9863..70f5fb94d3 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
@@ -21,14 +21,18 @@ package org.apache.syncope.client.console.panels;
 import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.lang.reflect.ParameterizedType;
+import java.time.Duration;
 import java.time.OffsetDateTime;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.time.DateFormatUtils;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.console.panels.search.AnyObjectSearchPanel;
 import org.apache.syncope.client.console.panels.search.GroupSearchPanel;
 import org.apache.syncope.client.console.panels.search.SearchClause;
@@ -52,6 +56,8 @@ import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.SchemaTO;
 import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.core.util.lang.PropertyResolver;
+import org.apache.wicket.core.util.lang.PropertyResolverConverter;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
@@ -247,6 +253,25 @@ public class BeanPanel<T extends Serializable> extends Panel {
                     DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT);
         } else if (type.isEnum()) {
             result = new AjaxDropDownChoicePanel(id, fieldName, model).setChoices(List.of(type.getEnumConstants()));
+        } else if (Duration.class.equals(type)) {
+            result = new AjaxTextFieldPanel(id, fieldName, new IModel<>() {
+
+                private static final long serialVersionUID = 807008909842554829L;
+
+                @Override
+                public String getObject() {
+                    return Optional.ofNullable(PropertyResolver.getValue(fieldName, bean)).
+                            map(Object::toString).orElse(null);
+                }
+
+                @Override
+                public void setObject(final String object) {
+                    PropertyResolverConverter prc = new PropertyResolverConverter(
+                            SyncopeWebApplication.get().getConverterLocator(),
+                            SyncopeConsoleSession.get().getLocale());
+                    PropertyResolver.setValue(fieldName, bean, Duration.parse(object), prc);
+                }
+            });
         } else {
             // treat as String if nothing matched above
             result = new AjaxTextFieldPanel(id, fieldName, model);
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractJDBCConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractJDBCConf.java
new file mode 100644
index 0000000000..f26dcfd437
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractJDBCConf.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib;
+
+import java.io.Serializable;
+import java.time.Duration;
+import org.apache.commons.lang3.StringUtils;
+
+public abstract class AbstractJDBCConf implements Serializable {
+
+    private static final long serialVersionUID = 2675132530878660196L;
+
+    /**
+     * SQL query to execute. Example: {@code SELECT * FROM table WHERE name=?}.
+     */
+    private String sql;
+
+    /**
+     * The database dialect is a configuration setting for platform independent software (JPA, Hibernate, etc)
+     * which allows such software to translate its generic SQL statements into vendor specific DDL, DML.
+     */
+    private String dialect = "org.hibernate.dialect.H2Dialect";
+
+    /**
+     * The JDBC driver used to connect to the database.
+     */
+    private String driverClass = "org.h2.Driver";
+
+    /**
+     * The database connection URL.
+     */
+    private String url = "jdbc:h2:tcp://localhost:9092/mem:authdb;DB_CLOSE_DELAY=-1";
+
+    /**
+     * The database user.
+     * <p>
+     * The database user must have sufficient permissions to be able to handle
+     * schema changes and updates, when needed.
+     */
+    private String user = "sa";
+
+    /**
+     * The database connection password.
+     */
+    private String password = "sa";
+
+    /**
+     * Qualifies unqualified table names with the given catalog in generated SQL.
+     */
+    private String defaultCatalog;
+
+    /**
+     * Qualify unqualified table names with the given schema/tablespace in generated SQL.
+     */
+    private String defaultSchema;
+
+    /**
+     * The SQL query to be executed to test the validity of connections.
+     * This is for "legacy" databases that do not support the JDBC4 {@code Connection.isValid()} API.
+     */
+    private String healthQuery = StringUtils.EMPTY;
+
+    /**
+     * Controls the maximum amount of time that a connection is allowed to sit idle in the pool.
+     */
+    private Duration idleTimeout = Duration.parse("PT10M");
+
+    /**
+     * Attempts to do a JNDI data source look up for the data source name specified.
+     * Will attempt to locate the data source object as is.
+     */
+    private String dataSourceName;
+
+    /**
+     * Controls the minimum size that the pool is allowed
+     * to reach, including both idle and in-use connections.
+     */
+    private int minPoolSize = 6;
+
+    /**
+     * Controls the maximum number of connections to keep
+     * in the pool, including both idle and in-use connections.
+     */
+    private int maxPoolSize = 18;
+
+    /**
+     * Sets the maximum time in seconds that this data source will wait
+     * while attempting to connect to a database.
+     * A value of zero specifies that the timeout is the default system timeout
+     * if there is one; otherwise, it specifies that there is no timeout.
+     */
+    private Duration maxPoolWait = Duration.parse("PT2S");
+
+    /**
+     * Whether or not pool suspension is allowed.
+     * There is a performance impact when pool suspension is enabled.
+     * Unless you need it (for a redundancy system for example) do not enable it.
+     */
+    private boolean poolSuspension;
+
+    /**
+     * The maximum number of milliseconds that the
+     * pool will wait for a connection to be validated as alive.
+     */
+    private long poolTimeoutMillis = 1_000;
+
+    /**
+     * Controls the amount of time that a connection can be out of the pool before a message
+     * is logged indicating a possible connection leak.
+     */
+    private int poolLeakThreshold = 3_000;
+
+    public String getSql() {
+        return sql;
+    }
+
+    public void setSql(final String sql) {
+        this.sql = sql;
+    }
+
+    public String getDialect() {
+        return dialect;
+    }
+
+    public void setDialect(final String dialect) {
+        this.dialect = dialect;
+    }
+
+    public String getDriverClass() {
+        return driverClass;
+    }
+
+    public void setDriverClass(final String driverClass) {
+        this.driverClass = driverClass;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(final String url) {
+        this.url = url;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public void setUser(final String user) {
+        this.user = user;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(final String password) {
+        this.password = password;
+    }
+
+    public String getDefaultCatalog() {
+        return defaultCatalog;
+    }
+
+    public void setDefaultCatalog(final String defaultCatalog) {
+        this.defaultCatalog = defaultCatalog;
+    }
+
+    public String getDefaultSchema() {
+        return defaultSchema;
+    }
+
+    public void setDefaultSchema(final String defaultSchema) {
+        this.defaultSchema = defaultSchema;
+    }
+
+    public String getHealthQuery() {
+        return healthQuery;
+    }
+
+    public void setHealthQuery(final String healthQuery) {
+        this.healthQuery = healthQuery;
+    }
+
+    public Duration getIdleTimeout() {
+        return idleTimeout;
+    }
+
+    public void setIdleTimeout(final Duration idleTimeout) {
+        this.idleTimeout = idleTimeout;
+    }
+
+    public String getDataSourceName() {
+        return dataSourceName;
+    }
+
+    public void setDataSourceName(final String dataSourceName) {
+        this.dataSourceName = dataSourceName;
+    }
+
+    public int getMinPoolSize() {
+        return minPoolSize;
+    }
+
+    public void setMinPoolSize(final int minPoolSize) {
+        this.minPoolSize = minPoolSize;
+    }
+
+    public int getMaxPoolSize() {
+        return maxPoolSize;
+    }
+
+    public void setMaxPoolSize(final int maxPoolSize) {
+        this.maxPoolSize = maxPoolSize;
+    }
+
+    public Duration getMaxPoolWait() {
+        return maxPoolWait;
+    }
+
+    public void setMaxPoolWait(final Duration maxPoolWait) {
+        this.maxPoolWait = maxPoolWait;
+    }
+
+    public boolean isPoolSuspension() {
+        return poolSuspension;
+    }
+
+    public void setPoolSuspension(final boolean poolSuspension) {
+        this.poolSuspension = poolSuspension;
+    }
+
+    public long getPoolTimeoutMillis() {
+        return poolTimeoutMillis;
+    }
+
+    public void setPoolTimeoutMillis(final long poolTimeoutMillis) {
+        this.poolTimeoutMillis = poolTimeoutMillis;
+    }
+
+    public int getPoolLeakThreshold() {
+        return poolLeakThreshold;
+    }
+
+    public void setPoolLeakThreshold(final int poolLeakThreshold) {
+        this.poolLeakThreshold = poolLeakThreshold;
+    }
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractLDAPConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractLDAPConf.java
new file mode 100644
index 0000000000..c40154654a
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/AbstractLDAPConf.java
@@ -0,0 +1,453 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib;
+
+import java.io.Serializable;
+import java.time.Duration;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public abstract class AbstractLDAPConf implements Serializable {
+
+    private static final long serialVersionUID = 3705514707899419599L;
+
+    /**
+     * The ldap connection pool passivator.
+     */
+    public enum LdapConnectionPoolPassivator {
+
+        /**
+         * No passivator.
+         */
+        NONE,
+        /**
+         * Bind passivator.
+         */
+        BIND
+
+    }
+
+    public enum LdapConnectionStrategy {
+
+        /**
+         * First ldap used until it fails.
+         */
+        ACTIVE_PASSIVE,
+        /**
+         * Navigate the ldap url list for new connections and circle back.
+         */
+        ROUND_ROBIN,
+        /**
+         * Randomly pick a url.
+         */
+        RANDOM,
+        /**
+         * ldap urls based on DNS SRV records.
+         */
+        DNS_SRV
+
+    }
+
+    private String searchFilter;
+
+    /**
+     * Whether subtree searching is allowed.
+     */
+    private boolean subtreeSearch = true;
+
+    private String ldapUrl;
+
+    /**
+     * The bind DN to use when connecting to LDAP.
+     * LDAP connection configuration injected into the LDAP connection pool
+     * can be initialized with the following parameters:
+     * <ul>
+     * <li>{@code bindDn/bindCredential} provided - Use the provided credentials
+     * to bind when initializing connections.</li>
+     * <li>{@code bindDn/bindCredential} set to {@code *} - Use a fast-bind
+     * strategy to initialize the pool.</li>
+     * <li>{@code bindDn/bindCredential} set to blank - Skip connection
+     * initializing; perform operations anonymously.</li>
+     * <li>SASL mechanism provided - Use the given SASL mechanism
+     * to bind when initializing connections. </li>
+     * </ul>
+     */
+    private String bindDn;
+
+    /**
+     * The bind credential to use when connecting to LDAP.
+     */
+    private String bindCredential;
+
+    private String baseDn;
+
+    /**
+     * Whether to use a pooled connection factory in components.
+     */
+    private boolean disablePooling;
+
+    /**
+     * Minimum LDAP connection pool size.
+     * Size the pool should be initialized to and pruned to
+     */
+    private int minPoolSize = 3;
+
+    /**
+     * Maximum LDAP connection pool size which the pool can use to grow.
+     */
+    private int maxPoolSize = 10;
+
+    /**
+     * You may receive unexpected LDAP failures, when CAS is configured to authenticate
+     * using {@code DIRECT} or {@code AUTHENTICATED} types and LDAP is locked down to not allow anonymous
+     * binds/searches.
+     * Every second attempt with a given LDAP connection from the pool would fail if it was on
+     * the same connection as a failed login attempt, and the regular connection validator would
+     * similarly fail. When a connection is returned back to a pool,
+     * it still may contain the principal and credentials from the previous attempt.
+     * Before the next bind attempt using that connection, the validator tries to
+     * validate the connection again but fails because it’s no longer trying with the
+     * configured bind credentials but with whatever user DN was used in the previous step.
+     * Given the validation failure, the connection is closed and CAS would deny
+     * access by default. Passivators attempt to reconnect
+     * to LDAP with the configured bind credentials, effectively resetting the connection
+     * to what it should be after each bind request.
+     * Furthermore if you are seeing errors in the logs that resemble
+     * a 'Operation exception encountered, reopening connection' type of message, this
+     * usually is an indication that the connection pool’s validation timeout
+     * established and created by CAS is greater than the timeout configured
+     * in the LDAP server, or more likely, in the load balancer in front of
+     * the LDAP servers. You can adjust the LDAP server session’s timeout
+     * for connections, or you can teach CAS to use a validity period that
+     * is equal or less than the LDAP server session’s timeout.
+     * Accepted values are:
+     * <ul>
+     * <li>{@code NONE}: No passivation takes place.</li>
+     * <li>{@code BIND}: The default behavior which passivates a connection by performing a
+     * bind operation on it. This option requires the availability of bind credentials when establishing connections to
+     * LDAP.</li>
+     * </ul>
+     */
+    private LdapConnectionPoolPassivator poolPassivator = LdapConnectionPoolPassivator.BIND;
+
+    /**
+     * Whether connections should be validated when loaned out from the pool.
+     */
+    private boolean validateOnCheckout = true;
+
+    /**
+     * Whether connections should be validated periodically when the pool is idle.
+     */
+    private boolean validatePeriodically = true;
+
+    /**
+     * Period at which validation operations may time out.
+     */
+    private Duration validateTimeout = Duration.parse("PT5S");
+
+    /**
+     * Period at which pool should be validated.
+     */
+    private Duration validatePeriod = Duration.parse("PT5M");
+
+    /**
+     * Attempt to populate the connection pool early on startup
+     * and fail quickly if something goes wrong.
+     */
+    private boolean failFast = true;
+
+    /**
+     * Removes connections from the pool based on how long they have been idle in the available queue.
+     * Prunes connections that have been idle for more than the indicated amount.
+     */
+    private Duration idleTime = Duration.parse("PT10M");
+
+    /**
+     * Removes connections from the pool based on how long they have been idle in the available queue.
+     * Run the pruning process at the indicated interval.
+     */
+    private Duration prunePeriod = Duration.parse("PT2H");
+
+    /**
+     * The length of time the pool will block.
+     * By default the pool will block indefinitely and there is no guarantee that
+     * waiting threads will be serviced in the order in which they made their request.
+     * This option should be used with a blocking connection pool when you need to control the exact
+     * number of connections that can be created
+     */
+    private Duration blockWaitTime = Duration.parse("PT3S");
+
+    /**
+     * If multiple URLs are provided as the ldapURL this describes how each URL will be processed.
+     * <ul>
+     * <li>{@code ACTIVE_PASSIVE} First LDAP will be used for every request unless it fails and then the next shall be
+     * used.</li>
+     * <li>{@code ROUND_ROBIN} For each new connection the next url in the list will be used.</li>
+     * <li>{@code RANDOM} For each new connection a random LDAP url will be selected.</li>
+     * <li>{@code DNS_SRV} LDAP urls based on DNS SRV records of the configured/given LDAP url will be used. </li>
+     * </ul>
+     */
+    private LdapConnectionStrategy connectionStrategy;
+
+    /**
+     * Whether TLS should be used and enabled when establishing the connection.
+     */
+    private boolean useStartTls;
+
+    /**
+     * Sets the maximum amount of time that connects will block.
+     */
+    private Duration connectTimeout = Duration.parse("PT5S");
+
+    /**
+     * Duration of time to wait for responses.
+     */
+    private Duration responseTimeout = Duration.parse("PT5S");
+
+    /**
+     * Whether search/query results are allowed to match on multiple DNs,
+     * or whether a single unique DN is expected for the result.
+     */
+    private boolean allowMultipleDns;
+
+    /**
+     * Set if multiple Entries are allowed.
+     */
+    private boolean allowMultipleEntries;
+
+    /**
+     * Set if search referrals should be followed.
+     */
+    private boolean followReferrals = true;
+
+    /**
+     * Indicate the collection of attributes that are to be tagged and processed as binary
+     * attributes by the underlying search resolver.
+     */
+    private List<String> binaryAttributes = Stream.of("objectGUID", "objectSid").collect(Collectors.toList());
+
+    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;
+    }
+
+    public String getLdapUrl() {
+        return ldapUrl;
+    }
+
+    public void setLdapUrl(final String ldapUrl) {
+        this.ldapUrl = ldapUrl;
+    }
+
+    public String getBindDn() {
+        return bindDn;
+    }
+
+    public void setBindDn(final String bindDn) {
+        this.bindDn = bindDn;
+    }
+
+    public String getBindCredential() {
+        return bindCredential;
+    }
+
+    public void setBindCredential(final String bindCredential) {
+        this.bindCredential = bindCredential;
+    }
+
+    public String getBaseDn() {
+        return baseDn;
+    }
+
+    public void setBaseDn(final String baseDn) {
+        this.baseDn = baseDn;
+    }
+
+    public boolean isDisablePooling() {
+        return disablePooling;
+    }
+
+    public void setDisablePooling(final boolean disablePooling) {
+        this.disablePooling = disablePooling;
+    }
+
+    public int getMinPoolSize() {
+        return minPoolSize;
+    }
+
+    public void setMinPoolSize(final int minPoolSize) {
+        this.minPoolSize = minPoolSize;
+    }
+
+    public int getMaxPoolSize() {
+        return maxPoolSize;
+    }
+
+    public void setMaxPoolSize(final int maxPoolSize) {
+        this.maxPoolSize = maxPoolSize;
+    }
+
+    public LdapConnectionPoolPassivator getPoolPassivator() {
+        return poolPassivator;
+    }
+
+    public void setPoolPassivator(final LdapConnectionPoolPassivator poolPassivator) {
+        this.poolPassivator = poolPassivator;
+    }
+
+    public boolean isValidateOnCheckout() {
+        return validateOnCheckout;
+    }
+
+    public void setValidateOnCheckout(final boolean validateOnCheckout) {
+        this.validateOnCheckout = validateOnCheckout;
+    }
+
+    public boolean isValidatePeriodically() {
+        return validatePeriodically;
+    }
+
+    public void setValidatePeriodically(final boolean validatePeriodically) {
+        this.validatePeriodically = validatePeriodically;
+    }
+
+    public Duration getValidateTimeout() {
+        return validateTimeout;
+    }
+
+    public void setValidateTimeout(final Duration validateTimeout) {
+        this.validateTimeout = validateTimeout;
+    }
+
+    public Duration getValidatePeriod() {
+        return validatePeriod;
+    }
+
+    public void setValidatePeriod(final Duration validatePeriod) {
+        this.validatePeriod = validatePeriod;
+    }
+
+    public boolean isFailFast() {
+        return failFast;
+    }
+
+    public void setFailFast(final boolean failFast) {
+        this.failFast = failFast;
+    }
+
+    public Duration getIdleTime() {
+        return idleTime;
+    }
+
+    public void setIdleTime(final Duration idleTime) {
+        this.idleTime = idleTime;
+    }
+
+    public Duration getPrunePeriod() {
+        return prunePeriod;
+    }
+
+    public void setPrunePeriod(final Duration prunePeriod) {
+        this.prunePeriod = prunePeriod;
+    }
+
+    public Duration getBlockWaitTime() {
+        return blockWaitTime;
+    }
+
+    public void setBlockWaitTime(final Duration blockWaitTime) {
+        this.blockWaitTime = blockWaitTime;
+    }
+
+    public LdapConnectionStrategy getConnectionStrategy() {
+        return connectionStrategy;
+    }
+
+    public void setConnectionStrategy(final LdapConnectionStrategy connectionStrategy) {
+        this.connectionStrategy = connectionStrategy;
+    }
+
+    public boolean isUseStartTls() {
+        return useStartTls;
+    }
+
+    public void setUseStartTls(final boolean useStartTls) {
+        this.useStartTls = useStartTls;
+    }
+
+    public Duration getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    public void setConnectTimeout(final Duration connectTimeout) {
+        this.connectTimeout = connectTimeout;
+    }
+
+    public Duration getResponseTimeout() {
+        return responseTimeout;
+    }
+
+    public void setResponseTimeout(final Duration responseTimeout) {
+        this.responseTimeout = responseTimeout;
+    }
+
+    public boolean isAllowMultipleDns() {
+        return allowMultipleDns;
+    }
+
+    public void setAllowMultipleDns(final boolean allowMultipleDns) {
+        this.allowMultipleDns = allowMultipleDns;
+    }
+
+    public boolean isAllowMultipleEntries() {
+        return allowMultipleEntries;
+    }
+
+    public void setAllowMultipleEntries(final boolean allowMultipleEntries) {
+        this.allowMultipleEntries = allowMultipleEntries;
+    }
+
+    public boolean isFollowReferrals() {
+        return followReferrals;
+    }
+
+    public void setFollowReferrals(final boolean followReferrals) {
+        this.followReferrals = followReferrals;
+    }
+
+    public List<String> getBinaryAttributes() {
+        return binaryAttributes;
+    }
+
+    public void setBinaryAttributes(final List<String> binaryAttributes) {
+        this.binaryAttributes = binaryAttributes;
+    }
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java
similarity index 70%
copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java
index 9fbac72ffe..9180728ccc 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java
@@ -16,11 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.auth;
+package org.apache.syncope.common.lib.attr;
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import java.util.Map;
 import org.apache.syncope.common.lib.BaseBean;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_class")
-public interface AuthModuleConf extends BaseBean {
+public interface AttrRepoConf extends BaseBean {
+
+    interface Mapper {
+
+        Map<String, Object> map(StubAttrRepoConf conf);
+
+        Map<String, Object> map(LDAPAttrRepoConf conf);
+
+        Map<String, Object> map(JDBCAttrRepoConf conf);
+
+        Map<String, Object> map(SyncopeAttrRepoConf conf);
+    }
+
+    Map<String, Object> map(Mapper mapper);
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/JDBCAttrRepoConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/JDBCAttrRepoConf.java
new file mode 100644
index 0000000000..71a2ef7151
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/JDBCAttrRepoConf.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.attr;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.AbstractJDBCConf;
+
+public class JDBCAttrRepoConf extends AbstractJDBCConf implements AttrRepoConf {
+
+    private static final long serialVersionUID = -4474060002361453868L;
+
+    public enum CaseCanonicalizationMode {
+
+        LOWER,
+        UPPER,
+        NONE;
+
+    }
+
+    public enum QueryType {
+        AND,
+        OR
+
+    }
+
+    /**
+     * Designed to work against a table where there is a mapping of one row to one user.
+     */
+    private boolean singleRow = true;
+
+    /**
+     * If the SQL should only be run if all attributes listed in the mappings exist in the query.
+     */
+    private boolean requireAllAttributes = true;
+
+    /**
+     * When constructing the final person object from the attribute repository,
+     * indicate how the username should be canonicalized.
+     */
+    private CaseCanonicalizationMode caseCanonicalization = CaseCanonicalizationMode.NONE;
+
+    /**
+     * Indicates how multiple attributes in a query should be concatenated together.
+     */
+    private QueryType queryType = QueryType.AND;
+
+    /**
+     * Used only when there is a mapping of many rows to one user.
+     * This is done using a key-value structure where the key is the
+     * name of the "attribute name" column the value is the name of the "attribute value" column.
+     */
+    private final Map<String, String> columnMappings = new HashMap<>(0);
+
+    /**
+     * Username attribute(s) to use when running the SQL query.
+     */
+    private final List<String> username = new ArrayList<>(0);
+
+    /**
+     * Map of attributes to fetch from the database.
+     * Attributes are defined using a key-value structure
+     * where CAS allows the attribute name/key to be renamed virtually
+     * to a different attribute. The key is the attribute fetched
+     * from the data source and the value is the attribute name CAS should
+     * use for virtual renames.
+     */
+    private final Map<String, String> attributes = new HashMap<>(0);
+
+    /**
+     * Collection of attributes, used to build the SQL query, that should go through
+     * a case canonicalization process defined as {@code key->value}.
+     */
+    private final List<String> caseInsensitiveQueryAttributes = new ArrayList<>(0);
+
+    /**
+     * Define a {@code Map} of query attribute names to data-layer attribute names to use when building the query.
+     * The key is always the name of the query attribute that is defined by CAS and passed internally,
+     * and the value is the database column that should map.
+     */
+    private final Map<String, String> queryAttributes = new HashMap<>(0);
+
+    public boolean isSingleRow() {
+        return singleRow;
+    }
+
+    public void setSingleRow(final boolean singleRow) {
+        this.singleRow = singleRow;
+    }
+
+    public boolean isRequireAllAttributes() {
+        return requireAllAttributes;
+    }
+
+    public void setRequireAllAttributes(final boolean requireAllAttributes) {
+        this.requireAllAttributes = requireAllAttributes;
+    }
+
+    public CaseCanonicalizationMode getCaseCanonicalization() {
+        return caseCanonicalization;
+    }
+
+    public void setCaseCanonicalization(final CaseCanonicalizationMode caseCanonicalization) {
+        this.caseCanonicalization = caseCanonicalization;
+    }
+
+    public QueryType getQueryType() {
+        return queryType;
+    }
+
+    public void setQueryType(final QueryType queryType) {
+        this.queryType = queryType;
+    }
+
+    public Map<String, String> getColumnMappings() {
+        return columnMappings;
+    }
+
+    public List<String> getUsername() {
+        return username;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public List<String> getCaseInsensitiveQueryAttributes() {
+        return caseInsensitiveQueryAttributes;
+    }
+
+    public Map<String, String> getQueryAttributes() {
+        return queryAttributes;
+    }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/LDAPAttrRepoConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/LDAPAttrRepoConf.java
new file mode 100644
index 0000000000..26de86db1b
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/LDAPAttrRepoConf.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.attr;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.syncope.common.lib.AbstractLDAPConf;
+
+public class LDAPAttrRepoConf extends AbstractLDAPConf implements AttrRepoConf {
+
+    private static final long serialVersionUID = -471527731042579422L;
+
+    /**
+     * Map of attributes to fetch from the database.
+     * Attributes are defined using a key-value structure
+     * where CAS allows the attribute name/key to be renamed virtually
+     * to a different attribute. The key is the attribute fetched
+     * from the data source and the value is the attribute name CAS should
+     * use for virtual renames.
+     */
+    private final Map<String, String> attributes = new HashMap<>(0);
+
+    /**
+     * Whether all existing attributes should be passed
+     * down to the query builder map and be used in the construction
+     * of the filter.
+     */
+    private boolean useAllQueryAttributes = true;
+
+    /**
+     * Define a {@code Map} of query attribute names to data-layer attribute names to use when building the query.
+     * The key is always the name of the query attribute that is defined by CAS and passed internally,
+     * and the value is the column/field that should map.
+     */
+    private final Map<String, String> queryAttributes = new HashMap<>(0);
+
+    public boolean isUseAllQueryAttributes() {
+        return useAllQueryAttributes;
+    }
+
+    public void setUseAllQueryAttributes(final boolean useAllQueryAttributes) {
+        this.useAllQueryAttributes = useAllQueryAttributes;
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public Map<String, String> getQueryAttributes() {
+        return queryAttributes;
+    }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/StubAttrRepoConf.java
similarity index 57%
copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/StubAttrRepoConf.java
index f9c8a253a2..6736a00d00 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/StubAttrRepoConf.java
@@ -16,18 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.auth;
+package org.apache.syncope.common.lib.attr;
 
 import java.util.HashMap;
 import java.util.Map;
 
-public class StaticAuthModuleConf implements AuthModuleConf {
+public class StubAttrRepoConf implements AttrRepoConf {
 
-    private static final long serialVersionUID = -7775771400318503131L;
+    private static final long serialVersionUID = 835890230066546723L;
 
-    private final Map<String, String> users = new HashMap<>();
+    /**
+     * Static attributes that need to be mapped to a hardcoded value belong here.
+     * The structure follows a key-value pair where key is the attribute name
+     * and value is the attribute value.
+     */
+    private final Map<String, String> attributes = new HashMap<>(0);
 
-    public Map<String, String> getUsers() {
-        return users;
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
     }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/SyncopeAttrRepoConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/SyncopeAttrRepoConf.java
new file mode 100644
index 0000000000..6d2d9fe565
--- /dev/null
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/SyncopeAttrRepoConf.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.attr;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.syncope.common.lib.SyncopeConstants;
+
+public class SyncopeAttrRepoConf implements AttrRepoConf {
+
+    private static final long serialVersionUID = -3334329948161152222L;
+
+    private String domain = SyncopeConstants.MASTER_DOMAIN;
+
+    /**
+     * User FIQL filter to use for searching.
+     */
+    protected String searchFilter;
+
+    /**
+     * Specify the username for REST authentication.
+     */
+    private String basicAuthUsername;
+
+    /**
+     * Specify the password for REST authentication.
+     */
+    private String basicAuthPassword;
+
+    /**
+     * Headers, defined as a Map, to include in the request when making the REST call.
+     * Will overwrite any header that CAS is pre-defined to
+     * send and include in the request. Key in the map should be the header name
+     * and the value in the map should be the header value.
+     */
+    private final Map<String, String> headers = new HashMap<>();
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public void setDomain(final String domain) {
+        this.domain = domain;
+    }
+
+    public String getSearchFilter() {
+        return searchFilter;
+    }
+
+    public void setSearchFilter(final String searchFilter) {
+        this.searchFilter = searchFilter;
+    }
+
+    public String getBasicAuthUsername() {
+        return basicAuthUsername;
+    }
+
+    public void setBasicAuthUsername(final String basicAuthUsername) {
+        this.basicAuthUsername = basicAuthUsername;
+    }
+
+    public String getBasicAuthPassword() {
+        return basicAuthPassword;
+    }
+
+    public void setBasicAuthPassword(final String basicAuthPassword) {
+        this.basicAuthPassword = basicAuthPassword;
+    }
+
+    public Map<String, String> getHeaders() {
+        return headers;
+    }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
+}
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
index 9fbac72ffe..8ba5d2cc39 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/AuthModuleConf.java
@@ -19,8 +19,36 @@
 package org.apache.syncope.common.lib.auth;
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import java.util.Map;
 import org.apache.syncope.common.lib.BaseBean;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_class")
 public interface AuthModuleConf extends BaseBean {
+
+    interface Mapper {
+
+        Map<String, Object> map(StaticAuthModuleConf conf);
+
+        Map<String, Object> map(LDAPAuthModuleConf conf);
+
+        Map<String, Object> map(JDBCAuthModuleConf conf);
+
+        Map<String, Object> map(JaasAuthModuleConf conf);
+
+        Map<String, Object> map(OIDCAuthModuleConf conf);
+
+        Map<String, Object> map(SAML2IdPAuthModuleConf conf);
+
+        Map<String, Object> map(SyncopeAuthModuleConf conf);
+
+        Map<String, Object> map(GoogleMfaAuthModuleConf conf);
+
+        Map<String, Object> map(DuoMfaAuthModuleConf conf);
+
+        Map<String, Object> map(U2FAuthModuleConf conf);
+
+        Map<String, Object> map(SimpleMfaAuthModuleConf conf);
+    }
+
+    Map<String, Object> map(Mapper mapper);
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/DuoMfaAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/DuoMfaAuthModuleConf.java
index d85ce3a2d1..ac29429f31 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/DuoMfaAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/DuoMfaAuthModuleConf.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.common.lib.auth;
 
+import java.util.Map;
+
 public class DuoMfaAuthModuleConf implements MFAAuthModuleConf {
 
     private static final long serialVersionUID = -2883257599439312426L;
@@ -66,4 +68,9 @@ public class DuoMfaAuthModuleConf implements MFAAuthModuleConf {
     public void setApiHost(final String apiHost) {
         this.apiHost = apiHost;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
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 c5c1d999ff..0caa62bb23 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
@@ -19,12 +19,14 @@
 package org.apache.syncope.common.lib.auth;
 
 import java.io.Serializable;
+import java.util.Map;
+import org.apache.syncope.common.lib.AbstractLDAPConf;
 
 public class GoogleMfaAuthModuleConf implements MFAAuthModuleConf {
 
     private static final long serialVersionUID = -7883257599139312426L;
 
-    public static class LDAP implements Serializable {
+    public static class LDAP extends AbstractLDAPConf implements Serializable {
 
         private static final long serialVersionUID = -7274446267090678730L;
 
@@ -33,38 +35,6 @@ public class GoogleMfaAuthModuleConf implements MFAAuthModuleConf {
          */
         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;
         }
@@ -72,54 +42,6 @@ public class GoogleMfaAuthModuleConf implements MFAAuthModuleConf {
         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;
-        }
     }
 
     /**
@@ -206,4 +128,9 @@ public class GoogleMfaAuthModuleConf implements MFAAuthModuleConf {
     public void setLdap(final LDAP ldap) {
         this.ldap = ldap;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JDBCAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JDBCAuthModuleConf.java
index 023ef1cbed..b1037451f8 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JDBCAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JDBCAuthModuleConf.java
@@ -20,16 +20,13 @@ package org.apache.syncope.common.lib.auth;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.AbstractJDBCConf;
 
-public class JDBCAuthModuleConf implements AuthModuleConf {
+public class JDBCAuthModuleConf extends AbstractJDBCConf implements AuthModuleConf {
 
     private static final long serialVersionUID = 8383233437907219385L;
 
-    /**
-     * SQL query to execute. Example: {@code SELECT * FROM table WHERE name=?}.
-     */
-    private String sql;
-
     /**
      * Password field/column name to retrieve.
      */
@@ -45,48 +42,11 @@ public class JDBCAuthModuleConf implements AuthModuleConf {
      */
     private String fieldDisabled;
 
-    /**
-     * The database dialect is a configuration setting for platform independent software (JPA, Hibernate, etc)
-     * which allows such software to translate its generic SQL statements into vendor specific DDL, DML.
-     */
-    private String dialect = "org.hibernate.dialect.H2Dialect";
-
-    /**
-     * The JDBC driver used to connect to the database.
-     */
-    private String driverClass = "org.h2.Driver";
-
-    /**
-     * The database connection URL.
-     */
-    private String url = "jdbc:h2:tcp://localhost:9092/mem:authdb;DB_CLOSE_DELAY=-1";
-
-    /**
-     * The database user.
-     * <p>
-     * The database user must have sufficient permissions to be able to handle
-     * schema changes and updates, when needed.
-     */
-    private String user = "sa";
-
-    /**
-     * The database connection password.
-     */
-    private String password = "sa";
-
     /**
      * List of column names to fetch as user attributes.
      */
     private final List<String> principalAttributeList = new ArrayList<>();
 
-    public String getSql() {
-        return sql;
-    }
-
-    public void setSql(final String sql) {
-        this.sql = sql;
-    }
-
     public String getFieldPassword() {
         return fieldPassword;
     }
@@ -115,43 +75,8 @@ public class JDBCAuthModuleConf implements AuthModuleConf {
         return principalAttributeList;
     }
 
-    public String getDialect() {
-        return dialect;
-    }
-
-    public void setDialect(final String dialect) {
-        this.dialect = dialect;
-    }
-
-    public String getDriverClass() {
-        return driverClass;
-    }
-
-    public void setDriverClass(final String driverClass) {
-        this.driverClass = driverClass;
-    }
-
-    public String getUrl() {
-        return url;
-    }
-
-    public void setUrl(final String url) {
-        this.url = url;
-    }
-
-    public String getUser() {
-        return user;
-    }
-
-    public void setUser(final String user) {
-        this.user = user;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(final String password) {
-        this.password = password;
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
     }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JaasAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JaasAuthModuleConf.java
index 8768226a47..1b4f5ef908 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JaasAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/JaasAuthModuleConf.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.common.lib.auth;
 
+import java.util.Map;
+
 public class JaasAuthModuleConf implements AuthModuleConf {
 
     private static final long serialVersionUID = -7775771400318503131L;
@@ -80,4 +82,9 @@ public class JaasAuthModuleConf implements AuthModuleConf {
     public void setLoginConfigurationFile(final String loginConfigurationFile) {
         this.loginConfigurationFile = loginConfigurationFile;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/LDAPAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/LDAPAuthModuleConf.java
index 6ab33eb39e..9c3c97746b 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/LDAPAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/LDAPAuthModuleConf.java
@@ -20,13 +20,13 @@ package org.apache.syncope.common.lib.auth;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.AbstractLDAPConf;
 
-public class LDAPAuthModuleConf implements AuthModuleConf {
+public class LDAPAuthModuleConf extends AbstractLDAPConf implements AuthModuleConf {
 
     private static final long serialVersionUID = -471527731042579422L;
 
-    protected String searchFilter;
-
     /**
      * The attribute value that should be used
      * for the authenticated username, upon a successful authentication
@@ -34,50 +34,11 @@ public class LDAPAuthModuleConf implements AuthModuleConf {
      */
     private String userIdAttribute;
 
-    /**
-     * Whether subtree searching is allowed.
-     */
-    private boolean subtreeSearch = true;
-
-    private String ldapUrl;
-
-    /**
-     * The bind DN to use when connecting to LDAP.
-     * LDAP connection configuration injected into the LDAP connection pool
-     * can be initialized with the following parameters:
-     * <ul>
-     * <li>{@code bindDn/bindCredential} provided - Use the provided credentials
-     * to bind when initializing connections.</li>
-     * <li>{@code bindDn/bindCredential} set to {@code *} - Use a fast-bind
-     * strategy to initialize the pool.</li>
-     * <li>{@code bindDn/bindCredential} set to blank - Skip connection
-     * initializing; perform operations anonymously.</li>
-     * <li>SASL mechanism provided - Use the given SASL mechanism
-     * to bind when initializing connections. </li>
-     * </ul>
-     */
-    private String bindDn;
-
-    /**
-     * The bind credential to use when connecting to LDAP.
-     */
-    private String bindCredential;
-
-    private String baseDn;
-
     /**
      * List of attribute names to fetch as user attributes.
      */
     private final List<String> principalAttributeList = new ArrayList<>();
 
-    public String getSearchFilter() {
-        return searchFilter;
-    }
-
-    public void setSearchFilter(final String searchFilter) {
-        this.searchFilter = searchFilter;
-    }
-
     public String getUserIdAttribute() {
         return userIdAttribute;
     }
@@ -86,47 +47,12 @@ public class LDAPAuthModuleConf implements AuthModuleConf {
         this.userIdAttribute = userIdAttribute;
     }
 
-    public boolean isSubtreeSearch() {
-        return subtreeSearch;
-    }
-
-    public void setSubtreeSearch(final boolean subtreeSearch) {
-        this.subtreeSearch = subtreeSearch;
-    }
-
-    public String getLdapUrl() {
-        return ldapUrl;
-    }
-
-    public void setLdapUrl(final String ldapUrl) {
-        this.ldapUrl = ldapUrl;
-    }
-
-    public String getBindDn() {
-        return bindDn;
-    }
-
-    public void setBindDn(final String bindDn) {
-        this.bindDn = bindDn;
-    }
-
-    public String getBindCredential() {
-        return bindCredential;
-    }
-
-    public void setBindCredential(final String bindCredential) {
-        this.bindCredential = bindCredential;
-    }
-
-    public String getBaseDn() {
-        return baseDn;
-    }
-
-    public void setBaseDn(final String baseDn) {
-        this.baseDn = baseDn;
-    }
-
     public List<String> getPrincipalAttributeList() {
         return principalAttributeList;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
index 4687263cc4..09141a74fe 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/OIDCAuthModuleConf.java
@@ -166,4 +166,9 @@ public class OIDCAuthModuleConf implements AuthModuleConf {
     public void setResponseType(final String responseType) {
         this.responseType = responseType;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
index f1489f9cbf..bdf1ffe07f 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SAML2IdPAuthModuleConf.java
@@ -20,6 +20,7 @@ package org.apache.syncope.common.lib.auth;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import org.apache.syncope.common.lib.types.SAML2BindingType;
 
 public class SAML2IdPAuthModuleConf implements AuthModuleConf {
@@ -383,4 +384,9 @@ public class SAML2IdPAuthModuleConf implements AuthModuleConf {
     public void setProviderName(final String providerName) {
         this.providerName = providerName;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SimpleMfaAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SimpleMfaAuthModuleConf.java
index 7ac948b973..a102533fb2 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SimpleMfaAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SimpleMfaAuthModuleConf.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.common.lib.auth;
 
+import java.util.Map;
+
 public class SimpleMfaAuthModuleConf implements MFAAuthModuleConf {
 
     private static final long serialVersionUID = -7663257599139312426L;
@@ -96,4 +98,9 @@ public class SimpleMfaAuthModuleConf implements MFAAuthModuleConf {
     public void setTokenLength(final int tokenLength) {
         this.tokenLength = tokenLength;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java
index f9c8a253a2..b7b82ee9b1 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/StaticAuthModuleConf.java
@@ -30,4 +30,9 @@ public class StaticAuthModuleConf implements AuthModuleConf {
     public Map<String, String> getUsers() {
         return users;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SyncopeAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SyncopeAuthModuleConf.java
index f61834bf69..e706b73e70 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SyncopeAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/SyncopeAuthModuleConf.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.common.lib.auth;
 
+import java.util.Map;
 import org.apache.syncope.common.lib.SyncopeConstants;
 
 public class SyncopeAuthModuleConf implements AuthModuleConf {
@@ -33,4 +34,9 @@ public class SyncopeAuthModuleConf implements AuthModuleConf {
     public void setDomain(final String domain) {
         this.domain = domain;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/U2FAuthModuleConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/U2FAuthModuleConf.java
index dc15038abd..617eec67d3 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/U2FAuthModuleConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/auth/U2FAuthModuleConf.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.common.lib.auth;
 
+import java.util.Map;
+
 public class U2FAuthModuleConf implements MFAAuthModuleConf {
 
     private static final long serialVersionUID = -1235771400318503131L;
@@ -66,4 +68,9 @@ public class U2FAuthModuleConf implements MFAAuthModuleConf {
     public void setExpireDevicesTimeUnit(final String expireDevicesTimeUnit) {
         this.expireDevicesTimeUnit = expireDevicesTimeUnit;
     }
+
+    @Override
+    public Map<String, Object> map(final Mapper mapper) {
+        return mapper.map(this);
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAttrReleasePolicyConf.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAttrReleasePolicyConf.java
index 90349f5de4..0c65f46dbf 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAttrReleasePolicyConf.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAttrReleasePolicyConf.java
@@ -20,13 +20,91 @@ package org.apache.syncope.common.lib.policy;
 
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 public class DefaultAttrReleasePolicyConf implements AttrReleasePolicyConf {
 
     private static final long serialVersionUID = -1969836661359025380L;
 
+    public enum PrincipalAttrRepoMergingStrategy {
+        /**
+         * Replace attributes. Overwrites existing attribute values, if any.
+         */
+        REPLACE,
+        /**
+         * Add attributes.
+         * Retains existing attribute values if any, and ignores values from subsequent sources in the resolution chain.
+         */
+        ADD,
+        /**
+         * No merging.
+         * Doesn't merge attributes, ignores attributes from non-authentication attribute repositories.
+         */
+        NONE,
+        /**
+         * Multivalued attributes.
+         * Combines all values into a single attribute, essentially creating a multi-valued attribute.
+         */
+        MULTIVALUED;
+
+    }
+
+    public static class PrincipalAttrRepoConf implements Serializable {
+
+        private static final long serialVersionUID = 6369987956789092057L;
+
+        private PrincipalAttrRepoMergingStrategy mergingStrategy = PrincipalAttrRepoMergingStrategy.MULTIVALUED;
+
+        private boolean ignoreResolvedAttributes;
+
+        private long expiration;
+
+        private TimeUnit timeUnit = TimeUnit.HOURS;
+
+        private final List<String> attrRepos = new ArrayList<>();
+
+        public PrincipalAttrRepoMergingStrategy getMergingStrategy() {
+            return mergingStrategy;
+        }
+
+        public void setMergingStrategy(final PrincipalAttrRepoMergingStrategy mergingStrategy) {
+            this.mergingStrategy = mergingStrategy;
+        }
+
+        public boolean isIgnoreResolvedAttributes() {
+            return ignoreResolvedAttributes;
+        }
+
+        public void setIgnoreResolvedAttributes(final boolean ignoreResolvedAttributes) {
+            this.ignoreResolvedAttributes = ignoreResolvedAttributes;
+        }
+
+        public long getExpiration() {
+            return expiration;
+        }
+
+        public void setExpiration(final long expiration) {
+            this.expiration = expiration;
+        }
+
+        public TimeUnit getTimeUnit() {
+            return timeUnit;
+        }
+
+        public void setTimeUnit(final TimeUnit timeUnit) {
+            this.timeUnit = timeUnit;
+        }
+
+        @JacksonXmlElementWrapper(localName = "attrRepos")
+        @JacksonXmlProperty(localName = "attrRepo")
+        public List<String> getAttrRepos() {
+            return attrRepos;
+        }
+    }
+
     /**
      * Specify the list of allowed attribute to release.
      * Use the special {@code *} to release everything.
@@ -37,6 +115,10 @@ public class DefaultAttrReleasePolicyConf implements AttrReleasePolicyConf {
 
     private final List<String> includeOnlyAttrs = new ArrayList<>();
 
+    private String principalIdAttr;
+
+    private final PrincipalAttrRepoConf principalAttrRepoConf = new PrincipalAttrRepoConf();
+
     @JacksonXmlElementWrapper(localName = "allowedAttrs")
     @JacksonXmlProperty(localName = "allowedAttr")
     public List<String> getAllowedAttrs() {
@@ -54,4 +136,16 @@ public class DefaultAttrReleasePolicyConf implements AttrReleasePolicyConf {
     public List<String> getIncludeOnlyAttrs() {
         return includeOnlyAttrs;
     }
+
+    public String getPrincipalIdAttr() {
+        return principalIdAttr;
+    }
+
+    public void setPrincipalIdAttr(final String principalIdAttr) {
+        this.principalIdAttr = principalIdAttr;
+    }
+
+    public PrincipalAttrRepoConf getPrincipalAttrRepoConf() {
+        return principalAttrRepoConf;
+    }
 }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AttrRepoTO.java
similarity index 74%
copy from common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AttrRepoTO.java
index 0fc1533adc..a10712a026 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AttrRepoTO.java
@@ -20,13 +20,13 @@ package org.apache.syncope.common.lib.to;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
 import javax.ws.rs.PathParam;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.common.lib.attr.AttrRepoConf;
+import org.apache.syncope.common.lib.types.AttrRepoState;
 
-public class AuthModuleTO implements EntityTO {
+public class AttrRepoTO implements EntityTO {
 
     private static final long serialVersionUID = -7490425997956703057L;
 
@@ -34,10 +34,14 @@ public class AuthModuleTO implements EntityTO {
 
     private String description;
 
-    private AuthModuleConf conf;
+    private AttrRepoState state = AttrRepoState.ACTIVE;
+
+    private int order = 0;
 
     private final List<ItemTO> items = new ArrayList<>();
 
+    private AttrRepoConf conf;
+
     @Override
     public String getKey() {
         return key;
@@ -57,24 +61,32 @@ public class AuthModuleTO implements EntityTO {
         this.description = description;
     }
 
-    public AuthModuleConf getConf() {
-        return conf;
+    public AttrRepoState getState() {
+        return state;
     }
 
-    public void setConf(final AuthModuleConf conf) {
-        this.conf = conf;
+    public void setState(final AttrRepoState state) {
+        this.state = state;
+    }
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(final int order) {
+        this.order = order;
     }
 
     public List<ItemTO> getItems() {
         return items;
     }
 
-    public boolean add(final ItemTO item) {
-        return Optional.ofNullable(item).filter(itemTO -> items.contains(itemTO) || items.add(itemTO)).isPresent();
+    public AttrRepoConf getConf() {
+        return conf;
     }
 
-    public boolean remove(final ItemTO item) {
-        return this.items.remove(item);
+    public void setConf(final AttrRepoConf conf) {
+        this.conf = conf;
     }
 
     @Override
@@ -88,10 +100,12 @@ public class AuthModuleTO implements EntityTO {
         if (getClass() != obj.getClass()) {
             return false;
         }
-        AuthModuleTO other = (AuthModuleTO) obj;
+        AttrRepoTO other = (AttrRepoTO) obj;
         return new EqualsBuilder().
                 append(key, other.key).
                 append(description, other.description).
+                append(state, other.state).
+                append(order, other.order).
                 append(items, other.items).
                 append(conf, other.conf).
                 build();
@@ -102,6 +116,8 @@ public class AuthModuleTO implements EntityTO {
         return new HashCodeBuilder().
                 append(key).
                 append(description).
+                append(state).
+                append(order).
                 append(items).
                 append(conf).
                 build();
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java
index 0fc1533adc..fa7239f406 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/AuthModuleTO.java
@@ -20,11 +20,11 @@ package org.apache.syncope.common.lib.to;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
 import javax.ws.rs.PathParam;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.common.lib.types.AuthModuleState;
 
 public class AuthModuleTO implements EntityTO {
 
@@ -34,10 +34,14 @@ public class AuthModuleTO implements EntityTO {
 
     private String description;
 
-    private AuthModuleConf conf;
+    private AuthModuleState state = AuthModuleState.ACTIVE;
+
+    private int order = 0;
 
     private final List<ItemTO> items = new ArrayList<>();
 
+    private AuthModuleConf conf;
+
     @Override
     public String getKey() {
         return key;
@@ -57,24 +61,32 @@ public class AuthModuleTO implements EntityTO {
         this.description = description;
     }
 
-    public AuthModuleConf getConf() {
-        return conf;
+    public AuthModuleState getState() {
+        return state;
     }
 
-    public void setConf(final AuthModuleConf conf) {
-        this.conf = conf;
+    public void setState(final AuthModuleState state) {
+        this.state = state;
+    }
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(final int order) {
+        this.order = order;
     }
 
     public List<ItemTO> getItems() {
         return items;
     }
 
-    public boolean add(final ItemTO item) {
-        return Optional.ofNullable(item).filter(itemTO -> items.contains(itemTO) || items.add(itemTO)).isPresent();
+    public AuthModuleConf getConf() {
+        return conf;
     }
 
-    public boolean remove(final ItemTO item) {
-        return this.items.remove(item);
+    public void setConf(final AuthModuleConf conf) {
+        this.conf = conf;
     }
 
     @Override
@@ -92,6 +104,8 @@ public class AuthModuleTO implements EntityTO {
         return new EqualsBuilder().
                 append(key, other.key).
                 append(description, other.description).
+                append(state, other.state).
+                append(order, other.order).
                 append(items, other.items).
                 append(conf, other.conf).
                 build();
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
index 305decac53..a80ecbe5c4 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AMEntitlement.java
@@ -60,6 +60,16 @@ public final class AMEntitlement {
 
     public static final String AUTH_MODULE_DELETE = "AUTH_MODULE_DELETE";
 
+    public static final String ATTR_REPO_LIST = "ATTR_REPO_LIST";
+
+    public static final String ATTR_REPO_CREATE = "ATTR_REPO_CREATE";
+
+    public static final String ATTR_REPO_READ = "ATTR_REPO_READ";
+
+    public static final String ATTR_REPO_UPDATE = "ATTR_REPO_UPDATE";
+
+    public static final String ATTR_REPO_DELETE = "ATTR_REPO_DELETE";
+
     public static final String SAML2_IDP_ENTITY_SET = "SAML2_IDP_ENTITY_SET";
 
     public static final String SAML2_IDP_ENTITY_LIST = "SAML2_IDP_ENTITY_LIST";
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AttrRepoState.java
similarity index 60%
copy from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AttrRepoState.java
index b17f377313..fa38485333 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AttrRepoState.java
@@ -16,21 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.common.lib.types;
 
-import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+public enum AttrRepoState {
+    /**
+     * Active and enabled repository,
+     * and is invoked by default automatically.
+     */
+    ACTIVE,
+    /**
+     * Attribute repository is disabled and will not be used
+     * to resolve people and attributes.
+     */
+    DISABLED,
+    /**
+     * Repository is in a semi-enabled state,
+     * waiting to be called only on-demand when explicitly
+     * asked for and will not be registered into the resolution plan.
+     */
+    STANDBY
 
-public interface AuthModuleDAO extends DAO<AuthModule> {
-
-    AuthModule find(String key);
-
-    List<AuthModule> findAll();
-
-    AuthModule save(AuthModule authModule);
-
-    void delete(String key);
-
-    void delete(AuthModule authModule);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCJWKSDAO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleState.java
similarity index 67%
copy from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCJWKSDAO.java
copy to common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleState.java
index 98acd8aa88..37581eeb58 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCJWKSDAO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/types/AuthModuleState.java
@@ -16,15 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.common.lib.types;
 
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
+public enum AuthModuleState {
+    /**
+     * Active authentication handler,
+     * and is invoked by default automatically to verify credentials globally.
+     */
+    ACTIVE,
+    /**
+     * Authentication handler is in a semi-enabled state,
+     * waiting to be called only on-demand when explicitly
+     * asked for.
+     */
+    STANDBY
 
-public interface OIDCJWKSDAO extends DAO<OIDCJWKS> {
-    OIDCJWKS get();
-
-    OIDCJWKS save(OIDCJWKS jwks);
-
-    void delete();
 }
diff --git a/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AttrRepoService.java b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AttrRepoService.java
new file mode 100644
index 0000000000..3a0f50addc
--- /dev/null
+++ b/common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AttrRepoService.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.rest.api.service;
+
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+/**
+ * REST operations for attribute repositories.
+ */
+@Tag(name = "AttrRepos")
+@SecurityRequirements({
+    @SecurityRequirement(name = "BasicAuthentication"),
+    @SecurityRequirement(name = "Bearer") })
+@Path("attrRepos")
+public interface AttrRepoService extends JAXRSService {
+
+    /**
+     * Returns the attribute repository matching the given key.
+     *
+     * @param key key of requested attribute repository
+     * @return attribute repository with matching id
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    AttrRepoTO read(@NotNull @PathParam("key") String key);
+
+    /**
+     * Returns a list of attribute repositories.
+     *
+     * @return list of attribute repositories
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    List<AttrRepoTO> list();
+
+    /**
+     * Create a new attribute repository.
+     *
+     * @param attrRepoTO AttrRepo to be created.
+     * @return Response object featuring Location header of created attribute repository
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "201",
+                    description = "AttrRepo successfully created", headers = {
+                @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+                        @Schema(type = "string"),
+                        description = "UUID generated for the entity created"),
+                @Header(name = HttpHeaders.LOCATION, schema =
+                        @Schema(type = "string"),
+                        description = "URL of the entity created") }))
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    Response create(@NotNull AttrRepoTO attrRepoTO);
+
+    /**
+     * Updates attribute repository matching the given key.
+     *
+     * @param attrRepoTO AttrRepo to replace existing attribute repository
+     */
+    @Parameter(name = "key", description = "AttrRepo's key", in = ParameterIn.PATH, schema =
+            @Schema(type = "string"))
+    @ApiResponses(
+            @ApiResponse(responseCode = "204", description = "Operation was successful"))
+    @PUT
+    @Path("{key}")
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void update(@NotNull AttrRepoTO attrRepoTO);
+
+    /**
+     * Delete attribute repository matching the given key.
+     *
+     * @param key key of attribute repository to be deleted
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "204", description = "Operation was successful"))
+    @DELETE
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void delete(@NotNull @PathParam("key") String key);
+}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/jackson/SyncopeXmlMapper.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/jackson/SyncopeXmlMapper.java
index 7183607651..48c26a4c56 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/jackson/SyncopeXmlMapper.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/jackson/SyncopeXmlMapper.java
@@ -51,6 +51,7 @@ public class SyncopeXmlMapper extends XmlMapper {
         findAndRegisterModules();
 
         configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false);
 
         configOverride(List.class).setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
         configOverride(Set.class).setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AMLogicContext.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AMLogicContext.java
index 91bcf3f81d..c3677119a2 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AMLogicContext.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AMLogicContext.java
@@ -27,18 +27,20 @@ import org.apache.syncope.core.logic.wa.U2FRegistrationLogic;
 import org.apache.syncope.core.logic.wa.WAClientAppLogic;
 import org.apache.syncope.core.logic.wa.WAConfigLogic;
 import org.apache.syncope.core.logic.wa.WebAuthnRegistrationLogic;
+import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
+import org.apache.syncope.core.persistence.api.dao.CASSPDAO;
+import org.apache.syncope.core.persistence.api.dao.OIDCJWKSDAO;
+import org.apache.syncope.core.persistence.api.dao.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO;
 import org.apache.syncope.core.persistence.api.dao.SRARouteDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.CASSPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCJWKSDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2IdPEntityDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPEntityDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtilsFactory;
+import org.apache.syncope.core.provisioning.api.data.AttrRepoDataBinder;
 import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
@@ -73,8 +75,19 @@ public class AMLogicContext {
 
     @ConditionalOnMissingBean
     @Bean
-    public AuthProfileLogic authProfileLogic(final AuthProfileDAO authProfileDAO,
-                                             final AuthProfileDataBinder authProfileDataBinder) {
+    public AttrRepoLogic attrRepoLogic(
+            final AttrRepoDataBinder binder,
+            final AttrRepoDAO attrRepoDAO) {
+
+        return new AttrRepoLogic(binder, attrRepoDAO);
+    }
+
+    @ConditionalOnMissingBean
+    @Bean
+    public AuthProfileLogic authProfileLogic(
+            final AuthProfileDAO authProfileDAO,
+            final AuthProfileDataBinder authProfileDataBinder) {
+
         return new AuthProfileLogic(authProfileDAO, authProfileDataBinder);
     }
 
@@ -140,42 +153,52 @@ public class AMLogicContext {
 
     @ConditionalOnMissingBean
     @Bean
-    public GoogleMfaAuthAccountLogic googleMfaAuthAccountLogic(final AuthProfileDAO authProfileDAO,
-                                                               final AuthProfileDataBinder authProfileDataBinder,
-                                                               final EntityFactory entityFactory) {
+    public GoogleMfaAuthAccountLogic googleMfaAuthAccountLogic(
+            final AuthProfileDAO authProfileDAO,
+            final AuthProfileDataBinder authProfileDataBinder,
+            final EntityFactory entityFactory) {
+
         return new GoogleMfaAuthAccountLogic(entityFactory, authProfileDAO, authProfileDataBinder);
     }
 
     @ConditionalOnMissingBean
     @Bean
-    public GoogleMfaAuthTokenLogic googleMfaAuthTokenLogic(final AuthProfileDAO authProfileDAO,
-                                                           final AuthProfileDataBinder authProfileDataBinder,
-                                                           final EntityFactory entityFactory) {
+    public GoogleMfaAuthTokenLogic googleMfaAuthTokenLogic(
+            final AuthProfileDAO authProfileDAO,
+            final AuthProfileDataBinder authProfileDataBinder,
+            final EntityFactory entityFactory) {
+
         return new GoogleMfaAuthTokenLogic(entityFactory, authProfileDAO, authProfileDataBinder);
     }
 
     @ConditionalOnMissingBean
     @Bean
-    public ImpersonationLogic impersonationLogic(final AuthProfileDAO authProfileDAO,
-                                                 final AuthProfileDataBinder authProfileDataBinder,
-                                                 final EntityFactory entityFactory) {
+    public ImpersonationLogic impersonationLogic(
+            final AuthProfileDAO authProfileDAO,
+            final AuthProfileDataBinder authProfileDataBinder,
+            final EntityFactory entityFactory) {
+
         return new ImpersonationLogic(entityFactory, authProfileDAO, authProfileDataBinder);
     }
 
     @ConditionalOnMissingBean
     @Bean
-    public U2FRegistrationLogic u2fRegistrationLogic(final AuthProfileDAO authProfileDAO,
-                                                     final AuthProfileDataBinder authProfileDataBinder,
-                                                     final EntityFactory entityFactory) {
+    public U2FRegistrationLogic u2fRegistrationLogic(
+            final AuthProfileDAO authProfileDAO,
+            final AuthProfileDataBinder authProfileDataBinder,
+            final EntityFactory entityFactory) {
+
         return new U2FRegistrationLogic(entityFactory, authProfileDAO, authProfileDataBinder);
     }
 
     @ConditionalOnMissingBean
     @Bean
-    public WAClientAppLogic waClientAppLogic(final WAClientAppDataBinder binder,
-                                             final SAML2SPDAO saml2spDAO,
-                                             final OIDCRPDAO oidcrpDAO,
-                                             final CASSPDAO casspDAO) {
+    public WAClientAppLogic waClientAppLogic(
+            final WAClientAppDataBinder binder,
+            final SAML2SPDAO saml2spDAO,
+            final OIDCRPDAO oidcrpDAO,
+            final CASSPDAO casspDAO) {
+
         return new WAClientAppLogic(binder, saml2spDAO, oidcrpDAO, casspDAO);
     }
 
@@ -192,9 +215,11 @@ public class AMLogicContext {
 
     @ConditionalOnMissingBean
     @Bean
-    public WebAuthnRegistrationLogic webAuthnRegistrationLogic(final AuthProfileDAO authProfileDAO,
-                                                               final AuthProfileDataBinder authProfileDataBinder,
-                                                               final EntityFactory entityFactory) {
+    public WebAuthnRegistrationLogic webAuthnRegistrationLogic(
+            final AuthProfileDAO authProfileDAO,
+            final AuthProfileDataBinder authProfileDataBinder,
+            final EntityFactory entityFactory) {
+
         return new WebAuthnRegistrationLogic(entityFactory, authProfileDAO, authProfileDataBinder);
     }
 }
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuthProfileLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuthProfileLogic.java
index c81ddc7c78..4118869bae 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuthProfileLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuthProfileLogic.java
@@ -21,7 +21,7 @@ package org.apache.syncope.core.logic;
 import java.lang.reflect.Method;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.syncope.common.lib.to.AuthProfileTO;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 
 public abstract class AbstractAuthProfileLogic extends AbstractTransactionalLogic<AuthProfileTO> {
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AttrRepoLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AttrRepoLogic.java
new file mode 100644
index 0000000000..530df926fc
--- /dev/null
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AttrRepoLogic.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
+import org.apache.syncope.core.provisioning.api.data.AttrRepoDataBinder;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.transaction.annotation.Transactional;
+
+public class AttrRepoLogic extends AbstractTransactionalLogic<AttrRepoTO> {
+
+    protected final AttrRepoDataBinder binder;
+
+    protected final AttrRepoDAO attrRepoDAO;
+
+    public AttrRepoLogic(final AttrRepoDataBinder binder, final AttrRepoDAO attrRepoDAO) {
+        this.binder = binder;
+        this.attrRepoDAO = attrRepoDAO;
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.ATTR_REPO_CREATE + "')")
+    public AttrRepoTO create(final AttrRepoTO attrRepoTO) {
+        return binder.getAttrRepoTO(attrRepoDAO.save(binder.create(attrRepoTO)));
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.ATTR_REPO_UPDATE + "')")
+    public AttrRepoTO update(final AttrRepoTO attrRepoTO) {
+        AttrRepo attrRepo = attrRepoDAO.find(attrRepoTO.getKey());
+        if (attrRepo == null) {
+            throw new NotFoundException("AttrRepo " + attrRepoTO.getKey() + " not found");
+        }
+
+        return binder.getAttrRepoTO(attrRepoDAO.save(binder.update(attrRepo, attrRepoTO)));
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.ATTR_REPO_LIST + "') or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
+    @Transactional(readOnly = true)
+    public List<AttrRepoTO> list() {
+        return attrRepoDAO.findAll().stream().map(binder::getAttrRepoTO).collect(Collectors.toList());
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.ATTR_REPO_READ + "')")
+    @Transactional(readOnly = true)
+    public AttrRepoTO read(final String key) {
+        AttrRepo attrRepo = attrRepoDAO.find(key);
+        if (attrRepo == null) {
+            throw new NotFoundException("AttrRepo " + key + " not found");
+        }
+
+        return binder.getAttrRepoTO(attrRepo);
+    }
+
+    @PreAuthorize("hasRole('" + AMEntitlement.ATTR_REPO_DELETE + "')")
+    public AttrRepoTO delete(final String key) {
+        AttrRepo attrRepo = attrRepoDAO.find(key);
+        if (attrRepo == null) {
+            throw new NotFoundException("AttrRepo " + key + " not found");
+        }
+
+        AttrRepoTO deleted = binder.getAttrRepoTO(attrRepo);
+        attrRepoDAO.delete(attrRepo);
+
+        return deleted;
+    }
+
+    @Override
+    protected AttrRepoTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof AttrRepoTO) {
+                    key = ((AttrRepoTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                return binder.getAttrRepoTO(attrRepoDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthModuleLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthModuleLogic.java
index 3bc5b6c2b6..f10251bbbd 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthModuleLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthModuleLogic.java
@@ -25,9 +25,9 @@ import org.apache.commons.lang3.ArrayUtils;
 import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
 import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
index e572c16ac5..31b1d17430 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/AuthProfileLogic.java
@@ -24,9 +24,9 @@ import java.util.stream.Collectors;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.to.AuthProfileTO;
 import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/ClientAppLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/ClientAppLogic.java
index e534877100..11cc286319 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/ClientAppLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/ClientAppLogic.java
@@ -41,16 +41,16 @@ import org.apache.syncope.common.lib.to.ClientAppTO;
 import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.types.ClientAppType;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.CASSPDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.CASSPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtils;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.dao.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtils;
+import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
 import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
 import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -264,15 +264,15 @@ public class ClientAppLogic extends AbstractTransactionalLogic<ClientAppTO> {
         try {
             NetworkService wa = serviceOps.get(NetworkService.Type.WA);
             String basicAuthHeader = DefaultBasicAuthSupplier.getBasicAuthHeader(
-                securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey());
+                    securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey());
             URI endpoint = URI.create(StringUtils.appendIfMissing(wa.getAddress(), "/")
-                                      + "actuator/registeredServices");
+                    + "actuator/registeredServices");
             HttpClient.newBuilder().build().send(
-                HttpRequest.newBuilder(endpoint).
-                    header(HttpHeaders.AUTHORIZATION, basicAuthHeader).
-                    header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).
-                    GET().build(),
-                HttpResponse.BodyHandlers.discarding());
+                    HttpRequest.newBuilder(endpoint).
+                            header(HttpHeaders.AUTHORIZATION, basicAuthHeader).
+                            header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON).
+                            GET().build(),
+                    HttpResponse.BodyHandlers.discarding());
         } catch (KeymasterException e) {
             throw new NotFoundException("Could not find any WA instance", e);
         } catch (IOException | InterruptedException e) {
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/OIDCJWKSLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/OIDCJWKSLogic.java
index e105e82111..59cb1b6b98 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/OIDCJWKSLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/OIDCJWKSLogic.java
@@ -26,8 +26,8 @@ import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.types.JWSAlgorithm;
 import org.apache.syncope.core.persistence.api.dao.DuplicateException;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCJWKSDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
+import org.apache.syncope.core.persistence.api.dao.OIDCJWKSDAO;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS;
 import org.apache.syncope.core.provisioning.api.data.OIDCJWKSDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPEntityLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPEntityLogic.java
index 312cc8e6ca..1c8bba3eac 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPEntityLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2IdPEntityLogic.java
@@ -27,8 +27,8 @@ import org.apache.syncope.common.lib.to.SAML2IdPEntityTO;
 import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2IdPEntityDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPEntity;
+import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity;
 import org.apache.syncope.core.provisioning.api.data.SAML2IdPEntityDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPEntityLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPEntityLogic.java
index 2b8b898810..3d3fcb0b3b 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPEntityLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPEntityLogic.java
@@ -27,8 +27,8 @@ import org.apache.syncope.common.lib.to.SAML2SPEntityTO;
 import org.apache.syncope.common.lib.types.AMEntitlement;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPEntityDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPEntity;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity;
 import org.apache.syncope.core.provisioning.api.data.SAML2SPEntityDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SRARouteLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SRARouteLogic.java
index 4c72164a56..70e05bf438 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/SRARouteLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/SRARouteLogic.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.logic;
 
-import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URI;
 import java.net.http.HttpClient;
@@ -26,7 +25,6 @@ import java.net.http.HttpRequest;
 import java.net.http.HttpResponse;
 import java.util.List;
 import java.util.stream.Collectors;
-import javax.ws.rs.InternalServerErrorException;
 import javax.ws.rs.core.HttpHeaders;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -118,19 +116,19 @@ public class SRARouteLogic extends AbstractTransactionalLogic<SRARouteTO> {
 
     @PreAuthorize("hasRole('" + AMEntitlement.SRA_ROUTE_PUSH + "')")
     public void pushToSRA() {
+        HttpClient client = HttpClient.newHttpClient();
         try {
-            NetworkService sra = serviceOps.get(NetworkService.Type.SRA);
-            HttpClient.newBuilder().build().send(
+            serviceOps.list(NetworkService.Type.SRA).forEach(sra -> client.sendAsync(
                     HttpRequest.newBuilder(URI.create(
                             StringUtils.appendIfMissing(sra.getAddress(), "/") + "actuator/gateway/refresh")).
                             header(HttpHeaders.AUTHORIZATION, DefaultBasicAuthSupplier.getBasicAuthHeader(
                                     securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey())).
                             POST(HttpRequest.BodyPublishers.noBody()).build(),
-                    HttpResponse.BodyHandlers.discarding());
+                    HttpResponse.BodyHandlers.discarding()).
+                    thenAcceptAsync(response -> LOG.info(
+                    "Pushed to SRA instance {} with HTTP status: {}", sra.getAddress(), response.statusCode())));
         } catch (KeymasterException e) {
-            throw new NotFoundException("Could not find any SRA instance", e);
-        } catch (IOException | InterruptedException e) {
-            throw new InternalServerErrorException("Errors while communicating with SRA instance", e);
+            throw new NotFoundException("Could not find any WA instance", e);
         }
     }
 
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java
index 958ff37660..e0de66a054 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogic.java
@@ -24,10 +24,10 @@ import java.util.stream.Collectors;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java
index 5469bd46d4..fffd51e9a3 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogic.java
@@ -25,10 +25,10 @@ import java.util.stream.Collectors;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java
index a9675b6216..7d2640609f 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/ImpersonationLogic.java
@@ -23,9 +23,9 @@ import java.util.List;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.ImpersonationAccount;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/U2FRegistrationLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/U2FRegistrationLogic.java
index cb4e376ced..c12d81b8ba 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/U2FRegistrationLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/U2FRegistrationLogic.java
@@ -29,10 +29,10 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.U2FDevice;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAClientAppLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAClientAppLogic.java
index 1c796b7637..08e838258c 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAClientAppLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAClientAppLogic.java
@@ -25,13 +25,13 @@ import java.util.stream.Stream;
 import org.apache.syncope.common.lib.types.ClientAppType;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.WAClientApp;
+import org.apache.syncope.core.persistence.api.dao.CASSPDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.CASSPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.dao.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
 import org.apache.syncope.core.provisioning.api.data.wa.WAClientAppDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAConfigLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAConfigLogic.java
index ab1b522c23..547880ad40 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAConfigLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WAConfigLogic.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.logic.wa;
 
-import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URI;
 import java.net.http.HttpClient;
@@ -27,7 +26,6 @@ import java.net.http.HttpResponse;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
-import javax.ws.rs.InternalServerErrorException;
 import javax.ws.rs.core.HttpHeaders;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier;
@@ -41,7 +39,7 @@ import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.core.logic.AbstractTransactionalLogic;
 import org.apache.syncope.core.logic.UnresolvedReferenceException;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
 import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder;
 import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -95,20 +93,19 @@ public class WAConfigLogic extends AbstractTransactionalLogic<EntityTO> {
 
     @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_PUSH + "')")
     public void pushToWA() {
+        HttpClient client = HttpClient.newHttpClient();
         try {
-            NetworkService wa = serviceOps.get(NetworkService.Type.WA);
-            HttpResponse response = HttpClient.newBuilder().build().send(
+            serviceOps.list(NetworkService.Type.WA).forEach(wa -> client.sendAsync(
                     HttpRequest.newBuilder(URI.create(
                             StringUtils.appendIfMissing(wa.getAddress(), "/") + "actuator/refresh")).
                             header(HttpHeaders.AUTHORIZATION, DefaultBasicAuthSupplier.getBasicAuthHeader(
                                     securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey())).
                             POST(HttpRequest.BodyPublishers.noBody()).build(),
-                    HttpResponse.BodyHandlers.discarding());
-            LOG.info("Pushed changes to WA with status: {}", response.statusCode());
+                    HttpResponse.BodyHandlers.discarding()).
+                    thenAcceptAsync(response -> LOG.info(
+                    "Pushed to WA instance {} with HTTP status: {}", wa.getAddress(), response.statusCode())));
         } catch (KeymasterException e) {
             throw new NotFoundException("Could not find any WA instance", e);
-        } catch (IOException | InterruptedException e) {
-            throw new InternalServerErrorException("Errors while communicating with WA instance", e);
         }
     }
 
diff --git a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java
index e9cb9c6fc3..d03b767350 100644
--- a/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java
+++ b/core/am/logic/src/main/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogic.java
@@ -24,10 +24,10 @@ import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.wa.WebAuthnAccount;
 import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
 import org.apache.syncope.core.logic.AbstractAuthProfileLogic;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/AMRESTCXFContext.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/AMRESTCXFContext.java
index c55da93269..06fd5faadf 100644
--- a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/AMRESTCXFContext.java
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/AMRESTCXFContext.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.rest.cxf;
 
+import org.apache.syncope.common.rest.api.service.AttrRepoService;
 import org.apache.syncope.common.rest.api.service.AuthModuleService;
 import org.apache.syncope.common.rest.api.service.AuthProfileService;
 import org.apache.syncope.common.rest.api.service.ClientAppService;
@@ -32,6 +33,7 @@ import org.apache.syncope.common.rest.api.service.wa.U2FRegistrationService;
 import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
 import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
 import org.apache.syncope.common.rest.api.service.wa.WebAuthnRegistrationService;
+import org.apache.syncope.core.logic.AttrRepoLogic;
 import org.apache.syncope.core.logic.AuthModuleLogic;
 import org.apache.syncope.core.logic.AuthProfileLogic;
 import org.apache.syncope.core.logic.ClientAppLogic;
@@ -46,6 +48,7 @@ import org.apache.syncope.core.logic.wa.U2FRegistrationLogic;
 import org.apache.syncope.core.logic.wa.WAClientAppLogic;
 import org.apache.syncope.core.logic.wa.WAConfigLogic;
 import org.apache.syncope.core.logic.wa.WebAuthnRegistrationLogic;
+import org.apache.syncope.core.rest.cxf.service.AttrRepoServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.AuthModuleServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.AuthProfileServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.ClientAppServiceImpl;
@@ -73,6 +76,12 @@ public class AMRESTCXFContext {
         return new AuthModuleServiceImpl(authModuleLogic);
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public AttrRepoService attrRepoService(final AttrRepoLogic attrRepoLogic) {
+        return new AttrRepoServiceImpl(attrRepoLogic);
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public AuthProfileService authProfileService(final AuthProfileLogic authProfileLogic) {
diff --git a/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AttrRepoServiceImpl.java b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AttrRepoServiceImpl.java
new file mode 100644
index 0000000000..7fee652d0f
--- /dev/null
+++ b/core/am/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AttrRepoServiceImpl.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import java.net.URI;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.AttrRepoService;
+import org.apache.syncope.core.logic.AttrRepoLogic;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AttrRepoServiceImpl extends AbstractService implements AttrRepoService {
+
+    protected final AttrRepoLogic logic;
+
+    public AttrRepoServiceImpl(final AttrRepoLogic logic) {
+        this.logic = logic;
+    }
+
+    @Override
+    public Response create(final AttrRepoTO attrRepoTO) {
+        AttrRepoTO attrRepo = logic.create(attrRepoTO);
+        URI location = uriInfo.getAbsolutePathBuilder().path(attrRepo.getKey()).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, attrRepo.getKey()).
+                build();
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+    @Override
+    public List<AttrRepoTO> list() {
+        return logic.list();
+    }
+
+    @Override
+    public AttrRepoTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public void update(final AttrRepoTO attrRepoTO) {
+        logic.update(attrRepoTO);
+    }
+}
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java
index 14686d614a..b47f7a1f6d 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java
@@ -188,11 +188,11 @@ public class SyncopeOpenApiCustomizer extends OpenApiCustomizer {
 
         Schema<ErrorTO> errorSchema = new Schema<>();
         errorSchema.example(sampleError).
-                addProperties("status", new IntegerSchema().description("HTTP status code")).
-                addProperties("type", new StringSchema().
+                addProperty("status", new IntegerSchema().description("HTTP status code")).
+                addProperty("type", new StringSchema().
                         _enum(Stream.of(ClientExceptionType.values()).map(Enum::name).collect(Collectors.toList())).
                         description("Error code")).
-                addProperties("elements", new ArraySchema().type("string").description("Error message(s)"));
+                addProperty("elements", new ArraySchema().type("string").description("Error message(s)"));
 
         Content content = new Content();
         content.addMediaType(
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AttrRepoDAO.java
similarity index 71%
copy from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java
copy to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AttrRepoDAO.java
index bb575fceb6..b352151fb7 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AttrRepoDAO.java
@@ -16,14 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
 
-public interface WAConfigEntry extends ProvidedKeyEntity {
+public interface AttrRepoDAO extends DAO<AttrRepo> {
 
-    List<String> getValues();
+    AttrRepo find(String key);
 
-    void setValues(List<String> value);
+    List<AttrRepo> findAll();
+
+    AttrRepo save(AttrRepo attrRepo);
+
+    void delete(String key);
+
+    void delete(AttrRepo attrRepo);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthModuleDAO.java
similarity index 85%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthModuleDAO.java
index b17f377313..d33796fbfb 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthModuleDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthModuleDAO.java
@@ -16,11 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
 
 public interface AuthModuleDAO extends DAO<AuthModule> {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthProfileDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
similarity index 86%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthProfileDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
index 7d2fe21a9e..f38f3381a3 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/AuthProfileDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuthProfileDAO.java
@@ -16,12 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
 import java.util.Optional;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 
 public interface AuthProfileDAO extends DAO<AuthProfile> {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/CASSPDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/CASSPDAO.java
similarity index 86%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/CASSPDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/CASSPDAO.java
index b6f6f27e85..2222701aa7 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/CASSPDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/CASSPDAO.java
@@ -16,11 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
 
 public interface CASSPDAO extends DAO<CASSPClientApp> {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCJWKSDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCJWKSDAO.java
similarity index 83%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCJWKSDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCJWKSDAO.java
index 98acd8aa88..1dc32a6989 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCJWKSDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCJWKSDAO.java
@@ -16,10 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS;
 
 public interface OIDCJWKSDAO extends DAO<OIDCJWKS> {
     OIDCJWKS get();
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCRPDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCRPDAO.java
similarity index 87%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCRPDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCRPDAO.java
index 6f4fe0ab26..6b6b42dddd 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/OIDCRPDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/OIDCRPDAO.java
@@ -16,11 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
 
 public interface OIDCRPDAO extends DAO<OIDCRPClientApp> {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2IdPEntityDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2IdPEntityDAO.java
similarity index 84%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2IdPEntityDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2IdPEntityDAO.java
index 5b29a8f30d..2f4b9823a9 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2IdPEntityDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2IdPEntityDAO.java
@@ -16,11 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity;
 
 public interface SAML2IdPEntityDAO extends DAO<SAML2IdPEntity> {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2SPDAO.java
similarity index 87%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2SPDAO.java
index cc3aa271b6..63ac6050f0 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2SPDAO.java
@@ -16,11 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
 
 public interface SAML2SPDAO extends DAO<SAML2SPClientApp> {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPEntityDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2SPEntityDAO.java
similarity index 84%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPEntityDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2SPEntityDAO.java
index 75f2652d4b..471b309042 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/SAML2SPEntityDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/SAML2SPEntityDAO.java
@@ -16,11 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity;
 
 public interface SAML2SPEntityDAO extends DAO<SAML2SPEntity> {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/WAConfigDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/WAConfigDAO.java
similarity index 84%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/WAConfigDAO.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/WAConfigDAO.java
index a586ebe01d..3b11eb8da2 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/auth/WAConfigDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/WAConfigDAO.java
@@ -16,11 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.dao.auth;
+package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.DAO;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry;
 
 public interface WAConfigDAO extends DAO<WAConfigEntry> {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AttrRepo.java
similarity index 66%
copy from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
copy to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AttrRepo.java
index f2c7bf93d5..fdab6475eb 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AttrRepo.java
@@ -16,23 +16,32 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import java.util.List;
-import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.common.lib.attr.AttrRepoConf;
+import org.apache.syncope.common.lib.types.AttrRepoState;
 import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity;
 
-public interface AuthModule extends ProvidedKeyEntity {
+public interface AttrRepo extends ProvidedKeyEntity {
 
     String getDescription();
 
     void setDescription(String description);
 
-    AuthModuleConf getConf();
+    AttrRepoState getState();
 
-    void setConf(AuthModuleConf description);
+    void setState(AttrRepoState state);
 
-    List<? extends AuthModuleItem> getItems();
+    int getOrder();
 
-    boolean add(AuthModuleItem item);
+    void setOrder(int order);
+
+    AttrRepoConf getConf();
+
+    void setConf(AttrRepoConf conf);
+
+    List<? extends AttrRepoItem> getItems();
+
+    boolean add(AttrRepoItem item);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AttrRepoItem.java
similarity index 82%
copy from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java
copy to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AttrRepoItem.java
index 2754b026eb..43213ef449 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AttrRepoItem.java
@@ -16,13 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import org.apache.syncope.core.persistence.api.entity.resource.Item;
 
-public interface AuthModuleItem extends Item {
+public interface AttrRepoItem extends Item {
 
-    AuthModule getAuthModule();
+    AttrRepo getAttrRepo();
 
-    void setAuthModule(AuthModule authModule);
+    void setAttrRepo(AttrRepo attrRepo);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthModule.java
similarity index 80%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthModule.java
index f2c7bf93d5..5edc23e488 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModule.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthModule.java
@@ -16,10 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import java.util.List;
 import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.common.lib.types.AuthModuleState;
 import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity;
 
 public interface AuthModule extends ProvidedKeyEntity {
@@ -28,9 +29,17 @@ public interface AuthModule extends ProvidedKeyEntity {
 
     void setDescription(String description);
 
+    AuthModuleState getState();
+
+    void setState(AuthModuleState state);
+
+    int getOrder();
+
+    void setOrder(int order);
+
     AuthModuleConf getConf();
 
-    void setConf(AuthModuleConf description);
+    void setConf(AuthModuleConf conf);
 
     List<? extends AuthModuleItem> getItems();
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthModuleItem.java
similarity index 94%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthModuleItem.java
index 2754b026eb..d7168abd25 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthModuleItem.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthModuleItem.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import org.apache.syncope.core.persistence.api.entity.resource.Item;
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthProfile.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthProfile.java
similarity index 96%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthProfile.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthProfile.java
index 51a5a61fe0..8abfefc15d 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/AuthProfile.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/AuthProfile.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import java.util.List;
 import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount;
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/CASSPClientApp.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/CASSPClientApp.java
similarity index 93%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/CASSPClientApp.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/CASSPClientApp.java
index f4d0e80b63..1156f67075 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/CASSPClientApp.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/CASSPClientApp.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 public interface CASSPClientApp extends ClientApp {
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientApp.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
similarity index 96%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientApp.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
index e313e38510..222c4ebf4b 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientApp.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientApp.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import java.util.List;
 import org.apache.syncope.common.lib.Attr;
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientAppUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtils.java
similarity index 94%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientAppUtils.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtils.java
index 7db5f1192c..dd77ca3a2a 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientAppUtils.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtils.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import org.apache.syncope.common.lib.types.ClientAppType;
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientAppUtilsFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactory.java
similarity index 95%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientAppUtilsFactory.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactory.java
index 3968b46b25..c5480d7e2e 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/ClientAppUtilsFactory.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactory.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import org.apache.syncope.common.lib.to.ClientAppTO;
 import org.apache.syncope.common.lib.types.ClientAppType;
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/OIDCJWKS.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCJWKS.java
similarity index 94%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/OIDCJWKS.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCJWKS.java
index fe617a0995..b111c971b1 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/OIDCJWKS.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCJWKS.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import org.apache.syncope.core.persistence.api.entity.Entity;
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/OIDCRPClientApp.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java
similarity index 96%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/OIDCRPClientApp.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java
index c3daaf42c8..df4f27e415 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/OIDCRPClientApp.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import java.util.Set;
 import org.apache.syncope.common.lib.types.OIDCGrantType;
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2IdPEntity.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2IdPEntity.java
similarity index 95%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2IdPEntity.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2IdPEntity.java
index 27c2747b70..1f7159c4f1 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2IdPEntity.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2IdPEntity.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity;
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPClientApp.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2SPClientApp.java
similarity index 97%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPClientApp.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2SPClientApp.java
index 5c7ee2121c..608b0c2762 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPClientApp.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2SPClientApp.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import java.util.List;
 import java.util.Set;
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPEntity.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2SPEntity.java
similarity index 94%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPEntity.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2SPEntity.java
index 4bc6bd45f8..469e68d24b 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/SAML2SPEntity.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/SAML2SPEntity.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity;
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/WAConfigEntry.java
similarity index 94%
rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java
rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/WAConfigEntry.java
index bb575fceb6..e94b71f80b 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/WAConfigEntry.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/WAConfigEntry.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.api.entity.auth;
+package org.apache.syncope.core.persistence.api.entity.am;
 
 import java.util.List;
 import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity;
diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
index 4e59e87a05..d508cad45e 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -61,29 +61,29 @@ under the License.
                      jsonConf='{"_class":"org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf","name":"AllowedAttrReleasePolicy","allowedAttributes":["cn","givenName","uid"]}'/>
   <AttrReleasePolicy id="219935c7-deb3-40b3-8a9a-683037e523a2" name="DenyAttrReleasePolicy"
                      jsonConf='{"_class":"org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf","name":"DenyAttrReleasePolicyConf"}'/>
-  
+      
   <!-- Authentication modules -->
-  <AuthModule id="DefaultLDAPAuthModule"
+  <AuthModule id="DefaultLDAPAuthModule" authModuleState="ACTIVE"
               description="LDAP auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.LDAPAuthModuleConf","userIdAttribute":"cn","bindDn": "uid=admin,ou=system", "bindCredential":"secret","ldapUrl":"ldap://localhost:1389","searchFilter":"cn={user}","baseDn":"ou=People,o=isp","subtreeSearch":true,"principalAttributeList":["sn","givenName","mail","cn"]}'/>
-  <AuthModule id="DefaultJDBCAuthModule"
+  <AuthModule id="DefaultJDBCAuthModule" authModuleState="ACTIVE"
               description="JDBC auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.JDBCAuthModuleConf","sql":"SELECT * FROM users_table WHERE name=?", "fieldPassword": "password"}'/>
-  <AuthModule id="DefaultGoogleMfaAuthModule"
+  <AuthModule id="DefaultGoogleMfaAuthModule" authModuleState="ACTIVE"
               description="Google Mfa auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf","codeDigits":6,"issuer":"SyncopeTest", "label":"SyncopeTest", "timeStepSize":30, "windowSize":3}'/>
-  <AuthModule id="DefaultSimpleMfaAuthModule"
+  <AuthModule id="DefaultSimpleMfaAuthModule" authModuleState="ACTIVE"
               description="Simple Mfa auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.SimpleMfaAuthModuleConf","tokenLength":6, "timeToKillInSeconds":30}'/>
-  <AuthModule id="DefaultDuoMfaAuthModule"
+  <AuthModule id="DefaultDuoMfaAuthModule" authModuleState="ACTIVE"
               description="Duo Mfa auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.DuoMfaAuthModuleConf","integrationKey":"DIOXVRZD2UMZ8XXMNFQ5","secretKey":"Q2IU2i8BFNd6VYflZT8Evl6lF7oPlj3PM15BmRU7", "applicationKey":"u1IHBaREMB7Cb5S4QMISAgHycpj8lPBkDGfWt23I", "apiHost":"theapi.duosecurity.com"}'/>
-  <AuthModule id="DefaultOIDCAuthModule"
+  <AuthModule id="DefaultOIDCAuthModule" authModuleState="ACTIVE"
               description="OIDC auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.OIDCAuthModuleConf","discoveryUri":"https://localhost:9443/syncope-wa/oidc/.well-known/openid-configuration", "id":"client-id", "secret": "client-secret" }'/>
-  <AuthModule id="DefaultSAML2IdPAuthModule"
+  <AuthModule id="DefaultSAML2IdPAuthModule" authModuleState="ACTIVE"
               description="SAML2 IdP auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf","keystorePassword":"p@$$word","privateKeyPassword":"p@$$word","identityProviderMetadataPath":"https://localhost:9443/syncope-wa/idp/metadata", "serviceProviderEntityId":"syncope:apache:org"}'/>
-  <AuthModule id="DefaultJaasAuthModule"
+  <AuthModule id="DefaultJaasAuthModule" authModuleState="ACTIVE"
               description="Jaas auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.JaasAuthModuleConf","realm":"SYNCOPE","kerberosRealmSystemProperty":"sample-value", "loginConfigType": "JavaLoginConfig", "loginConfigurationFile": "file:/etc/jaas/login.conf"}'/>
-  <AuthModule id="DefaultStaticAuthModule"
+  <AuthModule id="DefaultStaticAuthModule" authModuleState="ACTIVE"
               description="Static auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.StaticAuthModuleConf","users":{"syncope1": "$cynop3"}}'/>
-  <AuthModule id="DefaultSyncopeAuthModule"
+  <AuthModule id="DefaultSyncopeAuthModule" authModuleState="ACTIVE"
               description="Syncope auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf","domain":"Master"}'/>
-  <AuthModule id="DefaultU2FAuthModule"
+  <AuthModule id="DefaultU2FAuthModule" authModuleState="ACTIVE"
               description="U2F auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.U2FAuthModuleConf","expireDevices":40}'/>
 
   <AuthModuleItem id="26678936-af09-48b8-a789-36af0918b87d" extAttrName="family_name" intAttrName="syncopeUserAttr_surname"
@@ -106,6 +106,18 @@ under the License.
   <AuthModuleItem id="a1933059-e64f-409f-a86c-5b54da21787e" extAttrName="cn" intAttrName="cn"
                   password="0" purpose="NONE" mandatoryCondition="false" connObjectKey="0" authModule_id="DefaultLDAPAuthModule"/>
 
+  <!-- Attribute repositories -->
+  <AttrRepo id="DefaultLDAPAttrRepo" attrRepoState="ACTIVE"
+            description="LDAP attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.LDAPAttrRepoConf","searchFilter":"cn={user}","subtreeSearch":true,"ldapUrl":"ldap://localhost:1389","bindDn":"uid=admin,ou=system","bindCredential":"secret","baseDn":"ou=People,o=isp","attributes":{},"useAllQueryAttributes":true,"queryAttributes":{}}'/>
+  <AttrRepo id="DefaultJDBCAttrRepo" attrRepoState="ACTIVE"
+            description="JDBC attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.JDBCAttrRepoConf","sql":"SELECT * FROM table WHERE name=?","dialect":"org.hibernate.dialect.H2Dialect","driverClass":"org.h2.Driver","url":"jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1","user":"username","password":"password","singleRow":true,"requireAllAttributes":true,"caseCanonicalization":"NONE","queryType":"AND","columnMappings":{},"username":[],"attributes":{},"caseInsensitiveQueryAttributes [...]
+  <AttrRepo id="DefaultStubAttrRepo" attrRepoState="ACTIVE"
+            description="Stub attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.StubAttrRepoConf","attributes":{"attr1":"value1"}}'/>
+  <AttrRepo id="DefaultSyncopeAttrRepo" attrRepoState="ACTIVE"
+            description="Syncope attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.SyncopeAttrRepoConf","domain":"Master","searchFilter":"username=={user}","basicAuthUsername":"admin","basicAuthPassword":"password","headers":{}}'/>
+
+  <AttrRepoItem id="d2d9e7be-d82b-4698-b0fb-67d480413049" extAttrName="identifier" intAttrName="attr1"
+                password="0" purpose="NONE" mandatoryCondition="false" connObjectKey="0" attrRepo_id="DefaultStubAttrRepo"/>
   <RelationshipType id="inclusion" description="Models the act that an object is included in another"/>
   <RelationshipType id="neighborhood" description="Models the act that an object is near another"/>
   
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
index 1d61fde03c..07960734f0 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
@@ -33,8 +33,12 @@ import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.ApplicationDAO;
+import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO;
 import org.apache.syncope.core.persistence.api.dao.AuditConfDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
 import org.apache.syncope.core.persistence.api.dao.BatchDAO;
+import org.apache.syncope.core.persistence.api.dao.CASSPDAO;
 import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
 import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
@@ -45,6 +49,8 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
+import org.apache.syncope.core.persistence.api.dao.OIDCJWKSDAO;
+import org.apache.syncope.core.persistence.api.dao.OIDCRPDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
@@ -56,24 +62,19 @@ import org.apache.syncope.core.persistence.api.dao.ReportDAO;
 import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
 import org.apache.syncope.core.persistence.api.dao.ReportTemplateDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO;
 import org.apache.syncope.core.persistence.api.dao.SRARouteDAO;
 import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.CASSPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCJWKSDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2IdPEntityDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPEntityDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
 import org.apache.syncope.core.persistence.api.search.SearchCondVisitor;
@@ -87,8 +88,12 @@ import org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAAnyTypeClassDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAAnyTypeDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAApplicationDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAAttrRepoDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAAuditConfDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAAuthModuleDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAAuthProfileDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPABatchDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPACASSPDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAConnInstanceDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPADelegationDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPADerSchemaDAO;
@@ -99,6 +104,8 @@ import org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAImplementationDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAMailTemplateDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPANotificationDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAOIDCJWKSDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAOIDCRPDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAPlainSchemaDAO;
@@ -110,24 +117,19 @@ import org.apache.syncope.core.persistence.jpa.dao.JPAReportDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAReportExecDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAReportTemplateDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPARoleDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPASAML2IdPEntityDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPASAML2SPDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPASAML2SPEntityDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPASRARouteDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPASecurityQuestionDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPATaskDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPATaskExecDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAVirSchemaDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPAAuthModuleDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPAAuthProfileDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPACASSPDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPAOIDCJWKSDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPAOIDCRPDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPASAML2IdPEntityDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPASAML2SPDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPASAML2SPEntityDAO;
-import org.apache.syncope.core.persistence.jpa.dao.auth.JPAWAConfigDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAWAConfigDAO;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAClientAppUtilsFactory;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAClientAppUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPolicyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPATaskUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.spring.CommonEntityManagerFactoryConf;
@@ -395,6 +397,12 @@ public class PersistenceContext {
         return new JPAAuthModuleDAO(policyDAO);
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public AttrRepoDAO attrRepoDAO() {
+        return new JPAAttrRepoDAO();
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public AuthProfileDAO authProfileDAO() {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAttrRepoDAO.java
similarity index 54%
copy from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java
copy to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAttrRepoDAO.java
index 58083f5adb..099497732b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAttrRepoDAO.java
@@ -16,43 +16,48 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAWAConfigEntry;
+import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAttrRepo;
 import org.springframework.transaction.annotation.Transactional;
 
-public class JPAWAConfigDAO extends AbstractDAO<WAConfigEntry> implements WAConfigDAO {
+public class JPAAttrRepoDAO extends AbstractDAO<AttrRepo> implements AttrRepoDAO {
 
     @Transactional(readOnly = true)
     @Override
-    public WAConfigEntry find(final String key) {
-        return entityManager().find(JPAWAConfigEntry.class, key);
+    public AttrRepo find(final String key) {
+        return entityManager().find(JPAAttrRepo.class, key);
     }
 
     @Transactional(readOnly = true)
     @Override
-    public List<WAConfigEntry> findAll() {
-        TypedQuery<WAConfigEntry> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAWAConfigEntry.class.getSimpleName() + " e", WAConfigEntry.class);
+    public List<AttrRepo> findAll() {
+        TypedQuery<AttrRepo> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAAttrRepo.class.getSimpleName() + " e", AttrRepo.class);
         return query.getResultList();
     }
 
     @Override
-    public WAConfigEntry save(final WAConfigEntry configEntry) {
-        return entityManager().merge(configEntry);
+    public AttrRepo save(final AttrRepo attrRepo) {
+        return entityManager().merge(attrRepo);
     }
 
     @Override
     public void delete(final String key) {
-        WAConfigEntry entry = find(key);
-        if (entry == null) {
+        AttrRepo attrRepo = find(key);
+        if (attrRepo == null) {
             return;
         }
-        entityManager().remove(entry);
+
+        delete(attrRepo);
+    }
+
+    @Override
+    public void delete(final AttrRepo attrRepo) {
+        entityManager().remove(attrRepo);
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthModuleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuthModuleDAO.java
similarity index 89%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthModuleDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuthModuleDAO.java
index 0d5d4aae04..df8e88a6f9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthModuleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuthModuleDAO.java
@@ -16,17 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.TypedQuery;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
+import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthModule;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAuthModule;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPAAuthModuleDAO extends AbstractDAO<AuthModule> implements AuthModuleDAO {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthProfileDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuthProfileDAO.java
similarity index 89%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthProfileDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuthProfileDAO.java
index 8077fd1d58..acd862defd 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAAuthProfileDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuthProfileDAO.java
@@ -16,16 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import java.util.Optional;
 import javax.persistence.Query;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthProfile;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAuthProfile;
 
 public class JPAAuthProfileDAO extends AbstractDAO<AuthProfile> implements AuthProfileDAO {
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPACASSPDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPACASSPDAO.java
similarity index 89%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPACASSPDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPACASSPDAO.java
index 46de1661c3..90d632f7b6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPACASSPDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPACASSPDAO.java
@@ -16,15 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.CASSPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPACASSPClientApp;
+import org.apache.syncope.core.persistence.api.dao.CASSPDAO;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPACASSPClientApp;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPACASSPDAO extends AbstractDAO<CASSPClientApp> implements CASSPDAO {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAOIDCJWKSDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCJWKSDAO.java
similarity index 84%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAOIDCJWKSDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCJWKSDAO.java
index 3c41b406a4..3e1cd75ee5 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAOIDCJWKSDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCJWKSDAO.java
@@ -16,14 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCJWKSDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAOIDCJWKS;
+import org.apache.syncope.core.persistence.api.dao.OIDCJWKSDAO;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAOIDCJWKS;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPAOIDCJWKSDAO extends AbstractDAO<OIDCJWKS> implements OIDCJWKSDAO {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAOIDCRPDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCRPDAO.java
similarity index 90%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAOIDCRPDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCRPDAO.java
index 1fe5c5f47d..91f506b47c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAOIDCRPDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAOIDCRPDAO.java
@@ -16,15 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAOIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.dao.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAOIDCRPClientApp;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPAOIDCRPDAO extends AbstractDAO<OIDCRPClientApp> implements OIDCRPDAO {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2IdPEntityDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2IdPEntityDAO.java
similarity index 82%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2IdPEntityDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2IdPEntityDAO.java
index cb416fa5bf..27a7d1ff64 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2IdPEntityDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2IdPEntityDAO.java
@@ -16,14 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2IdPEntityDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPEntity;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2IdPEntity;
+import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPASAML2IdPEntity;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPASAML2IdPEntityDAO extends AbstractDAO<SAML2IdPEntity> implements SAML2IdPEntityDAO {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPDAO.java
similarity index 90%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPDAO.java
index 42732f154e..c79ca17523 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPDAO.java
@@ -16,15 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPASAML2SPClientApp;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPASAML2SPDAO extends AbstractDAO<SAML2SPClientApp> implements SAML2SPDAO {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPEntityDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPEntityDAO.java
similarity index 83%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPEntityDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPEntityDAO.java
index 1d530041a5..a1680029ca 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPASAML2SPEntityDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASAML2SPEntityDAO.java
@@ -16,14 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPEntityDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPEntity;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SPEntity;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPASAML2SPEntity;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPASAML2SPEntityDAO extends AbstractDAO<SAML2SPEntity> implements SAML2SPEntityDAO {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAWAConfigDAO.java
similarity index 84%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAWAConfigDAO.java
index 58083f5adb..f966af2cd1 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAWAConfigDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAWAConfigDAO.java
@@ -16,14 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.dao.auth;
+package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.TypedQuery;
-import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAWAConfigEntry;
+import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAWAConfigEntry;
 import org.springframework.transaction.annotation.Transactional;
 
 public class JPAWAConfigDAO extends AbstractDAO<WAConfigEntry> implements WAConfigDAO {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 7b04ca67de..374602ede8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -50,6 +50,18 @@ import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.SRARoute;
 import org.apache.syncope.core.persistence.api.entity.SchemaLabel;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepoItem;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModuleItem;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry;
 import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
 import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr;
@@ -57,16 +69,6 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrUnique
 import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPEntity;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPEntity;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue;
@@ -110,6 +112,18 @@ import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.user.URelationship;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAttrRepo;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAttrRepoItem;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAuthModule;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAuthModuleItem;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAuthProfile;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPACASSPClientApp;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAOIDCJWKS;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAOIDCRPClientApp;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPASAML2IdPEntity;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPASAML2SPClientApp;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPASAML2SPEntity;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAWAConfigEntry;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttr;
@@ -117,16 +131,6 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrUni
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrValue;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship;
 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthModule;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthModuleItem;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthProfile;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPACASSPClientApp;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAOIDCJWKS;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAOIDCRPClientApp;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2IdPEntity;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SPClientApp;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPASAML2SPEntity;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAWAConfigEntry;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttr;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrValue;
@@ -330,6 +334,10 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPAAuthModule();
         } else if (reference.equals(AuthModuleItem.class)) {
             result = (E) new JPAAuthModuleItem();
+        } else if (reference.equals(AttrRepo.class)) {
+            result = (E) new JPAAttrRepo();
+        } else if (reference.equals(AttrRepoItem.class)) {
+            result = (E) new JPAAttrRepoItem();
         } else if (reference.equals(AuthPolicy.class)) {
             result = (E) new JPAAuthPolicy();
         } else if (reference.equals(AccessPolicy.class)) {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/AbstractClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
similarity index 97%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/AbstractClientApp.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
index 7d094840d3..60301830ab 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/AbstractClientApp.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/AbstractClientApp.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.ArrayList;
@@ -28,7 +28,7 @@ import javax.persistence.ManyToOne;
 import javax.persistence.MappedSuperclass;
 import org.apache.syncope.common.lib.Attr;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
@@ -160,7 +160,7 @@ public class AbstractClientApp extends AbstractGeneratedKeyEntity implements Cli
         return properties == null
                 ? new ArrayList<>(0)
                 : POJOHelper.deserialize(properties, new TypeReference<>() {
-        });
+                });
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepo.java
similarity index 50%
copy from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
copy to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepo.java
index f510721899..23d8502cb7 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepo.java
@@ -16,35 +16,47 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import javax.persistence.CascadeType;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.Lob;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.auth.AuthModuleConf;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
+import org.apache.syncope.common.lib.attr.AttrRepoConf;
+import org.apache.syncope.common.lib.types.AttrRepoState;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepoItem;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
 @Entity
-@Table(name = JPAAuthModule.TABLE)
-public class JPAAuthModule extends AbstractProvidedKeyEntity implements AuthModule {
+@Table(name = JPAAttrRepo.TABLE)
+public class JPAAttrRepo extends AbstractProvidedKeyEntity implements AttrRepo {
 
-    public static final String TABLE = "AuthModule";
+    public static final String TABLE = "AttrRepo";
 
-    private static final long serialVersionUID = 5681033638234853077L;
+    private static final long serialVersionUID = 7337970107878689617L;
 
     private String description;
 
-    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "authModule")
-    private List<JPAAuthModuleItem> items = new ArrayList<>();
+    @Enumerated(EnumType.STRING)
+    @NotNull
+    private AttrRepoState attrRepoState;
+
+    @NotNull
+    private Integer attrRepoOrder = 0;
+
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "attrRepo")
+    private List<JPAAttrRepoItem> items = new ArrayList<>();
 
     @Lob
     private String jsonConf;
@@ -60,28 +72,48 @@ public class JPAAuthModule extends AbstractProvidedKeyEntity implements AuthModu
     }
 
     @Override
-    public List<? extends AuthModuleItem> getItems() {
+    public AttrRepoState getState() {
+        return attrRepoState;
+    }
+
+    @Override
+    public void setState(final AttrRepoState state) {
+        this.attrRepoState = state;
+    }
+
+    @Override
+    public int getOrder() {
+        return Optional.ofNullable(attrRepoOrder).orElse(0);
+    }
+
+    @Override
+    public void setOrder(final int order) {
+        this.attrRepoOrder = order;
+    }
+
+    @Override
+    public List<? extends AttrRepoItem> getItems() {
         return items;
     }
 
     @Override
-    public boolean add(final AuthModuleItem item) {
-        checkType(item, JPAAuthModuleItem.class);
-        return items.contains((JPAAuthModuleItem) item) || items.add((JPAAuthModuleItem) item);
+    public boolean add(final AttrRepoItem item) {
+        checkType(item, JPAAttrRepoItem.class);
+        return items.contains((JPAAttrRepoItem) item) || items.add((JPAAttrRepoItem) item);
     }
 
     @Override
-    public AuthModuleConf getConf() {
-        AuthModuleConf conf = null;
+    public AttrRepoConf getConf() {
+        AttrRepoConf conf = null;
         if (!StringUtils.isBlank(jsonConf)) {
-            conf = POJOHelper.deserialize(jsonConf, AuthModuleConf.class);
+            conf = POJOHelper.deserialize(jsonConf, AttrRepoConf.class);
         }
 
         return conf;
     }
 
     @Override
-    public void setConf(final AuthModuleConf conf) {
+    public void setConf(final AttrRepoConf conf) {
         jsonConf = POJOHelper.serialize(conf);
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepoItem.java
similarity index 80%
copy from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java
copy to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepoItem.java
index c851289422..c6d9afa2eb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAttrRepoItem.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -32,22 +32,22 @@ import javax.persistence.UniqueConstraint;
 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepoItem;
 import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.persistence.jpa.entity.resource.AbstractItem;
 
 @Entity
-@Table(name = JPAAuthModuleItem.TABLE)
+@Table(name = JPAAttrRepoItem.TABLE)
 @Cacheable
-public class JPAAuthModuleItem extends AbstractItem implements AuthModuleItem {
+public class JPAAttrRepoItem extends AbstractItem implements AttrRepoItem {
 
-    public static final String TABLE = "AuthModuleItem";
+    public static final String TABLE = "AttrRepoItem";
 
     private static final long serialVersionUID = 3165440920144995781L;
 
     @ManyToOne
-    private JPAAuthModule authModule;
+    private JPAAttrRepo attrRepo;
 
     @ManyToMany(fetch = FetchType.EAGER)
     @JoinTable(name = TABLE + "Transformer",
@@ -59,7 +59,7 @@ public class JPAAuthModuleItem extends AbstractItem implements AuthModuleItem {
             @UniqueConstraint(columnNames = { "item_id", "implementation_id" }))
     private final List<JPAImplementation> transformers = new ArrayList<>();
 
-    public JPAAuthModuleItem() {
+    public JPAAttrRepoItem() {
         super.setPurpose(MappingPurpose.NONE);
     }
 
@@ -69,14 +69,14 @@ public class JPAAuthModuleItem extends AbstractItem implements AuthModuleItem {
     }
 
     @Override
-    public AuthModule getAuthModule() {
-        return authModule;
+    public AttrRepo getAttrRepo() {
+        return attrRepo;
     }
 
     @Override
-    public void setAuthModule(final AuthModule authModule) {
-        checkType(authModule, JPAAuthModule.class);
-        this.authModule = (JPAAuthModule) authModule;
+    public void setAttrRepo(final AttrRepo attrRepo) {
+        checkType(attrRepo, JPAAttrRepo.class);
+        this.attrRepo = (JPAAttrRepo) attrRepo;
     }
 
     @Override
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java
similarity index 73%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java
index f510721899..69f19ac1ca 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java
@@ -16,20 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 import javax.persistence.CascadeType;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.Lob;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.auth.AuthModuleConf;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
+import org.apache.syncope.common.lib.types.AuthModuleState;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModuleItem;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
@@ -43,6 +48,13 @@ public class JPAAuthModule extends AbstractProvidedKeyEntity implements AuthModu
 
     private String description;
 
+    @Enumerated(EnumType.STRING)
+    @NotNull
+    private AuthModuleState authModuleState;
+
+    @NotNull
+    private Integer authModuleOrder = 0;
+
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "authModule")
     private List<JPAAuthModuleItem> items = new ArrayList<>();
 
@@ -59,6 +71,26 @@ public class JPAAuthModule extends AbstractProvidedKeyEntity implements AuthModu
         this.description = description;
     }
 
+    @Override
+    public AuthModuleState getState() {
+        return authModuleState;
+    }
+
+    @Override
+    public void setState(final AuthModuleState state) {
+        this.authModuleState = state;
+    }
+
+    @Override
+    public int getOrder() {
+        return Optional.ofNullable(authModuleOrder).orElse(0);
+    }
+
+    @Override
+    public void setOrder(final int order) {
+        this.authModuleOrder = order;
+    }
+
     @Override
     public List<? extends AuthModuleItem> getItems() {
         return items;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModuleItem.java
similarity index 94%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModuleItem.java
index c851289422..c2c6ba76d7 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthModuleItem.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModuleItem.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -32,8 +32,8 @@ import javax.persistence.UniqueConstraint;
 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModuleItem;
 import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.persistence.jpa.entity.resource.AbstractItem;
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthProfile.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthProfile.java
similarity index 97%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthProfile.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthProfile.java
index e1e4d030ed..7ac8baa57a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAAuthProfile.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthProfile.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.ArrayList;
@@ -32,7 +32,7 @@ import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.common.lib.wa.ImpersonationAccount;
 import org.apache.syncope.common.lib.wa.U2FDevice;
 import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPACASSPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPACASSPClientApp.java
similarity index 91%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPACASSPClientApp.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPACASSPClientApp.java
index 53fdd8dbe8..4fb1b55174 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPACASSPClientApp.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPACASSPClientApp.java
@@ -16,12 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Table;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
 
 @Entity
 @Table(name = JPACASSPClientApp.TABLE)
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAClientAppUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtils.java
similarity index 77%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAClientAppUtils.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtils.java
index ceacbb72df..f1f453f2e0 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAClientAppUtils.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtils.java
@@ -16,14 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import org.apache.syncope.common.lib.types.ClientAppType;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtils;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtils;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
 
 public class JPAClientAppUtils implements ClientAppUtils {
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAClientAppUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtilsFactory.java
similarity index 83%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAClientAppUtilsFactory.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtilsFactory.java
index 7fd135e21d..ccf5d1a785 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAClientAppUtilsFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtilsFactory.java
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import org.apache.syncope.common.lib.to.CASSPClientAppTO;
 import org.apache.syncope.common.lib.to.ClientAppTO;
 import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
 import org.apache.syncope.common.lib.to.SAML2SPClientAppTO;
 import org.apache.syncope.common.lib.types.ClientAppType;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtils;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientAppUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtils;
+import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
 
 public class JPAClientAppUtilsFactory implements ClientAppUtilsFactory {
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAOIDCJWKS.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCJWKS.java
similarity index 92%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAOIDCJWKS.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCJWKS.java
index ab0e5124c2..4745a30568 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAOIDCJWKS.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCJWKS.java
@@ -16,13 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Lob;
 import javax.persistence.Table;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
 
 @Entity
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAOIDCRPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java
similarity index 97%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAOIDCRPClientApp.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java
index 09dafc0ff0..8578df7223 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAOIDCRPClientApp.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -32,7 +32,7 @@ import javax.persistence.Table;
 import org.apache.syncope.common.lib.types.OIDCGrantType;
 import org.apache.syncope.common.lib.types.OIDCResponseType;
 import org.apache.syncope.common.lib.types.OIDCSubjectType;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
 
 @Entity
 @Table(name = JPAOIDCRPClientApp.TABLE)
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2IdPEntity.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2IdPEntity.java
similarity index 95%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2IdPEntity.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2IdPEntity.java
index bdd185286d..efa807297d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2IdPEntity.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2IdPEntity.java
@@ -16,14 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Lob;
 import javax.persistence.Table;
 import org.apache.commons.lang3.ArrayUtils;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
 
 @Entity
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java
similarity index 98%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPClientApp.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java
index b066c5d581..65b9ad1c3b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPClientApp.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPClientApp.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -31,7 +31,7 @@ import javax.persistence.JoinColumn;
 import javax.persistence.Table;
 import org.apache.syncope.common.lib.types.SAML2SPNameId;
 import org.apache.syncope.common.lib.types.XmlSecAlgorithm;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
 
 @Entity
 @Table(name = JPASAML2SPClientApp.TABLE)
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPEntity.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPEntity.java
similarity index 93%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPEntity.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPEntity.java
index df04354b29..85ffa9620c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPASAML2SPEntity.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPASAML2SPEntity.java
@@ -16,14 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Lob;
 import javax.persistence.Table;
 import org.apache.commons.lang3.ArrayUtils;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
 
 @Entity
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAWAConfigEntry.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java
similarity index 93%
rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAWAConfigEntry.java
rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java
index 30fc9cc935..4b5c4cd6fc 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAWAConfigEntry.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java
@@ -16,14 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.persistence.jpa.entity.auth;
+package org.apache.syncope.core.persistence.jpa.entity.am;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 import java.util.List;
 import javax.persistence.Entity;
 import javax.persistence.Lob;
 import javax.persistence.Table;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AttrRepoTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AttrRepoTest.java
new file mode 100644
index 0000000000..195dd67927
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AttrRepoTest.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.inner;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import java.util.UUID;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.syncope.common.lib.attr.AttrRepoConf;
+import org.apache.syncope.common.lib.attr.JDBCAttrRepoConf;
+import org.apache.syncope.common.lib.attr.LDAPAttrRepoConf;
+import org.apache.syncope.common.lib.attr.StubAttrRepoConf;
+import org.apache.syncope.common.lib.attr.SyncopeAttrRepoConf;
+import org.apache.syncope.common.lib.types.AttrRepoState;
+import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepoItem;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class AttrRepoTest extends AbstractTest {
+
+    @Autowired
+    private AttrRepoDAO attrRepoDAO;
+
+    @Test
+    public void findAll() {
+        List<AttrRepo> modules = attrRepoDAO.findAll();
+        assertNotNull(modules);
+        assertFalse(modules.isEmpty());
+        assertTrue(modules.size() >= 4);
+    }
+
+    @Test
+    public void find() {
+        AttrRepo attrRepo = attrRepoDAO.find("DefaultLDAPAttrRepo");
+        assertNotNull(attrRepo);
+        assertTrue(attrRepo.getConf() instanceof LDAPAttrRepoConf);
+
+        attrRepo = attrRepoDAO.find("DefaultJDBCAttrRepo");
+        assertNotNull(attrRepo);
+        assertTrue(attrRepo.getConf() instanceof JDBCAttrRepoConf);
+
+        attrRepo = attrRepoDAO.find("DefaultStubAttrRepo");
+        assertNotNull(attrRepo);
+        assertTrue(attrRepo.getConf() instanceof StubAttrRepoConf);
+        assertEquals(1, attrRepo.getItems().size());
+
+        attrRepo = attrRepoDAO.find("DefaultSyncopeAttrRepo");
+        assertNotNull(attrRepo);
+        assertTrue(attrRepo.getConf() instanceof SyncopeAttrRepoConf);
+    }
+
+    @Test
+    public void findByType() {
+        List<AttrRepo> attrRepos = attrRepoDAO.findAll();
+        assertTrue(attrRepos.stream().anyMatch(
+                attrRepo -> isSpecificConf(attrRepo.getConf(), LDAPAttrRepoConf.class)
+                && attrRepo.getKey().equals("DefaultLDAPAttrRepo")));
+        assertTrue(attrRepos.stream().anyMatch(
+                attrRepo -> isSpecificConf(attrRepo.getConf(), JDBCAttrRepoConf.class)
+                && attrRepo.getKey().equals("DefaultJDBCAttrRepo")));
+        assertTrue(attrRepos.stream().anyMatch(
+                attrRepo -> isSpecificConf(attrRepo.getConf(), SyncopeAttrRepoConf.class)
+                && attrRepo.getKey().equals("DefaultSyncopeAttrRepo")));
+        assertTrue(attrRepos.stream().anyMatch(
+                attrRepo -> isSpecificConf(attrRepo.getConf(), StubAttrRepoConf.class)
+                && attrRepo.getKey().equals("DefaultStubAttrRepo")));
+    }
+
+    @Test
+    public void saveWithStubRepo() {
+        StubAttrRepoConf conf = new StubAttrRepoConf();
+        conf.getAttributes().put("attr1", UUID.randomUUID().toString());
+        conf.getAttributes().put("attr2", UUID.randomUUID().toString());
+
+        saveAttrRepo("StaticAttrRepoTest", conf);
+    }
+
+    @Test
+    public void saveWithLdapRepo() {
+        LDAPAttrRepoConf conf = new LDAPAttrRepoConf();
+        conf.setBaseDn("dc=example,dc=org");
+        conf.setSearchFilter("cn={user}");
+        conf.setSubtreeSearch(true);
+        conf.setLdapUrl("ldap://localhost:1389");
+        conf.setBindCredential("Password");
+
+        saveAttrRepo("LDAPAttrRepoTest", conf);
+    }
+
+    @Test
+    public void saveWithJDBCRepo() {
+        JDBCAttrRepoConf conf = new JDBCAttrRepoConf();
+        conf.setSql("SELECT * FROM table WHERE name=?");
+        conf.setUrl("jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1");
+        conf.setUser("username");
+        conf.setPassword("password");
+
+        saveAttrRepo("JDBCAttrRepoTest", conf);
+    }
+
+    @Test
+    public void saveWithSyncopeRepo() {
+        SyncopeAttrRepoConf conf = new SyncopeAttrRepoConf();
+        conf.setDomain("Master");
+
+        saveAttrRepo("SyncopeAttrRepoTest", conf);
+    }
+
+    @Test
+    public void updateWithLDAPRepo() {
+        AttrRepo module = attrRepoDAO.find("DefaultLDAPAttrRepo");
+        assertNotNull(module);
+        AttrRepoConf conf = module.getConf();
+        LDAPAttrRepoConf.class.cast(conf).setBaseDn("dc=example2,dc=org");
+        LDAPAttrRepoConf.class.cast(conf).setSearchFilter("cn={user2}");
+        module.setConf(conf);
+
+        module = attrRepoDAO.save(module);
+        assertNotNull(module);
+        assertNotNull(module.getKey());
+
+        AttrRepo found = attrRepoDAO.find(module.getKey());
+        assertNotNull(found);
+        assertEquals("dc=example2,dc=org", LDAPAttrRepoConf.class.cast(found.getConf()).getBaseDn());
+        assertEquals("cn={user2}", LDAPAttrRepoConf.class.cast(found.getConf()).getSearchFilter());
+    }
+
+    @Test
+    public void updateWithJDBCRepo() {
+        AttrRepo module = attrRepoDAO.find("DefaultJDBCAttrRepo");
+        assertNotNull(module);
+        AttrRepoConf conf = module.getConf();
+        JDBCAttrRepoConf.class.cast(conf).setSql("SELECT * FROM otherTable WHERE name=?");
+        module.setConf(conf);
+
+        module = attrRepoDAO.save(module);
+        assertNotNull(module);
+        assertNotNull(module.getKey());
+        AttrRepo found = attrRepoDAO.find(module.getKey());
+        assertNotNull(found);
+        assertEquals("SELECT * FROM otherTable WHERE name=?", JDBCAttrRepoConf.class.cast(found.getConf()).getSql());
+    }
+
+    @Test
+    public void updateWithStubRepo() {
+        AttrRepo module = attrRepoDAO.find("DefaultStubAttrRepo");
+        assertNotNull(module);
+        assertEquals(1, StubAttrRepoConf.class.cast(module.getConf()).getAttributes().size());
+        AttrRepoConf conf = module.getConf();
+        StubAttrRepoConf.class.cast(conf).getAttributes().put("attr3", UUID.randomUUID().toString());
+        module.setConf(conf);
+
+        module = attrRepoDAO.save(module);
+        assertNotNull(module);
+        assertNotNull(module.getKey());
+        AttrRepo found = attrRepoDAO.find(module.getKey());
+        assertNotNull(found);
+        assertEquals(2, StubAttrRepoConf.class.cast(found.getConf()).getAttributes().size());
+    }
+
+    @Test
+    public void updateWithSyncopeRepo() {
+        AttrRepo module = attrRepoDAO.find("DefaultSyncopeAttrRepo");
+        assertNotNull(module);
+
+        AttrRepoConf conf = module.getConf();
+        SyncopeAttrRepoConf.class.cast(conf).setDomain("Two");
+        module.setConf(conf);
+
+        module = attrRepoDAO.save(module);
+        assertNotNull(module);
+        assertNotNull(module.getKey());
+        AttrRepo found = attrRepoDAO.find(module.getKey());
+        assertNotNull(found);
+        assertEquals("Two", SyncopeAttrRepoConf.class.cast(found.getConf()).getDomain());
+    }
+
+    @Test
+    public void delete() {
+        AttrRepo attrRepo = attrRepoDAO.find("DefaultSyncopeAttrRepo");
+        assertNotNull(attrRepo);
+
+        attrRepoDAO.delete("DefaultSyncopeAttrRepo");
+
+        attrRepo = attrRepoDAO.find("DefaultSyncopeAttrRepo");
+        assertNull(attrRepo);
+    }
+
+    private void saveAttrRepo(final String key, final AttrRepoConf conf) {
+        AttrRepo attrRepo = entityFactory.newEntity(AttrRepo.class);
+        attrRepo.setKey(key);
+        attrRepo.setDescription("An attr repo");
+        attrRepo.setState(AttrRepoState.ACTIVE);
+        attrRepo.setConf(conf);
+
+        AttrRepoItem keyMapping = entityFactory.newEntity(AttrRepoItem.class);
+        keyMapping.setIntAttrName("uid");
+        keyMapping.setExtAttrName("username");
+        keyMapping.setAttrRepo(attrRepo);
+        attrRepo.add(keyMapping);
+
+        AttrRepoItem fullnameMapping = entityFactory.newEntity(AttrRepoItem.class);
+        fullnameMapping.setIntAttrName("cn");
+        fullnameMapping.setExtAttrName("fullname");
+        fullnameMapping.setAttrRepo(attrRepo);
+        attrRepo.add(fullnameMapping);
+
+        attrRepo = attrRepoDAO.save(attrRepo);
+        assertNotNull(attrRepo);
+        assertNotNull(attrRepo.getKey());
+        assertEquals(attrRepo, attrRepoDAO.find(attrRepo.getKey()));
+        assertEquals(2, attrRepo.getItems().size());
+    }
+
+    private static boolean isSpecificConf(final AttrRepoConf conf, final Class<? extends AttrRepoConf> clazz) {
+        return ClassUtils.isAssignable(clazz, conf.getClass());
+    }
+}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
index 069028aede..e00ab6c18b 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java
@@ -39,9 +39,10 @@ import org.apache.syncope.common.lib.auth.SimpleMfaAuthModuleConf;
 import org.apache.syncope.common.lib.auth.StaticAuthModuleConf;
 import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
 import org.apache.syncope.common.lib.auth.U2FAuthModuleConf;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
+import org.apache.syncope.common.lib.types.AuthModuleState;
+import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModuleItem;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -434,6 +435,7 @@ public class AuthModuleTest extends AbstractTest {
         AuthModule module = entityFactory.newEntity(AuthModule.class);
         module.setKey(key);
         module.setDescription("An authentication module");
+        module.setState(AuthModuleState.ACTIVE);
         module.setConf(conf);
 
         AuthModuleItem keyMapping = entityFactory.newEntity(AuthModuleItem.class);
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
index ec3961368a..bc54c790bd 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java
@@ -33,10 +33,10 @@ import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken;
 import org.apache.syncope.common.lib.wa.ImpersonationAccount;
 import org.apache.syncope.common.lib.wa.U2FDevice;
 import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthProfileDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAAuthProfile;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAAuthProfile;
 import org.apache.syncope.core.spring.security.SecureRandomUtils;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/CASSPTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/CASSPTest.java
index 80d527d24f..d5be6b77b1 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/CASSPTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/CASSPTest.java
@@ -23,8 +23,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
 import java.util.UUID;
-import org.apache.syncope.core.persistence.api.dao.auth.CASSPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.dao.CASSPDAO;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.junit.jupiter.api.Test;
@@ -61,10 +61,9 @@ public class CASSPTest extends AbstractClientAppTest {
         int afterCount = casspDAO.findAll().size();
         assertEquals(afterCount, beforeCount + 1);
 
-
         rp = casspDAO.findByName("CAS");
         assertNotNull(rp);
-        
+
         rp = casspDAO.findByClientAppId(rp.getClientAppId());
         assertNotNull(rp);
 
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCJWKSTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCJWKSTest.java
index 9fdf77a810..e01e07aa34 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCJWKSTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCJWKSTest.java
@@ -25,8 +25,8 @@ import com.nimbusds.jose.jwk.KeyUse;
 import com.nimbusds.jose.jwk.RSAKey;
 import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
 import java.util.UUID;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCJWKSDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
+import org.apache.syncope.core.persistence.api.dao.OIDCJWKSDAO;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -43,9 +43,9 @@ public class OIDCJWKSTest extends AbstractTest {
         OIDCJWKS jwks = entityFactory.newEntity(OIDCJWKS.class);
 
         RSAKey jwk = new RSAKeyGenerator(2048)
-            .keyUse(KeyUse.SIGNATURE)
-            .keyID(UUID.randomUUID().toString())
-            .generate();
+                .keyUse(KeyUse.SIGNATURE)
+                .keyID(UUID.randomUUID().toString())
+                .generate();
 
         String json = new JWKSet(jwk).toString();
         jwks.setJson(json);
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java
index 8b0f3fa974..8b535f9950 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java
@@ -26,8 +26,8 @@ import java.util.UUID;
 import org.apache.syncope.common.lib.types.OIDCGrantType;
 import org.apache.syncope.common.lib.types.OIDCResponseType;
 import org.apache.syncope.common.lib.types.OIDCSubjectType;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.dao.OIDCRPDAO;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.junit.jupiter.api.Test;
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java
index 6c960011f3..57f2928ee9 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java
@@ -24,8 +24,8 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 
 import java.nio.charset.StandardCharsets;
 import java.util.UUID;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2IdPEntityDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPEntity;
+import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPEntityTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPEntityTest.java
index ba5978ca48..371c064ef9 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPEntityTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPEntityTest.java
@@ -35,8 +35,8 @@ import java.security.cert.CertificateFactory;
 import java.util.Date;
 import java.util.UUID;
 import org.apache.commons.io.IOUtils;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPEntityDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPEntity;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java
index 5978521c85..7410d6ee67 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java
@@ -26,8 +26,8 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import java.util.UUID;
 import org.apache.syncope.common.lib.types.SAML2SPNameId;
 import org.apache.syncope.common.lib.types.XmlSecAlgorithm;
-import org.apache.syncope.core.persistence.api.dao.auth.SAML2SPDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.dao.SAML2SPDAO;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
 import org.junit.jupiter.api.Test;
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java
index 411a73eed0..4089c30ab7 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java
@@ -25,10 +25,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
-import org.apache.syncope.core.persistence.jpa.entity.auth.JPAWAConfigEntry;
+import org.apache.syncope.core.persistence.jpa.entity.am.JPAWAConfigEntry;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
index ddcd0f99bf..a404aa400b 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java
@@ -26,10 +26,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 import java.util.UUID;
 import javax.persistence.PersistenceException;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.core.persistence.api.dao.OIDCRPDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.OIDCRPDAO;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 066d186d20..7a90deec3f 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -63,27 +63,27 @@ under the License.
                      jsonConf='{"_class":"org.apache.syncope.common.lib.policy.AllowedAttrReleasePolicyConf","name":"DenyAttrReleasePolicyConf"}'/>
 
   <!-- Authentication modules -->
-  <AuthModule id="DefaultLDAPAuthModule"
+  <AuthModule id="DefaultLDAPAuthModule" authModuleState="ACTIVE"
               description="LDAP auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.LDAPAuthModuleConf","userIdAttribute":"cn","bindDn": "uid=admin,ou=system", "bindCredential":"secret","ldapUrl":"ldap://localhost:1389","searchFilter":"cn={user}","baseDn":"ou=People,o=isp","subtreeSearch":true,"principalAttributeList":["sn","givenName","mail","cn"]}'/>
-  <AuthModule id="DefaultJDBCAuthModule"
+  <AuthModule id="DefaultJDBCAuthModule" authModuleState="ACTIVE"
               description="JDBC auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.JDBCAuthModuleConf","sql":"SELECT * FROM users_table WHERE name=?", "fieldPassword": "password"}'/>
-  <AuthModule id="DefaultGoogleMfaAuthModule"
+  <AuthModule id="DefaultGoogleMfaAuthModule" authModuleState="ACTIVE"
               description="Google Mfa auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf","codeDigits":6,"issuer":"SyncopeTest", "label":"SyncopeTest", "timeStepSize":30, "windowSize":3}'/>
-  <AuthModule id="DefaultSimpleMfaAuthModule"
+  <AuthModule id="DefaultSimpleMfaAuthModule" authModuleState="ACTIVE"
               description="Simple Mfa auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.SimpleMfaAuthModuleConf","tokenLength":6, "timeToKillInSeconds":30}'/>
-  <AuthModule id="DefaultDuoMfaAuthModule"
+  <AuthModule id="DefaultDuoMfaAuthModule" authModuleState="ACTIVE"
               description="Duo Mfa auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.DuoMfaAuthModuleConf","integrationKey":"DIOXVRZD2UMZ8XXMNFQ5","secretKey":"Q2IU2i8BFNd6VYflZT8Evl6lF7oPlj3PM15BmRU7", "applicationKey":"u1IHBaREMB7Cb5S4QMISAgHycpj8lPBkDGfWt23I", "apiHost":"theapi.duosecurity.com"}'/>
-  <AuthModule id="DefaultOIDCAuthModule"
+  <AuthModule id="DefaultOIDCAuthModule" authModuleState="ACTIVE"
               description="OIDC auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.OIDCAuthModuleConf","discoveryUri":"https://localhost:9443/syncope-wa/oidc/.well-known/openid-configuration", "id":"client-id", "secret": "client-secret" }'/>
-  <AuthModule id="DefaultSAML2IdPAuthModule"
+  <AuthModule id="DefaultSAML2IdPAuthModule" authModuleState="ACTIVE"
               description="SAML2 IdP auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf","keystorePassword":"p@$$word","privateKeyPassword":"p@$$word","identityProviderMetadataPath":"https://localhost:9443/syncope-wa/idp/metadata", "serviceProviderEntityId":"syncope:apache:org"}'/>
-  <AuthModule id="DefaultJaasAuthModule"
+  <AuthModule id="DefaultJaasAuthModule" authModuleState="ACTIVE"
               description="Jaas auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.JaasAuthModuleConf","realm":"SYNCOPE","kerberosRealmSystemProperty":"sample-value", "loginConfigType": "JavaLoginConfig", "loginConfigurationFile": "file:/etc/jaas/login.conf"}'/>
-  <AuthModule id="DefaultStaticAuthModule"
+  <AuthModule id="DefaultStaticAuthModule" authModuleState="ACTIVE"
               description="Static auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.StaticAuthModuleConf","users":{"syncope1": "$cynop3"}}'/>
-  <AuthModule id="DefaultSyncopeAuthModule"
+  <AuthModule id="DefaultSyncopeAuthModule" authModuleState="ACTIVE"
               description="Syncope auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf","domain":"Master"}'/>
-  <AuthModule id="DefaultU2FAuthModule"
+  <AuthModule id="DefaultU2FAuthModule" authModuleState="ACTIVE"
               description="U2F auth module" jsonConf='{"_class":"org.apache.syncope.common.lib.auth.U2FAuthModuleConf","expireDevices":40}'/>
 
   <AuthModuleItem id="26678936-af09-48b8-a789-36af0918b87d" extAttrName="family_name" intAttrName="syncopeUserAttr_surname"
@@ -106,6 +106,19 @@ under the License.
   <AuthModuleItem id="a1933059-e64f-409f-a86c-5b54da21787e" extAttrName="cn" intAttrName="cn"
                   password="0" purpose="NONE" mandatoryCondition="false" connObjectKey="0" authModule_id="DefaultLDAPAuthModule"/>
 
+  <!-- Attribute repositories -->
+  <AttrRepo id="DefaultLDAPAttrRepo" attrRepoState="ACTIVE"
+            description="LDAP attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.LDAPAttrRepoConf","searchFilter":"cn={user}","subtreeSearch":true,"ldapUrl":"ldap://localhost:1389","bindDn":"uid=admin,ou=system","bindCredential":"secret","baseDn":"ou=People,o=isp","attributes":{},"useAllQueryAttributes":true,"queryAttributes":{}}'/>
+  <AttrRepo id="DefaultJDBCAttrRepo" attrRepoState="ACTIVE"
+            description="JDBC attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.JDBCAttrRepoConf","sql":"SELECT * FROM table WHERE name=?","dialect":"org.hibernate.dialect.H2Dialect","driverClass":"org.h2.Driver","url":"jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1","user":"username","password":"password","singleRow":true,"requireAllAttributes":true,"caseCanonicalization":"NONE","queryType":"AND","columnMappings":{},"username":[],"attributes":{},"caseInsensitiveQueryAttributes [...]
+  <AttrRepo id="DefaultStubAttrRepo" attrRepoState="ACTIVE"
+            description="Stub attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.StubAttrRepoConf","attributes":{"attr1":"value1"}}'/>
+  <AttrRepo id="DefaultSyncopeAttrRepo" attrRepoState="ACTIVE"
+            description="Syncope attr repo" jsonConf='{"_class":"org.apache.syncope.common.lib.attr.SyncopeAttrRepoConf","domain":"Master","searchFilter":"username=={user}","basicAuthUsername":"admin","basicAuthPassword":"password","headers":{}}'/>
+
+  <AttrRepoItem id="d2d9e7be-d82b-4698-b0fb-67d480413049" extAttrName="identifier" intAttrName="attr1"
+                password="0" purpose="NONE" mandatoryCondition="false" connObjectKey="0" attrRepo_id="DefaultStubAttrRepo"/>
+
   <RelationshipType id="inclusion" description="Models the act that an object is included in another"/>
   <RelationshipType id="neighborhood" description="Models the act that an object is near another"/>
   
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AttrRepoDataBinder.java
similarity index 73%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AttrRepoDataBinder.java
index 0b13cf7e3a..33ac88e0f1 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AttrRepoDataBinder.java
@@ -18,12 +18,14 @@
  */
 package org.apache.syncope.core.provisioning.api.data;
 
-import org.apache.syncope.common.lib.Attr;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
 
-public interface WAConfigDataBinder {
+public interface AttrRepoDataBinder {
 
-    Attr get(WAConfigEntry waConfigEntry);
+    AttrRepo create(AttrRepoTO attrRepoTO);
 
-    WAConfigEntry set(Attr value);
+    AttrRepo update(AttrRepo attrRepo, AttrRepoTO attrRepoTO);
+
+    AttrRepoTO getAttrRepoTO(AttrRepo attrRepo);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthModuleDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthModuleDataBinder.java
index 1eab9f4f48..c42c0da80b 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthModuleDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthModuleDataBinder.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.core.provisioning.api.data;
 
 import org.apache.syncope.common.lib.to.AuthModuleTO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
 
 public interface AuthModuleDataBinder {
 
@@ -28,5 +28,4 @@ public interface AuthModuleDataBinder {
     AuthModule update(AuthModule authModule, AuthModuleTO authModuleTO);
 
     AuthModuleTO getAuthModuleTO(AuthModule authModule);
-
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthProfileDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthProfileDataBinder.java
index de3ed260b7..787800d18a 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthProfileDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuthProfileDataBinder.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.core.provisioning.api.data;
 
 import org.apache.syncope.common.lib.to.AuthProfileTO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 
 public interface AuthProfileDataBinder {
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ClientAppDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ClientAppDataBinder.java
index a4bf6bc0c2..bda214e1a0 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ClientAppDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ClientAppDataBinder.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.core.provisioning.api.data;
 
 import org.apache.syncope.common.lib.to.ClientAppTO;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
 
 public interface ClientAppDataBinder {
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCJWKSDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCJWKSDataBinder.java
index 7443afce74..bfd8303c32 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCJWKSDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/OIDCJWKSDataBinder.java
@@ -20,7 +20,7 @@ package org.apache.syncope.core.provisioning.api.data;
 
 import org.apache.syncope.common.lib.to.OIDCJWKSTO;
 import org.apache.syncope.common.lib.types.JWSAlgorithm;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS;
 
 public interface OIDCJWKSDataBinder {
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2IdPEntityDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2IdPEntityDataBinder.java
index 4a5bddfc2f..5f38b0e157 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2IdPEntityDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2IdPEntityDataBinder.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.core.provisioning.api.data;
 
 import org.apache.syncope.common.lib.to.SAML2IdPEntityTO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity;
 
 public interface SAML2IdPEntityDataBinder {
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPEntityDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPEntityDataBinder.java
index 450acb5ee2..f21ae3586d 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPEntityDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/SAML2SPEntityDataBinder.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.core.provisioning.api.data;
 
 import org.apache.syncope.common.lib.to.SAML2SPEntityTO;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity;
 
 public interface SAML2SPEntityDataBinder {
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java
index 0b13cf7e3a..0114aaf82e 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/WAConfigDataBinder.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.core.provisioning.api.data;
 
 import org.apache.syncope.common.lib.Attr;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry;
 
 public interface WAConfigDataBinder {
 
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppDataBinder.java
index 67f9ba5e65..28e058c132 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/wa/WAClientAppDataBinder.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.core.provisioning.api.data.wa;
 
 import org.apache.syncope.common.lib.wa.WAClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
 
 @FunctionalInterface
 public interface WAClientAppDataBinder {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
index cb1f72894a..28bbdd62ed 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
@@ -41,7 +41,9 @@ import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.ApplicationDAO;
+import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO;
 import org.apache.syncope.core.persistence.api.dao.AuditConfDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO;
 import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
 import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
@@ -66,8 +68,7 @@ import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
-import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
@@ -88,6 +89,7 @@ import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
 import org.apache.syncope.core.provisioning.api.data.AnyTypeClassDataBinder;
 import org.apache.syncope.core.provisioning.api.data.AnyTypeDataBinder;
 import org.apache.syncope.core.provisioning.api.data.ApplicationDataBinder;
+import org.apache.syncope.core.provisioning.api.data.AttrRepoDataBinder;
 import org.apache.syncope.core.provisioning.api.data.AuditDataBinder;
 import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
@@ -127,6 +129,7 @@ import org.apache.syncope.core.provisioning.java.data.AnyObjectDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.AnyTypeClassDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.AnyTypeDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.ApplicationDataBinderImpl;
+import org.apache.syncope.core.provisioning.java.data.AttrRepoDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.AuditDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.AuthModuleDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.AuthProfileDataBinderImpl;
@@ -899,6 +902,12 @@ public class ProvisioningContext {
         return new AuthModuleDataBinderImpl(entityFactory);
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public AttrRepoDataBinder attrRepoDataBinder(final EntityFactory entityFactory) {
+        return new AttrRepoDataBinderImpl(entityFactory);
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public AuthProfileDataBinder authProfileDataBinder(final EntityFactory entityFactory) {
@@ -1259,9 +1268,10 @@ public class ProvisioningContext {
             final ClientAppDataBinder clientAppDataBinder,
             final PolicyDataBinder policyDataBinder,
             final AuthModuleDataBinder authModuleDataBinder,
-            final AuthModuleDAO authModuleDAO) {
+            final AuthModuleDAO authModuleDAO,
+            final AttrRepoDAO attrRepoDAO) {
 
         return new WAClientAppDataBinderImpl(
-                clientAppDataBinder, policyDataBinder, authModuleDataBinder, authModuleDAO);
+                clientAppDataBinder, policyDataBinder, authModuleDataBinder, authModuleDAO, attrRepoDAO);
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderImpl.java
similarity index 68%
copy from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
copy to core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderImpl.java
index 5f223c61b2..82da7138a3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderImpl.java
@@ -20,36 +20,36 @@ package org.apache.syncope.core.provisioning.java.data;
 
 import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
 import org.apache.syncope.common.lib.to.ItemTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
-import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepoItem;
+import org.apache.syncope.core.provisioning.api.data.AttrRepoDataBinder;
 import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
+public class AttrRepoDataBinderImpl implements AttrRepoDataBinder {
 
-    protected static final Logger LOG = LoggerFactory.getLogger(AuthModuleDataBinder.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(AttrRepoDataBinder.class);
 
     protected final EntityFactory entityFactory;
 
-    public AuthModuleDataBinderImpl(final EntityFactory entityFactory) {
+    public AttrRepoDataBinderImpl(final EntityFactory entityFactory) {
         this.entityFactory = entityFactory;
     }
 
-    protected void populateItems(final AuthModuleTO authModuleTO, final AuthModule authModule) {
+    protected void populateItems(final AttrRepoTO attrRepoTO, final AttrRepo attrRepo) {
         SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
         SyncopeClientException invalidMapping =
                 SyncopeClientException.build(ClientExceptionType.InvalidMapping);
         SyncopeClientException requiredValuesMissing =
                 SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
 
-        authModuleTO.getItems().forEach(itemTO -> {
+        attrRepoTO.getItems().forEach(itemTO -> {
             if (itemTO == null) {
                 LOG.error("Null {}", ItemTO.class.getSimpleName());
                 invalidMapping.getElements().add("Null " + ItemTO.class.getSimpleName());
@@ -67,7 +67,7 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
                     scce.addException(invalidMandatoryCondition);
                 }
 
-                AuthModuleItem item = entityFactory.newEntity(AuthModuleItem.class);
+                AttrRepoItem item = entityFactory.newEntity(AttrRepoItem.class);
                 item.setIntAttrName(itemTO.getIntAttrName());
                 item.setExtAttrName(itemTO.getExtAttrName());
                 item.setMandatoryCondition(itemTO.getMandatoryCondition());
@@ -75,8 +75,8 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
                 item.setPassword(itemTO.isPassword());
                 item.setPropagationJEXLTransformer(itemTO.getPropagationJEXLTransformer());
                 item.setPullJEXLTransformer(itemTO.getPullJEXLTransformer());
-                item.setAuthModule(authModule);
-                authModule.add(item);
+                item.setAttrRepo(attrRepo);
+                attrRepo.add(item);
             }
         });
 
@@ -89,25 +89,27 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
     }
 
     @Override
-    public AuthModule create(final AuthModuleTO authModuleTO) {
-        AuthModule authModule = entityFactory.newEntity(AuthModule.class);
-        authModule.setKey(authModuleTO.getKey());
-        return update(authModule, authModuleTO);
+    public AttrRepo create(final AttrRepoTO attrRepoTO) {
+        AttrRepo attrRepo = entityFactory.newEntity(AttrRepo.class);
+        attrRepo.setKey(attrRepoTO.getKey());
+        return update(attrRepo, attrRepoTO);
     }
 
     @Override
-    public AuthModule update(final AuthModule authModule, final AuthModuleTO authModuleTO) {
-        authModule.setDescription(authModuleTO.getDescription());
-        authModule.setConf(authModuleTO.getConf());
+    public AttrRepo update(final AttrRepo attrRepo, final AttrRepoTO attrRepoTO) {
+        attrRepo.setDescription(attrRepoTO.getDescription());
+        attrRepo.setState(attrRepoTO.getState());
+        attrRepo.setOrder(attrRepoTO.getOrder());
+        attrRepo.setConf(attrRepoTO.getConf());
 
-        authModule.getItems().clear();
-        populateItems(authModuleTO, authModule);
+        attrRepo.getItems().clear();
+        populateItems(attrRepoTO, attrRepo);
 
-        return authModule;
+        return attrRepo;
     }
 
-    protected static void populateItems(final AuthModule authModule, final AuthModuleTO authModuleTO) {
-        authModule.getItems().forEach(item -> {
+    protected void populateItems(final AttrRepo attrRepo, final AttrRepoTO attrRepoTO) {
+        attrRepo.getItems().forEach(item -> {
             ItemTO itemTO = new ItemTO();
             itemTO.setKey(item.getKey());
             itemTO.setIntAttrName(item.getIntAttrName());
@@ -119,20 +121,22 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
             itemTO.setPullJEXLTransformer(item.getPullJEXLTransformer());
             itemTO.setPurpose(MappingPurpose.NONE);
 
-            authModuleTO.add(itemTO);
+            attrRepoTO.getItems().add(itemTO);
         });
     }
 
     @Override
-    public AuthModuleTO getAuthModuleTO(final AuthModule authModule) {
-        AuthModuleTO authModuleTO = new AuthModuleTO();
+    public AttrRepoTO getAttrRepoTO(final AttrRepo attrRepo) {
+        AttrRepoTO attrRepoTO = new AttrRepoTO();
 
-        authModuleTO.setKey(authModule.getKey());
-        authModuleTO.setDescription(authModule.getDescription());
-        authModuleTO.setConf(authModule.getConf());
+        attrRepoTO.setKey(attrRepo.getKey());
+        attrRepoTO.setDescription(attrRepo.getDescription());
+        attrRepoTO.setState(attrRepo.getState());
+        attrRepoTO.setOrder(attrRepo.getOrder());
+        attrRepoTO.setConf(attrRepo.getConf());
 
-        populateItems(authModule, authModuleTO);
+        populateItems(attrRepo, attrRepoTO);
 
-        return authModuleTO;
+        return attrRepoTO;
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
index 5f223c61b2..cfed222b25 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
@@ -25,8 +25,8 @@ import org.apache.syncope.common.lib.to.ItemTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModuleItem;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModuleItem;
 import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
 import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
 import org.slf4j.Logger;
@@ -98,6 +98,8 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
     @Override
     public AuthModule update(final AuthModule authModule, final AuthModuleTO authModuleTO) {
         authModule.setDescription(authModuleTO.getDescription());
+        authModule.setState(authModuleTO.getState());
+        authModule.setOrder(authModuleTO.getOrder());
         authModule.setConf(authModuleTO.getConf());
 
         authModule.getItems().clear();
@@ -106,7 +108,7 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
         return authModule;
     }
 
-    protected static void populateItems(final AuthModule authModule, final AuthModuleTO authModuleTO) {
+    protected void populateItems(final AuthModule authModule, final AuthModuleTO authModuleTO) {
         authModule.getItems().forEach(item -> {
             ItemTO itemTO = new ItemTO();
             itemTO.setKey(item.getKey());
@@ -119,7 +121,7 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
             itemTO.setPullJEXLTransformer(item.getPullJEXLTransformer());
             itemTO.setPurpose(MappingPurpose.NONE);
 
-            authModuleTO.add(itemTO);
+            authModuleTO.getItems().add(itemTO);
         });
     }
 
@@ -129,6 +131,8 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
 
         authModuleTO.setKey(authModule.getKey());
         authModuleTO.setDescription(authModule.getDescription());
+        authModuleTO.setState(authModule.getState());
+        authModuleTO.setOrder(authModule.getOrder());
         authModuleTO.setConf(authModule.getConf());
 
         populateItems(authModule, authModuleTO);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java
index cb7b8c8f6d..79722e4ebe 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderImpl.java
@@ -20,7 +20,7 @@ package org.apache.syncope.core.provisioning.java.data;
 
 import org.apache.syncope.common.lib.to.AuthProfileTO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthProfile;
+import org.apache.syncope.core.persistence.api.entity.am.AuthProfile;
 import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder;
 
 public class AuthProfileDataBinderImpl implements AuthProfileDataBinder {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
index 431d419722..ee3ec97164 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java
@@ -26,10 +26,10 @@ import org.apache.syncope.common.lib.to.SAML2SPClientAppTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.CASSPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCRPClientApp;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp;
 import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCJWKSDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCJWKSDataBinderImpl.java
index f35ca8cb2e..88f28c9222 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCJWKSDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCJWKSDataBinderImpl.java
@@ -29,7 +29,7 @@ import org.apache.syncope.common.lib.to.OIDCJWKSTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.JWSAlgorithm;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.OIDCJWKS;
+import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS;
 import org.apache.syncope.core.provisioning.api.data.OIDCJWKSDataBinder;
 import org.apache.syncope.core.spring.security.SecureRandomUtils;
 import org.slf4j.Logger;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPEntityDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPEntityDataBinderImpl.java
index f538b242c5..af43b597fb 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPEntityDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPEntityDataBinderImpl.java
@@ -21,7 +21,7 @@ package org.apache.syncope.core.provisioning.java.data;
 import java.util.Base64;
 import org.apache.syncope.common.lib.to.SAML2IdPEntityTO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2IdPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity;
 import org.apache.syncope.core.provisioning.api.data.SAML2IdPEntityDataBinder;
 
 public class SAML2IdPEntityDataBinderImpl implements SAML2IdPEntityDataBinder {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPEntityDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPEntityDataBinderImpl.java
index b6df09390c..2ea41ee719 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPEntityDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SPEntityDataBinderImpl.java
@@ -21,7 +21,7 @@ package org.apache.syncope.core.provisioning.java.data;
 import java.util.Base64;
 import org.apache.syncope.common.lib.to.SAML2SPEntityTO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPEntity;
+import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity;
 import org.apache.syncope.core.provisioning.api.data.SAML2SPEntityDataBinder;
 
 public class SAML2SPEntityDataBinderImpl implements SAML2SPEntityDataBinder {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderImpl.java
index 634de278da..fed6a334ab 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderImpl.java
@@ -20,9 +20,9 @@ package org.apache.syncope.core.provisioning.java.data;
 
 import java.util.Optional;
 import org.apache.syncope.common.lib.Attr;
-import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
+import org.apache.syncope.core.persistence.api.dao.WAConfigDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.auth.WAConfigEntry;
+import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry;
 import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder;
 
 public class WAConfigDataBinderImpl implements WAConfigDataBinder {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
index 23eb9a189c..7012ab1a91 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderImpl.java
@@ -18,12 +18,16 @@
  */
 package org.apache.syncope.core.provisioning.java.data.wa;
 
+import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.AuthPolicyConf;
+import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
 import org.apache.syncope.common.lib.wa.WAClientApp;
-import org.apache.syncope.core.persistence.api.dao.auth.AuthModuleDAO;
-import org.apache.syncope.core.persistence.api.entity.auth.AuthModule;
-import org.apache.syncope.core.persistence.api.entity.auth.ClientApp;
+import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO;
+import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO;
+import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
+import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
+import org.apache.syncope.core.persistence.api.entity.am.ClientApp;
 import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
 import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
 import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder;
@@ -43,16 +47,20 @@ public class WAClientAppDataBinderImpl implements WAClientAppDataBinder {
 
     protected final AuthModuleDAO authModuleDAO;
 
+    protected final AttrRepoDAO attrRepoDAO;
+
     public WAClientAppDataBinderImpl(
             final ClientAppDataBinder clientAppDataBinder,
             final PolicyDataBinder policyDataBinder,
             final AuthModuleDataBinder authModuleDataBinder,
-            final AuthModuleDAO authModuleDAO) {
+            final AuthModuleDAO authModuleDAO,
+            final AttrRepoDAO attrRepoDAO) {
 
         this.clientAppDataBinder = clientAppDataBinder;
         this.policyDataBinder = policyDataBinder;
         this.authModuleDataBinder = authModuleDataBinder;
         this.authModuleDAO = authModuleDAO;
+        this.attrRepoDAO = attrRepoDAO;
     }
 
     @Override
@@ -90,22 +98,30 @@ public class WAClientAppDataBinderImpl implements WAClientAppDataBinder {
                 waClientApp.setAccessPolicy(policyDataBinder.getPolicyTO(clientApp.getRealm().getAccessPolicy()));
             }
 
+            AttrReleasePolicyConf attrReleasePolicyConf = null;
             if (clientApp.getAttrReleasePolicy() != null) {
+                attrReleasePolicyConf = clientApp.getAttrReleasePolicy().getConf();
                 waClientApp.setAttrReleasePolicy(
                         policyDataBinder.getPolicyTO(clientApp.getAttrReleasePolicy()));
             } else if (clientApp.getRealm() != null && clientApp.getRealm().getAttrReleasePolicy() != null) {
+                attrReleasePolicyConf = clientApp.getRealm().getAttrReleasePolicy().getConf();
                 waClientApp.setAttrReleasePolicy(
                         policyDataBinder.getPolicyTO(clientApp.getRealm().getAttrReleasePolicy()));
             }
-
-            if (waClientApp.getReleaseAttrs().isEmpty()) {
-                if (clientApp.getAttrReleasePolicy() != null) {
-                    waClientApp.setAttrReleasePolicy(
-                            policyDataBinder.getPolicyTO(clientApp.getAttrReleasePolicy()));
-                } else if (clientApp.getRealm() != null && clientApp.getRealm().getAttrReleasePolicy() != null) {
-                    waClientApp.setAttrReleasePolicy(
-                            policyDataBinder.getPolicyTO(clientApp.getRealm().getAttrReleasePolicy()));
-                }
+            if (attrReleasePolicyConf instanceof DefaultAttrReleasePolicyConf
+                    && ((DefaultAttrReleasePolicyConf) attrReleasePolicyConf).getPrincipalAttrRepoConf() != null) {
+
+                (((DefaultAttrReleasePolicyConf) attrReleasePolicyConf).getPrincipalAttrRepoConf()).
+                        getAttrRepos().forEach(key -> {
+                            AttrRepo attrRepo = attrRepoDAO.find(key);
+                            if (attrRepo == null) {
+                                LOG.warn("AttrRepo " + attrRepo + " not found");
+                            } else {
+                                attrRepo.getItems().
+                                        forEach(item -> waClientApp.getReleaseAttrs().put(
+                                        item.getIntAttrName(), item.getExtAttrName()));
+                            }
+                        });
             }
         } catch (Exception e) {
             LOG.error("While building the configuration from an application's policy ", e);
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 64f472e47d..7c1faf0474 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -79,7 +79,6 @@ import org.apache.syncope.common.lib.request.GroupUR;
 import org.apache.syncope.common.lib.request.UserCR;
 import org.apache.syncope.common.lib.request.UserUR;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.syncope.common.lib.to.ClientAppTO;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
 import org.apache.syncope.common.lib.to.GroupTO;
@@ -111,6 +110,7 @@ import org.apache.syncope.common.rest.api.service.AnyObjectService;
 import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
 import org.apache.syncope.common.rest.api.service.AnyTypeService;
 import org.apache.syncope.common.rest.api.service.ApplicationService;
+import org.apache.syncope.common.rest.api.service.AttrRepoService;
 import org.apache.syncope.common.rest.api.service.AuditService;
 import org.apache.syncope.common.rest.api.service.AuthModuleService;
 import org.apache.syncope.common.rest.api.service.AuthProfileService;
@@ -339,6 +339,8 @@ public abstract class AbstractITCase {
 
     protected static AuthModuleService AUTH_MODULE_SERVICE;
 
+    protected static AttrRepoService ATTR_REPO_SERVICE;
+
     protected static SecurityQuestionService SECURITY_QUESTION_SERVICE;
 
     protected static ImplementationService IMPLEMENTATION_SERVICE;
@@ -463,6 +465,7 @@ public abstract class AbstractITCase {
         SCIM_CONF_SERVICE = ADMIN_CLIENT.getService(SCIMConfService.class);
         CLIENT_APP_SERVICE = ADMIN_CLIENT.getService(ClientAppService.class);
         AUTH_MODULE_SERVICE = ADMIN_CLIENT.getService(AuthModuleService.class);
+        ATTR_REPO_SERVICE = ADMIN_CLIENT.getService(AttrRepoService.class);
         SAML2SP_ENTITY_SERVICE = ADMIN_CLIENT.getService(SAML2SPEntityService.class);
         SAML2IDP_ENTITY_SERVICE = ADMIN_CLIENT.getService(SAML2IdPEntityService.class);
         AUTH_PROFILE_SERVICE = ADMIN_CLIENT.getService(AuthProfileService.class);
@@ -492,20 +495,8 @@ public abstract class AbstractITCase {
                 get(resultClass);
     }
 
-    @Autowired
-    protected ConfParamOps confParamOps;
-
-    @Autowired
-    protected ServiceOps serviceOps;
-
-    @Autowired
-    protected DomainOps domainOps;
-
-    @Autowired
-    protected DataSource testDataSource;
-
     @SuppressWarnings("unchecked")
-    protected <T extends SchemaTO> T createSchema(final SchemaType type, final T schemaTO) {
+    protected static <T extends SchemaTO> T createSchema(final SchemaType type, final T schemaTO) {
         Response response = SCHEMA_SERVICE.create(type, schemaTO);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
             Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
@@ -517,7 +508,7 @@ public abstract class AbstractITCase {
         return (T) getObject(response.getLocation(), SchemaService.class, schemaTO.getClass());
     }
 
-    protected RoleTO createRole(final RoleTO roleTO) {
+    protected static RoleTO createRole(final RoleTO roleTO) {
         Response response = ROLE_SERVICE.create(roleTO);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
             Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
@@ -528,13 +519,13 @@ public abstract class AbstractITCase {
         return getObject(response.getLocation(), RoleService.class, RoleTO.class);
     }
 
-    protected ReportTO createReport(final ReportTO report) {
+    protected static ReportTO createReport(final ReportTO report) {
         Response response = REPORT_SERVICE.create(report);
         assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
         return getObject(response.getLocation(), ReportService.class, ReportTO.class);
     }
 
-    protected Pair<String, String> createNotificationTask(
+    protected static Pair<String, String> createNotificationTask(
             final boolean active,
             final boolean includeAbout,
             final TraceLevel traceLevel,
@@ -579,7 +570,7 @@ public abstract class AbstractITCase {
         return Pair.of(notification.getKey(), req.getUsername());
     }
 
-    protected ProvisioningResult<UserTO> createUser(final UserCR req) {
+    protected static ProvisioningResult<UserTO> createUser(final UserCR req) {
         Response response = USER_SERVICE.create(req);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
             Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
@@ -591,26 +582,26 @@ public abstract class AbstractITCase {
         });
     }
 
-    protected ProvisioningResult<UserTO> updateUser(final UserUR req) {
+    protected static ProvisioningResult<UserTO> updateUser(final UserUR req) {
         return USER_SERVICE.update(req).
                 readEntity(new GenericType<>() {
                 });
     }
 
-    protected ProvisioningResult<UserTO> updateUser(final UserTO userTO) {
+    protected static ProvisioningResult<UserTO> updateUser(final UserTO userTO) {
         UserTO before = USER_SERVICE.read(userTO.getKey());
         return USER_SERVICE.update(AnyOperations.diff(userTO, before, false)).
                 readEntity(new GenericType<>() {
                 });
     }
 
-    protected ProvisioningResult<UserTO> deleteUser(final String key) {
+    protected static ProvisioningResult<UserTO> deleteUser(final String key) {
         return USER_SERVICE.delete(key).
                 readEntity(new GenericType<>() {
                 });
     }
 
-    protected ProvisioningResult<AnyObjectTO> createAnyObject(final AnyObjectCR req) {
+    protected static ProvisioningResult<AnyObjectTO> createAnyObject(final AnyObjectCR req) {
         Response response = ANY_OBJECT_SERVICE.create(req);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
             Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
@@ -622,19 +613,19 @@ public abstract class AbstractITCase {
         });
     }
 
-    protected ProvisioningResult<AnyObjectTO> updateAnyObject(final AnyObjectUR req) {
+    protected static ProvisioningResult<AnyObjectTO> updateAnyObject(final AnyObjectUR req) {
         return ANY_OBJECT_SERVICE.update(req).
                 readEntity(new GenericType<>() {
                 });
     }
 
-    protected ProvisioningResult<AnyObjectTO> deleteAnyObject(final String key) {
+    protected static ProvisioningResult<AnyObjectTO> deleteAnyObject(final String key) {
         return ANY_OBJECT_SERVICE.delete(key).
                 readEntity(new GenericType<>() {
                 });
     }
 
-    protected ProvisioningResult<GroupTO> createGroup(final GroupCR req) {
+    protected static ProvisioningResult<GroupTO> createGroup(final GroupCR req) {
         Response response = GROUP_SERVICE.create(req);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
             Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
@@ -646,20 +637,20 @@ public abstract class AbstractITCase {
         });
     }
 
-    protected ProvisioningResult<GroupTO> updateGroup(final GroupUR req) {
+    protected static ProvisioningResult<GroupTO> updateGroup(final GroupUR req) {
         return GROUP_SERVICE.update(req).
                 readEntity(new GenericType<>() {
                 });
     }
 
-    protected ProvisioningResult<GroupTO> deleteGroup(final String key) {
+    protected static ProvisioningResult<GroupTO> deleteGroup(final String key) {
         return GROUP_SERVICE.delete(key).
                 readEntity(new GenericType<>() {
                 });
     }
 
     @SuppressWarnings("unchecked")
-    protected <T extends PolicyTO> T createPolicy(final PolicyType type, final T policy) {
+    protected static <T extends PolicyTO> T createPolicy(final PolicyType type, final T policy) {
         Response response = POLICY_SERVICE.create(type, policy);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
             Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
@@ -670,19 +661,7 @@ public abstract class AbstractITCase {
         return (T) getObject(response.getLocation(), PolicyService.class, policy.getClass());
     }
 
-    @SuppressWarnings("unchecked")
-    protected AuthModuleTO createAuthModule(final AuthModuleTO authModule) {
-        Response response = AUTH_MODULE_SERVICE.create(authModule);
-        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
-            Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
-            if (ex != null) {
-                throw (RuntimeException) ex;
-            }
-        }
-        return getObject(response.getLocation(), AuthModuleService.class, authModule.getClass());
-    }
-
-    protected ResourceTO createResource(final ResourceTO resourceTO) {
+    protected static ResourceTO createResource(final ResourceTO resourceTO) {
         Response response = RESOURCE_SERVICE.create(resourceTO);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
             Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
@@ -693,14 +672,14 @@ public abstract class AbstractITCase {
         return getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
     }
 
-    protected List<BatchResponseItem> parseBatchResponse(final Response response) throws IOException {
+    protected static List<BatchResponseItem> parseBatchResponse(final Response response) throws IOException {
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         return BatchPayloadParser.parse(
                 (InputStream) response.getEntity(), response.getMediaType(), new BatchResponseItem());
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes", "UseOfObsoleteCollectionType" })
-    protected InitialDirContext getLdapResourceDirContext(final String bindDn, final String bindPwd)
+    protected static InitialDirContext getLdapResourceDirContext(final String bindDn, final String bindPwd)
             throws NamingException {
         ResourceTO ldapRes = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP);
         ConnInstanceTO ldapConn = CONNECTOR_SERVICE.read(ldapRes.getConnector(), Locale.ENGLISH.getLanguage());
@@ -718,7 +697,7 @@ public abstract class AbstractITCase {
         return new InitialDirContext(env);
     }
 
-    protected Object getLdapRemoteObject(final String bindDn, final String bindPwd, final String objectDn) {
+    protected static Object getLdapRemoteObject(final String bindDn, final String bindPwd, final String objectDn) {
         InitialDirContext ctx = null;
         try {
             ctx = getLdapResourceDirContext(bindDn, bindPwd);
@@ -737,7 +716,7 @@ public abstract class AbstractITCase {
         }
     }
 
-    protected void createLdapRemoteObject(
+    protected static void createLdapRemoteObject(
             final String bindDn,
             final String bindPwd,
             final Pair<String, Set<Attribute>> entryAttrs) throws NamingException {
@@ -765,7 +744,7 @@ public abstract class AbstractITCase {
         }
     }
 
-    protected void updateLdapRemoteObject(
+    protected static void updateLdapRemoteObject(
             final String bindDn,
             final String bindPwd,
             final String objectDn,
@@ -793,7 +772,7 @@ public abstract class AbstractITCase {
         }
     }
 
-    protected void removeLdapRemoteObject(
+    protected static void removeLdapRemoteObject(
             final String bindDn,
             final String bindPwd,
             final String objectDn) {
@@ -816,7 +795,7 @@ public abstract class AbstractITCase {
         }
     }
 
-    protected <T> T queryForObject(
+    protected static <T> T queryForObject(
             final JdbcTemplate jdbcTemplate,
             final int maxWaitSeconds,
             final String sql, final Class<T> requiredType, final Object... args) {
@@ -834,7 +813,7 @@ public abstract class AbstractITCase {
         return object.get();
     }
 
-    protected OIDCRPClientAppTO buildOIDCRP() {
+    protected static OIDCRPClientAppTO buildOIDCRP() {
         AuthPolicyTO authPolicyTO = new AuthPolicyTO();
         authPolicyTO.setKey("AuthPolicyTest_" + getUUIDString());
         authPolicyTO.setName("Authentication Policy");
@@ -863,7 +842,7 @@ public abstract class AbstractITCase {
         return oidcrpTO;
     }
 
-    protected SAML2SPClientAppTO buildSAML2SP() {
+    protected static SAML2SPClientAppTO buildSAML2SP() {
         AuthPolicyTO authPolicyTO = new AuthPolicyTO();
         authPolicyTO.setKey("AuthPolicyTest_" + getUUIDString());
         authPolicyTO.setName("Authentication Policy");
@@ -893,7 +872,7 @@ public abstract class AbstractITCase {
     }
 
     @SuppressWarnings("unchecked")
-    protected <T extends ClientAppTO> T createClientApp(final ClientAppType type, final T clientAppTO) {
+    protected static <T extends ClientAppTO> T createClientApp(final ClientAppType type, final T clientAppTO) {
         Response response = CLIENT_APP_SERVICE.create(type, clientAppTO);
         if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
             Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
@@ -904,7 +883,7 @@ public abstract class AbstractITCase {
         return (T) getObject(response.getLocation(), ClientAppService.class, clientAppTO.getClass());
     }
 
-    protected AuthPolicyTO buildAuthPolicyTO(final String authModuleKey) {
+    protected static AuthPolicyTO buildAuthPolicyTO(final String authModuleKey) {
         AuthPolicyTO policy = new AuthPolicyTO();
         policy.setName("Test Authentication policy");
 
@@ -915,7 +894,7 @@ public abstract class AbstractITCase {
         return policy;
     }
 
-    protected AttrReleasePolicyTO buildAttrReleasePolicyTO() {
+    protected static AttrReleasePolicyTO buildAttrReleasePolicyTO() {
         AttrReleasePolicyTO policy = new AttrReleasePolicyTO();
         policy.setName("Test Attribute Release policy");
         policy.setStatus(Boolean.TRUE);
@@ -929,7 +908,7 @@ public abstract class AbstractITCase {
         return policy;
     }
 
-    protected AccessPolicyTO buildAccessPolicyTO() {
+    protected static AccessPolicyTO buildAccessPolicyTO() {
         AccessPolicyTO policy = new AccessPolicyTO();
         policy.setName("Test Access policy");
         policy.setEnabled(true);
@@ -941,7 +920,7 @@ public abstract class AbstractITCase {
         return policy;
     }
 
-    protected List<AuditEntry> query(final AuditQuery query, final int maxWaitSeconds) {
+    protected static List<AuditEntry> query(final AuditQuery query, final int maxWaitSeconds) {
         int i = 0;
         List<AuditEntry> results = List.of();
         do {
@@ -955,4 +934,16 @@ public abstract class AbstractITCase {
         return results;
     }
 
+    @Autowired
+    protected ConfParamOps confParamOps;
+
+    @Autowired
+    protected ServiceOps serviceOps;
+
+    @Autowired
+    protected DomainOps domainOps;
+
+    @Autowired
+    protected DataSource testDataSource;
+
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AttrRepoITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AttrRepoITCase.java
new file mode 100644
index 0000000000..946e3cc77e
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AttrRepoITCase.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.fit.core;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.UUID;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.attr.AttrRepoConf;
+import org.apache.syncope.common.lib.attr.JDBCAttrRepoConf;
+import org.apache.syncope.common.lib.attr.LDAPAttrRepoConf;
+import org.apache.syncope.common.lib.attr.StubAttrRepoConf;
+import org.apache.syncope.common.lib.attr.SyncopeAttrRepoConf;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
+import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.types.AttrRepoState;
+import org.apache.syncope.common.rest.api.service.AttrRepoService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.Test;
+
+public class AttrRepoITCase extends AbstractITCase {
+
+    private enum AttrRepoSupportedType {
+        STUB,
+        SYNCOPE,
+        LDAP,
+        JDBC;
+
+    };
+
+    private static AttrRepoTO createAttrRepo(final AttrRepoTO attrRepo) {
+        Response response = ATTR_REPO_SERVICE.create(attrRepo);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+        return getObject(response.getLocation(), AttrRepoService.class, attrRepo.getClass());
+    }
+
+    private static AttrRepoTO buildAttrRepoTO(final AttrRepoSupportedType type) {
+        AttrRepoTO attrRepoTO = new AttrRepoTO();
+        attrRepoTO.setKey("Test" + type + "AttrRepo" + getUUIDString());
+        attrRepoTO.setDescription("A test " + type + " attr repo");
+        attrRepoTO.setState(AttrRepoState.ACTIVE);
+
+        AttrRepoConf conf;
+        switch (type) {
+            case LDAP:
+                conf = new LDAPAttrRepoConf();
+                LDAPAttrRepoConf.class.cast(conf).setBaseDn("dc=example,dc=org");
+                LDAPAttrRepoConf.class.cast(conf).setSearchFilter("cn={user}");
+                LDAPAttrRepoConf.class.cast(conf).setSubtreeSearch(true);
+                LDAPAttrRepoConf.class.cast(conf).setLdapUrl("ldap://localhost:1389");
+                LDAPAttrRepoConf.class.cast(conf).setBaseDn("cn=Directory Manager,dc=example,dc=org");
+                LDAPAttrRepoConf.class.cast(conf).setBindCredential("Password");
+                break;
+
+            case JDBC:
+                conf = new JDBCAttrRepoConf();
+                JDBCAttrRepoConf.class.cast(conf).setSql("SELECT * FROM table WHERE name=?");
+                JDBCAttrRepoConf.class.cast(conf).getUsername().add("name");
+                JDBCAttrRepoConf.class.cast(conf).getQueryAttributes().put("key1", "value1");
+                break;
+
+            case SYNCOPE:
+                conf = new SyncopeAttrRepoConf();
+                SyncopeAttrRepoConf.class.cast(conf).setDomain(SyncopeConstants.MASTER_DOMAIN);
+                break;
+
+            case STUB:
+            default:
+                conf = new StubAttrRepoConf();
+                StubAttrRepoConf.class.cast(conf).getAttributes().put("attr9", UUID.randomUUID().toString());
+                StubAttrRepoConf.class.cast(conf).getAttributes().put("attr8", UUID.randomUUID().toString());
+                break;
+        }
+        attrRepoTO.setConf(conf);
+
+        ItemTO keyMapping = new ItemTO();
+        keyMapping.setIntAttrName("uid");
+        keyMapping.setExtAttrName("username");
+        attrRepoTO.getItems().add(keyMapping);
+
+        ItemTO fullnameMapping = new ItemTO();
+        fullnameMapping.setIntAttrName("cn");
+        fullnameMapping.setExtAttrName("fullname");
+        attrRepoTO.getItems().add(fullnameMapping);
+
+        return attrRepoTO;
+    }
+
+    private static boolean isSpecificConf(final AttrRepoConf conf, final Class<? extends AttrRepoConf> clazz) {
+        return ClassUtils.isAssignable(clazz, conf.getClass());
+    }
+
+    @Test
+    public void list() {
+        List<AttrRepoTO> attrRepoTOs = ATTR_REPO_SERVICE.list();
+        assertNotNull(attrRepoTOs);
+        assertFalse(attrRepoTOs.isEmpty());
+
+        assertTrue(attrRepoTOs.stream().anyMatch(
+                attrRepo -> isSpecificConf(attrRepo.getConf(), LDAPAttrRepoConf.class)
+                && attrRepo.getKey().equals("DefaultLDAPAttrRepo")));
+        assertTrue(attrRepoTOs.stream().anyMatch(
+                attrRepo -> isSpecificConf(attrRepo.getConf(), JDBCAttrRepoConf.class)
+                && attrRepo.getKey().equals("DefaultJDBCAttrRepo")));
+        assertTrue(attrRepoTOs.stream().anyMatch(
+                attrRepo -> isSpecificConf(attrRepo.getConf(), StubAttrRepoConf.class)
+                && attrRepo.getKey().equals("DefaultStubAttrRepo")));
+        assertTrue(attrRepoTOs.stream().anyMatch(
+                attrRepo -> isSpecificConf(attrRepo.getConf(), SyncopeAttrRepoConf.class)
+                && attrRepo.getKey().equals("DefaultSyncopeAttrRepo")));
+    }
+
+    @Test
+    public void getLDAPAttrRepo() {
+        AttrRepoTO attrRepoTO = ATTR_REPO_SERVICE.read("DefaultLDAPAttrRepo");
+
+        assertNotNull(attrRepoTO);
+        assertTrue(StringUtils.isNotBlank(attrRepoTO.getDescription()));
+        assertTrue(isSpecificConf(attrRepoTO.getConf(), LDAPAttrRepoConf.class));
+        assertFalse(isSpecificConf(attrRepoTO.getConf(), JDBCAttrRepoConf.class));
+    }
+
+    @Test
+    public void getJDBCAttrRepo() {
+        AttrRepoTO attrRepoTO = ATTR_REPO_SERVICE.read("DefaultJDBCAttrRepo");
+
+        assertNotNull(attrRepoTO);
+        assertTrue(StringUtils.isNotBlank(attrRepoTO.getDescription()));
+        assertTrue(isSpecificConf(attrRepoTO.getConf(), JDBCAttrRepoConf.class));
+        assertFalse(isSpecificConf(attrRepoTO.getConf(), LDAPAttrRepoConf.class));
+    }
+
+    @Test
+    public void getStubAttrRepo() {
+        AttrRepoTO attrRepoTO = ATTR_REPO_SERVICE.read("DefaultStubAttrRepo");
+
+        assertNotNull(attrRepoTO);
+        assertTrue(StringUtils.isNotBlank(attrRepoTO.getDescription()));
+        assertTrue(isSpecificConf(attrRepoTO.getConf(), StubAttrRepoConf.class));
+        assertFalse(isSpecificConf(attrRepoTO.getConf(), SyncopeAttrRepoConf.class));
+    }
+
+    @Test
+    public void getSyncopeAttrRepo() {
+        AttrRepoTO attrRepoTO = ATTR_REPO_SERVICE.read("DefaultSyncopeAttrRepo");
+
+        assertNotNull(attrRepoTO);
+        assertTrue(StringUtils.isNotBlank(attrRepoTO.getDescription()));
+        assertTrue(isSpecificConf(attrRepoTO.getConf(), SyncopeAttrRepoConf.class));
+        assertFalse(isSpecificConf(attrRepoTO.getConf(), StubAttrRepoConf.class));
+    }
+
+    @Test
+    public void create() {
+        EnumSet.allOf(AttrRepoSupportedType.class).forEach(type -> {
+            AttrRepoTO attrRepoTO = createAttrRepo(buildAttrRepoTO(type));
+            assertNotNull(attrRepoTO);
+            assertTrue(attrRepoTO.getDescription().contains("A test " + type + " attr repo"));
+            assertEquals(2, attrRepoTO.getItems().size());
+        });
+    }
+
+    @Test
+    public void updateLDAPAttrRepo() {
+        AttrRepoTO ldapAttrRepoTO = ATTR_REPO_SERVICE.read("DefaultLDAPAttrRepo");
+        assertNotNull(ldapAttrRepoTO);
+
+        AttrRepoTO newLdapAttrRepoTO = buildAttrRepoTO(AttrRepoSupportedType.LDAP);
+        newLdapAttrRepoTO = createAttrRepo(newLdapAttrRepoTO);
+        assertNotNull(newLdapAttrRepoTO);
+
+        AttrRepoConf conf = ldapAttrRepoTO.getConf();
+        assertNotNull(conf);
+        LDAPAttrRepoConf.class.cast(conf).setSubtreeSearch(false);
+        newLdapAttrRepoTO.setConf(conf);
+
+        // update new attr repo
+        ATTR_REPO_SERVICE.update(newLdapAttrRepoTO);
+        newLdapAttrRepoTO = ATTR_REPO_SERVICE.read(newLdapAttrRepoTO.getKey());
+        assertNotNull(newLdapAttrRepoTO);
+
+        conf = newLdapAttrRepoTO.getConf();
+        assertFalse(LDAPAttrRepoConf.class.cast(conf).isSubtreeSearch());
+    }
+
+    @Test
+    public void updateJDBCAttrRepo() {
+        AttrRepoTO jdbcAttrRepoTO = ATTR_REPO_SERVICE.read("DefaultJDBCAttrRepo");
+        assertNotNull(jdbcAttrRepoTO);
+
+        AttrRepoTO newJDBCAttrRepoTO = buildAttrRepoTO(AttrRepoSupportedType.JDBC);
+        newJDBCAttrRepoTO = createAttrRepo(newJDBCAttrRepoTO);
+        assertNotNull(newJDBCAttrRepoTO);
+
+        AttrRepoConf conf = jdbcAttrRepoTO.getConf();
+        assertNotNull(conf);
+        JDBCAttrRepoConf.class.cast(conf).setCaseCanonicalization(JDBCAttrRepoConf.CaseCanonicalizationMode.UPPER);
+        newJDBCAttrRepoTO.setConf(conf);
+
+        // update new attr repo
+        ATTR_REPO_SERVICE.update(newJDBCAttrRepoTO);
+        newJDBCAttrRepoTO = ATTR_REPO_SERVICE.read(newJDBCAttrRepoTO.getKey());
+        assertNotNull(newJDBCAttrRepoTO);
+
+        conf = newJDBCAttrRepoTO.getConf();
+        assertEquals(
+                JDBCAttrRepoConf.CaseCanonicalizationMode.UPPER,
+                JDBCAttrRepoConf.class.cast(conf).getCaseCanonicalization());
+    }
+
+    @Test
+    public void updateStubAttrRepo() {
+        AttrRepoTO staticAttrRepoTO = ATTR_REPO_SERVICE.read("DefaultStubAttrRepo");
+        assertNotNull(staticAttrRepoTO);
+
+        AttrRepoTO newStubAttrRepoTO = buildAttrRepoTO(AttrRepoSupportedType.STUB);
+        newStubAttrRepoTO = createAttrRepo(newStubAttrRepoTO);
+        assertNotNull(newStubAttrRepoTO);
+
+        AttrRepoConf conf = staticAttrRepoTO.getConf();
+        assertNotNull(conf);
+        assertEquals(1, StubAttrRepoConf.class.cast(conf).getAttributes().size());
+        StubAttrRepoConf.class.cast(conf).getAttributes().put("attr3", "value3");
+        newStubAttrRepoTO.setConf(conf);
+
+        // update new attr repo
+        ATTR_REPO_SERVICE.update(newStubAttrRepoTO);
+        newStubAttrRepoTO = ATTR_REPO_SERVICE.read(newStubAttrRepoTO.getKey());
+        assertNotNull(newStubAttrRepoTO);
+
+        conf = newStubAttrRepoTO.getConf();
+        assertEquals(2, StubAttrRepoConf.class.cast(conf).getAttributes().size());
+    }
+
+    @Test
+    public void updateSyncopeAttrRepo() {
+        AttrRepoTO syncopeAttrRepoTO = ATTR_REPO_SERVICE.read("DefaultSyncopeAttrRepo");
+        assertNotNull(syncopeAttrRepoTO);
+
+        AttrRepoTO newSyncopeAttrRepoTO = buildAttrRepoTO(AttrRepoSupportedType.SYNCOPE);
+        newSyncopeAttrRepoTO = createAttrRepo(newSyncopeAttrRepoTO);
+        assertNotNull(newSyncopeAttrRepoTO);
+
+        AttrRepoConf conf = syncopeAttrRepoTO.getConf();
+        assertNotNull(conf);
+        SyncopeAttrRepoConf.class.cast(conf).setDomain("Two");
+        newSyncopeAttrRepoTO.setConf(conf);
+
+        // update new attr repo
+        ATTR_REPO_SERVICE.update(newSyncopeAttrRepoTO);
+        newSyncopeAttrRepoTO = ATTR_REPO_SERVICE.read(newSyncopeAttrRepoTO.getKey());
+        assertNotNull(newSyncopeAttrRepoTO);
+
+        conf = newSyncopeAttrRepoTO.getConf();
+        assertEquals("Two", SyncopeAttrRepoConf.class.cast(conf).getDomain());
+    }
+
+    @Test
+    public void delete() throws IOException {
+        EnumSet.allOf(AttrRepoSupportedType.class).forEach(type -> {
+            AttrRepoTO read = createAttrRepo(buildAttrRepoTO(type));
+            assertNotNull(read);
+
+            ATTR_REPO_SERVICE.delete(read.getKey());
+
+            try {
+                ATTR_REPO_SERVICE.read(read.getKey());
+                fail("This should not happen");
+            } catch (SyncopeClientException e) {
+                assertNotNull(e);
+            }
+        });
+    }
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
index 0905809d13..807be22a48 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthModuleITCase.java
@@ -28,6 +28,7 @@ import java.io.IOException;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.UUID;
+import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -45,6 +46,7 @@ import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
 import org.apache.syncope.common.lib.auth.U2FAuthModuleConf;
 import org.apache.syncope.common.lib.to.AuthModuleTO;
 import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.rest.api.service.AuthModuleService;
 import org.apache.syncope.fit.AbstractITCase;
 import org.junit.jupiter.api.Test;
 
@@ -64,6 +66,17 @@ public class AuthModuleITCase extends AbstractITCase {
 
     };
 
+    private static AuthModuleTO createAuthModule(final AuthModuleTO authModule) {
+        Response response = AUTH_MODULE_SERVICE.create(authModule);
+        if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+            Exception ex = CLIENT_FACTORY.getExceptionMapper().fromResponse(response);
+            if (ex != null) {
+                throw (RuntimeException) ex;
+            }
+        }
+        return getObject(response.getLocation(), AuthModuleService.class, authModule.getClass());
+    }
+
     private static AuthModuleTO buildAuthModuleTO(final AuthModuleSupportedType type) {
         AuthModuleTO authModuleTO = new AuthModuleTO();
         authModuleTO.setKey("Test" + type + "AuthenticationModule" + getUUIDString());
@@ -153,12 +166,12 @@ public class AuthModuleITCase extends AbstractITCase {
         ItemTO keyMapping = new ItemTO();
         keyMapping.setIntAttrName("uid");
         keyMapping.setExtAttrName("username");
-        authModuleTO.add(keyMapping);
+        authModuleTO.getItems().add(keyMapping);
 
         ItemTO fullnameMapping = new ItemTO();
         fullnameMapping.setIntAttrName("cn");
         fullnameMapping.setExtAttrName("fullname");
-        authModuleTO.add(fullnameMapping);
+        authModuleTO.getItems().add(fullnameMapping);
 
         return authModuleTO;
     }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WAClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WAClientAppITCase.java
index ff197fceb3..11d2820501 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WAClientAppITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/wa/WAClientAppITCase.java
@@ -134,12 +134,12 @@ public class WAClientAppITCase extends AbstractITCase {
         ItemTO keyMapping = new ItemTO();
         keyMapping.setIntAttrName("uid");
         keyMapping.setExtAttrName("username");
-        authModuleTO.add(keyMapping);
+        authModuleTO.getItems().add(keyMapping);
 
         ItemTO fullnameMapping = new ItemTO();
         fullnameMapping.setIntAttrName("cn");
         fullnameMapping.setExtAttrName("fullname");
-        authModuleTO.add(fullnameMapping);
+        authModuleTO.getItems().add(fullnameMapping);
 
         AUTH_MODULE_SERVICE.update(authModuleTO);
 
diff --git a/fit/wa-reference/pom.xml b/fit/wa-reference/pom.xml
index 8d916f908f..50eedfe73b 100644
--- a/fit/wa-reference/pom.xml
+++ b/fit/wa-reference/pom.xml
@@ -52,6 +52,12 @@ under the License.
       <version>${project.version}</version>
     </dependency>
 
+    <!-- required by JDBC attribute repository -->
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+    </dependency>
+
     <!-- TEST -->
     <dependency>
       <groupId>org.apache.syncope.ext.saml2sp4ui</groupId>
diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/SAML2SRAITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/SAML2SRAITCase.java
index 7f8091bbc8..aa772a5a7a 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/SAML2SRAITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/SAML2SRAITCase.java
@@ -60,7 +60,6 @@ public class SAML2SRAITCase extends AbstractSRAITCase {
         assumeTrue(SAML2SRAITCase.class.equals(MethodHandles.lookup().lookupClass()));
 
         doStartSRA("saml2");
-//        sraRouteService.pushToSRA();
     }
 
     @BeforeAll
diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/AbstractUIITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/AbstractUIITCase.java
index f02e083ffd..4d1db40dea 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/AbstractUIITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/AbstractUIITCase.java
@@ -25,7 +25,9 @@ import static org.junit.jupiter.api.Assertions.fail;
 import java.io.IOException;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
 import org.apache.syncope.common.lib.policy.AuthPolicyTO;
+import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
 import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.PolicyType;
@@ -61,7 +63,39 @@ public abstract class AbstractUIITCase extends AbstractITCase {
                         fail("Could not create Test Auth Policy");
                     }
 
-                    return POLICY_SERVICE.read(PolicyType.AUTH, response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+                    return POLICY_SERVICE.read(
+                            PolicyType.AUTH,
+                            response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+                });
+    }
+
+    protected static AttrReleasePolicyTO getAttrReleasePolicy() {
+        String stubAttrRepo = "DefaultStubAttrRepo";
+        String description = "UI attr release policy";
+
+        return POLICY_SERVICE.list(PolicyType.ATTR_RELEASE).stream().
+                map(AttrReleasePolicyTO.class::cast).
+                filter(policy -> description.equals(policy.getName())
+                && policy.getConf() instanceof DefaultAttrReleasePolicyConf
+                && ((DefaultAttrReleasePolicyConf) policy.getConf()).getPrincipalAttrRepoConf().
+                        getAttrRepos().contains(stubAttrRepo)).
+                findFirst().
+                orElseGet(() -> {
+                    DefaultAttrReleasePolicyConf policyConf = new DefaultAttrReleasePolicyConf();
+                    policyConf.getPrincipalAttrRepoConf().getAttrRepos().add(stubAttrRepo);
+
+                    AttrReleasePolicyTO policy = new AttrReleasePolicyTO();
+                    policy.setName(description);
+                    policy.setConf(policyConf);
+
+                    Response response = POLICY_SERVICE.create(PolicyType.ATTR_RELEASE, policy);
+                    if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
+                        fail("Could not create Test Attr Release Policy");
+                    }
+
+                    return POLICY_SERVICE.read(
+                            PolicyType.ATTR_RELEASE,
+                            response.getHeaderString(RESTHeaders.RESOURCE_KEY));
                 });
     }
 
diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDC4UIITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDC4UIITCase.java
index 0f0b5bbb68..8839169402 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDC4UIITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDC4UIITCase.java
@@ -81,10 +81,11 @@ public class OIDC4UIITCase extends AbstractUIITCase {
         clientApp.setSubjectType(OIDCSubjectType.PUBLIC);
         clientApp.getRedirectUris().clear();
         clientApp.getRedirectUris().add(baseAddress + OIDCC4UIConstants.URL_CONTEXT + "/code-consumer");
-        clientApp.setAuthPolicy(getAuthPolicy().getKey());
         clientApp.setSignIdToken(true);
         clientApp.setJwtAccessToken(true);
         clientApp.setLogoutUri(baseAddress + OIDCC4UIConstants.URL_CONTEXT + "/logout");
+        clientApp.setAuthPolicy(getAuthPolicy().getKey());
+        clientApp.setAttrReleasePolicy(getAttrReleasePolicy().getKey());
 
         CLIENT_APP_SERVICE.update(ClientAppType.OIDCRP, clientApp);
         CLIENT_APP_SERVICE.pushToWA();
@@ -200,6 +201,11 @@ public class OIDC4UIITCase extends AbstractUIITCase {
         // 2b. WA attribute consent screen
         if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
             responseBody = EntityUtils.toString(response.getEntity());
+
+            // check attribute repository
+            assertTrue(responseBody.contains("identifier"));
+            assertTrue(responseBody.contains("[value1]"));
+
             String execution = extractWAExecution(responseBody);
 
             List<NameValuePair> form = new ArrayList<>();
diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/SAML2SP4UIITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/SAML2SP4UIITCase.java
index 67fbe272b4..1334835d77 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/SAML2SP4UIITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/SAML2SP4UIITCase.java
@@ -84,6 +84,7 @@ public class SAML2SP4UIITCase extends AbstractUIITCase {
         clientApp.setSignResponses(true);
         clientApp.setRequiredNameIdFormat(SAML2SPNameId.PERSISTENT);
         clientApp.setAuthPolicy(getAuthPolicy().getKey());
+        clientApp.setAttrReleasePolicy(getAttrReleasePolicy().getKey());
 
         CLIENT_APP_SERVICE.update(ClientAppType.SAML2SP, clientApp);
         CLIENT_APP_SERVICE.pushToWA();
@@ -237,6 +238,10 @@ public class SAML2SP4UIITCase extends AbstractUIITCase {
 
         // 2c. WA attribute consent screen
         if (isOk) {
+            // check attribute repository
+            assertTrue(responseBody.contains("identifier"));
+            assertTrue(responseBody.contains("[value1]"));
+
             String execution = extractWAExecution(responseBody);
 
             List<NameValuePair> form = new ArrayList<>();
@@ -252,7 +257,8 @@ public class SAML2SP4UIITCase extends AbstractUIITCase {
             post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8));
             try (CloseableHttpResponse response = httpclient.execute(post, context)) {
                 assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
-                location = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
+                location = response.getFirstHeader(HttpHeaders.LOCATION).getValue().
+                        replace("http://", "https://").replace(":8080", ":9443");
             }
         }
 
diff --git a/pom.xml b/pom.xml
index 6a258cce8f..736dd914a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -448,7 +448,7 @@ under the License.
 
     <pac4j.version>5.3.1</pac4j.version>
 
-    <cas.version>6.5.5</cas.version>
+    <cas.version>6.5.6-SNAPSHOT</cas.version>
     <cas-client.version>3.6.4</cas-client.version>
 
     <h2.version>2.1.214</h2.version>
@@ -1382,6 +1382,11 @@ under the License.
         <artifactId>cas-server-support-actions</artifactId>
         <version>${cas.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apereo.cas</groupId>
+        <artifactId>cas-server-support-jpa-util</artifactId>
+        <version>${cas.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.apereo.cas</groupId>
         <artifactId>cas-server-support-ldap</artifactId>
diff --git a/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc b/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc
new file mode 100644
index 0000000000..27f39ff891
--- /dev/null
+++ b/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc
@@ -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.
+//
+=== Attribute Repositories
+
+Attribute Repositories allow to enrich the profile of an user authenticated by <<web-access,WA>>, in the context of a
+certain <<policies-attribute-release,Attribute Release Policy>>.
+
+Some attribute repositories are provided:
+
+* https://apereo.github.io/cas/6.5.x/integration/Attribute-Resolution-JDBC.html[Database^]
+* https://apereo.github.io/cas/6.5.x/integration/Attribute-Resolution-LDAP.html[LDAP^]
+* https://apereo.github.io/cas/6.5.x/integration/Attribute-Resolution-Stub.html[Stub^]
+* https://apereo.github.io/cas/6.5.x/integration/Attribute-Resolution-Syncope.html[Syncope^]
+
+[TIP]
+====
+Custom authentication modules can be provided by implementing the
+ifeval::["{snapshotOrRelease}" == "release"]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java[AttrRepoConf^]
+endif::[]
+ifeval::["{snapshotOrRelease}" == "snapshot"]
+https://github.com/apache/syncope/blob/master/common/am/lib/src/main/java/org/apache/syncope/common/lib/attr/AttrRepoConf.java[AttrRepoConf^]
+endif::[]
+interface and extending appropriately the
+ifeval::["{snapshotOrRelease}" == "release"]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java[WAPropertySourceLocator^]
+endif::[]
+ifeval::["{snapshotOrRelease}" == "snapshot"]
+https://github.com/apache/syncope/blob/master/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java[WAPropertySourceLocator^]
+endif::[]
+class.
+====
+
+[NOTE]
+Attribute Repositories are dynamically translated into 
+https://apereo.github.io/cas/6.5.x/integration/Attribute-Resolution.html[CAS Attribute Resolution^] configuration.
diff --git a/src/main/asciidoc/reference-guide/concepts/concepts.adoc b/src/main/asciidoc/reference-guide/concepts/concepts.adoc
index ed09b9dfaf..328e5dcf16 100644
--- a/src/main/asciidoc/reference-guide/concepts/concepts.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/concepts.adoc
@@ -50,6 +50,8 @@ include::routes.adoc[]
 
 include::authenticationmodules.adoc[]
 
+include::attributerepositories.adoc[]
+
 include::clientapplications.adoc[]
 
 include::domains.adoc[]
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AttrRepoPropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AttrRepoPropertySourceMapper.java
new file mode 100644
index 0000000000..14768a0f0c
--- /dev/null
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AttrRepoPropertySourceMapper.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.attr.AttrRepoConf;
+import org.apache.syncope.common.lib.attr.JDBCAttrRepoConf;
+import org.apache.syncope.common.lib.attr.LDAPAttrRepoConf;
+import org.apache.syncope.common.lib.attr.StubAttrRepoConf;
+import org.apache.syncope.common.lib.attr.SyncopeAttrRepoConf;
+import org.apache.syncope.common.lib.to.AttrRepoTO;
+import org.apereo.cas.configuration.CasCoreConfigurationUtils;
+import org.apereo.cas.configuration.model.core.authentication.AttributeRepositoryStates;
+import org.apereo.cas.configuration.model.core.authentication.StubPrincipalAttributesProperties;
+import org.apereo.cas.configuration.model.support.jdbc.JdbcPrincipalAttributesProperties;
+import org.apereo.cas.configuration.model.support.ldap.LdapPrincipalAttributesProperties;
+import org.apereo.cas.configuration.model.support.syncope.SyncopePrincipalAttributesProperties;
+
+public class AttrRepoPropertySourceMapper extends PropertySourceMapper implements AttrRepoConf.Mapper {
+
+    protected final String syncopeClientAddress;
+
+    protected final AttrRepoTO attrRepoTO;
+
+    public AttrRepoPropertySourceMapper(final String syncopeClientAddress, final AttrRepoTO attrRepoTO) {
+        this.syncopeClientAddress = syncopeClientAddress;
+        this.attrRepoTO = attrRepoTO;
+    }
+
+    @Override
+    public Map<String, Object> map(final StubAttrRepoConf conf) {
+        StubPrincipalAttributesProperties props = new StubPrincipalAttributesProperties();
+        props.setId(attrRepoTO.getKey());
+        props.setState(AttributeRepositoryStates.valueOf(attrRepoTO.getState().name()));
+        props.setOrder(attrRepoTO.getOrder());
+        props.setAttributes(conf.getAttributes());
+
+        return prefix("cas.authn.attribute-repository.stub.", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final LDAPAttrRepoConf conf) {
+        LdapPrincipalAttributesProperties props = new LdapPrincipalAttributesProperties();
+        props.setId(attrRepoTO.getKey());
+        props.setState(AttributeRepositoryStates.valueOf(attrRepoTO.getState().name()));
+        props.setOrder(attrRepoTO.getOrder());
+        props.setAttributes(conf.getAttributes());
+        props.setUseAllQueryAttributes(conf.isUseAllQueryAttributes());
+        props.setQueryAttributes(conf.getQueryAttributes());
+        fill(props, conf);
+
+        return prefix("cas.authn.attribute-repository.ldap[].", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final JDBCAttrRepoConf conf) {
+        JdbcPrincipalAttributesProperties props = new JdbcPrincipalAttributesProperties();
+        props.setId(attrRepoTO.getKey());
+        props.setState(AttributeRepositoryStates.valueOf(attrRepoTO.getState().name()));
+        props.setOrder(attrRepoTO.getOrder());
+        props.setSql(conf.getSql());
+        props.setSingleRow(conf.isSingleRow());
+        props.setRequireAllAttributes(conf.isRequireAllAttributes());
+        props.setCaseCanonicalization(conf.getCaseCanonicalization().name());
+        props.setQueryType(conf.getQueryType().name());
+        props.setColumnMappings(conf.getColumnMappings());
+        props.setUsername(conf.getUsername());
+        props.setAttributes(conf.getAttributes());
+        props.setCaseInsensitiveQueryAttributes(conf.getCaseInsensitiveQueryAttributes());
+        props.setQueryAttributes(conf.getQueryAttributes());
+        fill(props, conf);
+
+        return prefix("cas.authn.attribute-repository.jdbc[].", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final SyncopeAttrRepoConf conf) {
+        SyncopePrincipalAttributesProperties props = new SyncopePrincipalAttributesProperties();
+        props.setId(attrRepoTO.getKey());
+        props.setState(AttributeRepositoryStates.valueOf(attrRepoTO.getState().name()));
+        props.setOrder(attrRepoTO.getOrder());
+        props.setDomain(conf.getDomain());
+        props.setUrl(StringUtils.substringBefore(syncopeClientAddress, "/rest"));
+        props.setSearchFilter(conf.getSearchFilter());
+        props.setBasicAuthUsername(conf.getBasicAuthUsername());
+        props.setBasicAuthPassword(conf.getBasicAuthPassword());
+        props.setHeaders(props.getHeaders());
+
+        return prefix("cas.authn.attribute-repository.syncope.", CasCoreConfigurationUtils.asMap(props));
+    }
+}
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java
new file mode 100644
index 0000000000..61966d9a93
--- /dev/null
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/AuthModulePropertySourceMapper.java
@@ -0,0 +1,264 @@
+/*
+ * 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 java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.auth.AuthModuleConf;
+import org.apache.syncope.common.lib.auth.DuoMfaAuthModuleConf;
+import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf;
+import org.apache.syncope.common.lib.auth.JDBCAuthModuleConf;
+import org.apache.syncope.common.lib.auth.JaasAuthModuleConf;
+import org.apache.syncope.common.lib.auth.LDAPAuthModuleConf;
+import org.apache.syncope.common.lib.auth.OIDCAuthModuleConf;
+import org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf;
+import org.apache.syncope.common.lib.auth.SimpleMfaAuthModuleConf;
+import org.apache.syncope.common.lib.auth.StaticAuthModuleConf;
+import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
+import org.apache.syncope.common.lib.auth.U2FAuthModuleConf;
+import org.apache.syncope.common.lib.to.AuthModuleTO;
+import org.apache.syncope.common.lib.types.AuthModuleState;
+import org.apereo.cas.configuration.CasCoreConfigurationUtils;
+import org.apereo.cas.configuration.model.core.authentication.AuthenticationHandlerStates;
+import org.apereo.cas.configuration.model.support.generic.AcceptAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.jaas.JaasAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.jdbc.authn.QueryJdbcAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.ldap.AbstractLdapAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.ldap.LdapAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.mfa.DuoSecurityMultifactorAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.mfa.gauth.GoogleAuthenticatorMultifactorProperties;
+import org.apereo.cas.configuration.model.support.mfa.gauth.LdapGoogleAuthenticatorMultifactorProperties;
+import org.apereo.cas.configuration.model.support.mfa.simple.CasSimpleMultifactorAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.mfa.u2f.U2FMultifactorAuthenticationProperties;
+import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jGenericOidcClientProperties;
+import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jOidcClientProperties;
+import org.apereo.cas.configuration.model.support.pac4j.saml.Pac4jSamlClientProperties;
+import org.apereo.cas.configuration.model.support.syncope.SyncopeAuthenticationProperties;
+import org.apereo.cas.util.ResourceUtils;
+import org.apereo.cas.util.model.TriStateBoolean;
+
+public class AuthModulePropertySourceMapper extends PropertySourceMapper implements AuthModuleConf.Mapper {
+
+    protected final String syncopeClientAddress;
+
+    protected final AuthModuleTO authModuleTO;
+
+    public AuthModulePropertySourceMapper(final String syncopeClientAddress, final AuthModuleTO attrRepoTO) {
+        this.syncopeClientAddress = syncopeClientAddress;
+        this.authModuleTO = attrRepoTO;
+    }
+
+    @Override
+    public Map<String, Object> map(final StaticAuthModuleConf conf) {
+        AcceptAuthenticationProperties props = new AcceptAuthenticationProperties();
+        props.setName(authModuleTO.getKey());
+        props.setState(AuthenticationHandlerStates.valueOf(authModuleTO.getState().name()));
+        props.setOrder(authModuleTO.getOrder());
+        String users = conf.getUsers().entrySet().stream().
+                map(entry -> entry.getKey() + "::" + entry.getValue()).
+                collect(Collectors.joining(","));
+        props.setUsers(users);
+
+        return prefix("cas.authn.accept.", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final LDAPAuthModuleConf conf) {
+        LdapAuthenticationProperties props = new LdapAuthenticationProperties();
+        props.setName(authModuleTO.getKey());
+        props.setState(AuthenticationHandlerStates.valueOf(authModuleTO.getState().name()));
+        props.setOrder(authModuleTO.getOrder());
+        if (StringUtils.isNotBlank(conf.getBindDn()) && StringUtils.isNotBlank(conf.getBindCredential())) {
+            props.setType(AbstractLdapAuthenticationProperties.AuthenticationTypes.AUTHENTICATED);
+        }
+        props.setPrincipalAttributeId(conf.getUserIdAttribute());
+        props.setPrincipalAttributeList(conf.getPrincipalAttributeList());
+        fill(props, conf);
+
+        return prefix("cas.authn.ldap[].", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final JDBCAuthModuleConf conf) {
+        QueryJdbcAuthenticationProperties props = new QueryJdbcAuthenticationProperties();
+        props.setName(authModuleTO.getKey());
+        props.setState(AuthenticationHandlerStates.valueOf(authModuleTO.getState().name()));
+        props.setOrder(authModuleTO.getOrder());
+        props.setSql(conf.getSql());
+        props.setFieldDisabled(conf.getFieldDisabled());
+        props.setFieldExpired(conf.getFieldExpired());
+        props.setFieldPassword(conf.getFieldPassword());
+        props.setPrincipalAttributeList(conf.getPrincipalAttributeList());
+        fill(props, conf);
+
+        return prefix("cas.authn.jdbc.query[].", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final JaasAuthModuleConf conf) {
+        JaasAuthenticationProperties props = new JaasAuthenticationProperties();
+        props.setName(authModuleTO.getKey());
+        props.setState(AuthenticationHandlerStates.valueOf(authModuleTO.getState().name()));
+        props.setOrder(authModuleTO.getOrder());
+        props.setLoginConfigType(conf.getLoginConfigType());
+        props.setKerberosKdcSystemProperty(conf.getKerberosKdcSystemProperty());
+        props.setKerberosRealmSystemProperty(conf.getKerberosRealmSystemProperty());
+        props.setLoginConfigType(conf.getLoginConfigurationFile());
+        props.setRealm(conf.getRealm());
+
+        return prefix("cas.authn.jaas[].", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final OIDCAuthModuleConf conf) {
+        Pac4jGenericOidcClientProperties props = new Pac4jGenericOidcClientProperties();
+        props.setId(conf.getId());
+        props.setEnabled(authModuleTO.getState() == AuthModuleState.ACTIVE);
+        props.setCustomParams(conf.getCustomParams());
+        props.setDiscoveryUri(conf.getDiscoveryUri());
+        props.setMaxClockSkew(conf.getMaxClockSkew());
+        props.setClientName(authModuleTO.getKey());
+        props.setPreferredJwsAlgorithm(conf.getPreferredJwsAlgorithm());
+        props.setResponseMode(conf.getResponseMode());
+        props.setResponseType(conf.getResponseType());
+        props.setScope(conf.getScope());
+        props.setSecret(conf.getSecret());
+        props.setPrincipalAttributeId(conf.getUserIdAttribute());
+        Pac4jOidcClientProperties client = new Pac4jOidcClientProperties();
+        client.setGeneric(props);
+
+        return prefix("cas.authn.pac4j.oidc[].generic.", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final SAML2IdPAuthModuleConf conf) {
+        Pac4jSamlClientProperties props = new Pac4jSamlClientProperties();
+        props.setClientName(authModuleTO.getKey());
+        props.setEnabled(authModuleTO.getState() == AuthModuleState.ACTIVE);
+        props.setAcceptedSkew(conf.getAcceptedSkew());
+        props.setAssertionConsumerServiceIndex(conf.getAssertionConsumerServiceIndex());
+        props.setAttributeConsumingServiceIndex(conf.getAttributeConsumingServiceIndex());
+        props.setAuthnContextClassRef(conf.getAuthnContextClassRefs());
+        props.setAuthnContextComparisonType(conf.getAuthnContextComparisonType());
+        props.setBlockedSignatureSigningAlgorithms(conf.getBlockedSignatureSigningAlgorithms());
+        props.setDestinationBinding(conf.getDestinationBinding().getUri());
+        props.setIdentityProviderMetadataPath(conf.getIdentityProviderMetadataPath());
+        props.setKeystoreAlias(conf.getKeystoreAlias());
+        props.setKeystorePassword(conf.getKeystorePassword());
+        props.setMaximumAuthenticationLifetime(conf.getMaximumAuthenticationLifetime());
+        props.setNameIdPolicyFormat(conf.getNameIdPolicyFormat());
+        props.setPrivateKeyPassword(conf.getPrivateKeyPassword());
+        props.setProviderName(conf.getProviderName());
+        props.setServiceProviderEntityId(conf.getServiceProviderEntityId());
+        props.setSignatureAlgorithms(conf.getSignatureAlgorithms());
+        props.setSignatureCanonicalizationAlgorithm(conf.getSignatureCanonicalizationAlgorithm());
+        props.setSignatureReferenceDigestMethods(conf.getSignatureReferenceDigestMethods());
+        props.setPrincipalAttributeId(conf.getUserIdAttribute());
+        props.setNameIdPolicyAllowCreate(StringUtils.isBlank(conf.getNameIdPolicyAllowCreate())
+                ? TriStateBoolean.UNDEFINED
+                : TriStateBoolean.valueOf(conf.getNameIdPolicyAllowCreate().toUpperCase()));
+
+        return prefix("cas.authn.pac4j.saml[].", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final SyncopeAuthModuleConf conf) {
+        SyncopeAuthenticationProperties props = new SyncopeAuthenticationProperties();
+        props.setName(authModuleTO.getKey());
+        props.setState(AuthenticationHandlerStates.valueOf(authModuleTO.getState().name()));
+        props.setDomain(conf.getDomain());
+        props.setUrl(StringUtils.substringBefore(syncopeClientAddress, "/rest"));
+
+        return prefix("cas.authn.syncope.", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final GoogleMfaAuthModuleConf conf) {
+        GoogleAuthenticatorMultifactorProperties props = new GoogleAuthenticatorMultifactorProperties();
+        props.setName(authModuleTO.getKey());
+        props.setOrder(authModuleTO.getOrder());
+        props.getCore().setIssuer(conf.getIssuer());
+        props.getCore().setCodeDigits(conf.getCodeDigits());
+        props.getCore().setLabel(conf.getLabel());
+        props.getCore().setTimeStepSize(conf.getTimeStepSize());
+        props.getCore().setWindowSize(conf.getWindowSize());
+
+        if (conf.getLdap() != null) {
+            LdapGoogleAuthenticatorMultifactorProperties ldapProps = new LdapGoogleAuthenticatorMultifactorProperties();
+            ldapProps.setAccountAttributeName(conf.getLdap().getAccountAttributeName());
+            fill(ldapProps, conf.getLdap());
+            props.setLdap(ldapProps);
+        }
+
+        return prefix("cas.authn.mfa.gauth.", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public Map<String, Object> map(final DuoMfaAuthModuleConf conf) {
+        DuoSecurityMultifactorAuthenticationProperties props = new DuoSecurityMultifactorAuthenticationProperties();
+        props.setName(authModuleTO.getKey());
+        props.setOrder(authModuleTO.getOrder());
+        props.setDuoApiHost(conf.getApiHost());
+        props.setDuoApplicationKey(conf.getApplicationKey());
+        props.setDuoIntegrationKey(conf.getIntegrationKey());
+        props.setDuoSecretKey(conf.getSecretKey());
+
+        return prefix("cas.authn.mfa.duo.", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final U2FAuthModuleConf conf) {
+        U2FMultifactorAuthenticationProperties props = new U2FMultifactorAuthenticationProperties();
+        props.setName(authModuleTO.getKey());
+        props.setOrder(authModuleTO.getOrder());
+        props.getCore().setExpireDevices(conf.getExpireDevices());
+        props.getCore().setExpireDevicesTimeUnit(TimeUnit.valueOf(conf.getExpireDevicesTimeUnit()));
+        props.getCore().setExpireRegistrations(conf.getExpireRegistrations());
+        props.getCore().setExpireRegistrationsTimeUnit(TimeUnit.valueOf(conf.getExpireRegistrationsTimeUnit()));
+
+        return prefix("cas.authn.mfa.u2f.", CasCoreConfigurationUtils.asMap(props));
+    }
+
+    @Override
+    public Map<String, Object> map(final SimpleMfaAuthModuleConf conf) {
+        CasSimpleMultifactorAuthenticationProperties props = new CasSimpleMultifactorAuthenticationProperties();
+        props.setName(authModuleTO.getKey());
+        props.setOrder(authModuleTO.getOrder());
+        props.setTokenLength(conf.getTokenLength());
+        props.setTimeToKillInSeconds(conf.getTimeToKillInSeconds());
+        props.getMail().setAttributeName(conf.getEmailAttribute());
+        props.getMail().setFrom(conf.getEmailFrom());
+        props.getMail().setSubject(conf.getEmailSubject());
+        props.getMail().setText(conf.getEmailText());
+
+        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);
+            }
+        }
+
+        return prefix("cas.authn.mfa.simple.", CasCoreConfigurationUtils.asMap(props));
+    }
+}
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/PropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/PropertySourceMapper.java
new file mode 100644
index 0000000000..548cc965d9
--- /dev/null
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/PropertySourceMapper.java
@@ -0,0 +1,92 @@
+/*
+ * 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 java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.AbstractJDBCConf;
+import org.apache.syncope.common.lib.AbstractLDAPConf;
+import org.apereo.cas.configuration.model.support.ConnectionPoolingProperties;
+import org.apereo.cas.configuration.model.support.jpa.AbstractJpaProperties;
+import org.apereo.cas.configuration.model.support.ldap.AbstractLdapProperties.LdapConnectionPoolPassivator;
+import org.apereo.cas.configuration.model.support.ldap.AbstractLdapSearchProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class PropertySourceMapper {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(PropertySourceMapper.class);
+
+    protected static Map<String, Object> prefix(final String prefix, final Map<String, Object> map) {
+        return map.entrySet().stream().
+                map(e -> Pair.of(prefix + e.getKey(), e.getValue())).
+                collect(Collectors.toMap(Pair::getKey, Pair::getValue));
+    }
+
+    protected void fill(final AbstractLdapSearchProperties props, final AbstractLDAPConf conf) {
+        props.setLdapUrl(conf.getLdapUrl());
+        props.setBaseDn(conf.getBaseDn());
+        props.setSearchFilter(conf.getSearchFilter());
+        props.setSubtreeSearch(conf.isSubtreeSearch());
+        props.setBindDn(conf.getBindDn());
+        props.setBindCredential(conf.getBindCredential());
+        props.setDisablePooling(conf.isDisablePooling());
+        props.setMinPoolSize(conf.getMinPoolSize());
+        props.setMaxPoolSize(conf.getMaxPoolSize());
+        props.setPoolPassivator(LdapConnectionPoolPassivator.valueOf(conf.getPoolPassivator().name()).name());
+        props.setValidateOnCheckout(conf.isValidateOnCheckout());
+        props.setValidatePeriodically(conf.isValidatePeriodically());
+        props.setValidateTimeout(conf.getValidateTimeout().toString());
+        props.setValidatePeriod(conf.getValidatePeriod().toString());
+        props.setFailFast(conf.isFailFast());
+        props.setIdleTime(conf.getIdleTime().toString());
+        props.setPrunePeriod(conf.getPrunePeriod().toString());
+        props.setBlockWaitTime(conf.getBlockWaitTime().toString());
+        props.setUseStartTls(conf.isUseStartTls());
+        props.setConnectTimeout(conf.getConnectTimeout().toString());
+        props.setResponseTimeout(conf.getResponseTimeout().toString());
+        props.setAllowMultipleDns(conf.isAllowMultipleDns());
+        props.setAllowMultipleEntries(conf.isAllowMultipleEntries());
+        props.setFollowReferrals(conf.isFollowReferrals());
+        props.setBinaryAttributes(conf.getBinaryAttributes());
+    }
+
+    protected void fill(final AbstractJpaProperties props, final AbstractJDBCConf conf) {
+        props.setDialect(conf.getDialect());
+        props.setDriverClass(conf.getDriverClass());
+        props.setUrl(conf.getUrl());
+        props.setUser(conf.getUser());
+        props.setPassword(conf.getPassword());
+        props.setDefaultCatalog(conf.getDefaultCatalog());
+        props.setDefaultSchema(conf.getDefaultSchema());
+        props.setHealthQuery(conf.getHealthQuery());
+        props.setIdleTimeout(conf.getIdleTimeout().toString());
+        props.setDataSourceName(conf.getDataSourceName());
+        props.setLeakThreshold(conf.getPoolLeakThreshold());
+
+        ConnectionPoolingProperties connProps = new ConnectionPoolingProperties();
+        connProps.setMinSize(conf.getMinPoolSize());
+        connProps.setMaxSize(conf.getMaxPoolSize());
+        connProps.setMaxWait(conf.getMaxPoolWait().toString());
+        connProps.setSuspension(conf.isPoolSuspension());
+        connProps.setTimeoutMillis(conf.getPoolTimeoutMillis());
+        props.setPool(connProps);
+    }
+}
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
index 6b36823e96..79d2dd60da 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WABootstrapConfiguration.java
@@ -31,6 +31,7 @@ public class WABootstrapConfiguration {
 
     @Configuration(proxyBeanMethods = false)
     public static class WAClientConfiguration {
+
         @Value("${wa.anonymousUser}")
         private String anonymousUser;
 
@@ -48,6 +49,7 @@ public class WABootstrapConfiguration {
 
     @Configuration(proxyBeanMethods = false)
     public static class PropertySourceConfiguration {
+
         @Bean
         public PropertySourceLocator configPropertySourceLocator(final WARestClient waRestClient) {
             return new WAPropertySourceLocator(waRestClient);
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
index bf6b799a9b..3f289ea50c 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java
@@ -18,51 +18,16 @@
  */
 package org.apache.syncope.wa.bootstrap;
 
-import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
-import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
-import java.util.List;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
-import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.lib.SyncopeClient;
-import org.apache.syncope.common.lib.auth.AuthModuleConf;
-import org.apache.syncope.common.lib.auth.DuoMfaAuthModuleConf;
-import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf;
-import org.apache.syncope.common.lib.auth.JDBCAuthModuleConf;
-import org.apache.syncope.common.lib.auth.JaasAuthModuleConf;
-import org.apache.syncope.common.lib.auth.LDAPAuthModuleConf;
-import org.apache.syncope.common.lib.auth.OIDCAuthModuleConf;
-import org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf;
-import org.apache.syncope.common.lib.auth.SimpleMfaAuthModuleConf;
-import org.apache.syncope.common.lib.auth.StaticAuthModuleConf;
-import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf;
-import org.apache.syncope.common.lib.auth.U2FAuthModuleConf;
+import org.apache.syncope.common.rest.api.service.AttrRepoService;
 import org.apache.syncope.common.rest.api.service.AuthModuleService;
 import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
-import org.apereo.cas.configuration.CasConfigurationProperties;
-import org.apereo.cas.configuration.CasCoreConfigurationUtils;
-import org.apereo.cas.configuration.model.core.authentication.AuthenticationProperties;
-import org.apereo.cas.configuration.model.support.generic.AcceptAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.jaas.JaasAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.jdbc.JdbcAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.jdbc.authn.QueryJdbcAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.ldap.AbstractLdapAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.ldap.LdapAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.mfa.DuoSecurityMultifactorAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.mfa.MultifactorAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.mfa.gauth.GoogleAuthenticatorMultifactorProperties;
-import org.apereo.cas.configuration.model.support.mfa.gauth.LdapGoogleAuthenticatorMultifactorProperties;
-import org.apereo.cas.configuration.model.support.mfa.simple.CasSimpleMultifactorAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.mfa.u2f.U2FMultifactorAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.pac4j.Pac4jDelegatedAuthenticationProperties;
-import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jGenericOidcClientProperties;
-import org.apereo.cas.configuration.model.support.pac4j.oidc.Pac4jOidcClientProperties;
-import org.apereo.cas.configuration.model.support.pac4j.saml.Pac4jSamlClientProperties;
-import org.apereo.cas.configuration.model.support.syncope.SyncopeAuthenticationProperties;
-import org.apereo.cas.util.ResourceUtils;
-import org.apereo.cas.util.model.TriStateBoolean;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
@@ -82,381 +47,24 @@ public class WAPropertySourceLocator implements PropertySourceLocator {
         this.waRestClient = waRestClient;
     }
 
-    protected SimpleFilterProvider getParentCasFilterProvider() {
-        return new SimpleFilterProvider().
-                setFailOnUnknownId(false).
-                addFilter(CasConfigurationProperties.class.getSimpleName(),
-                        SimpleBeanPropertyFilter.filterOutAllExcept(
-                                CasCoreConfigurationUtils.getPropertyName(
-                                        CasConfigurationProperties.class,
-                                        CasConfigurationProperties::getAuthn)));
-    }
-
-    protected Map<String, Object> filterCasProperties(
-            final CasConfigurationProperties casProperties,
-            final SimpleFilterProvider filters) {
-
-        return CasCoreConfigurationUtils.asMap(casProperties.withHolder(), filters);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final SyncopeAuthModuleConf conf,
-            final String address) {
-
-        SyncopeAuthenticationProperties props = new SyncopeAuthenticationProperties();
-        props.setName(authModule);
-        props.setDomain(conf.getDomain());
-        props.setUrl(StringUtils.substringBefore(address, "/rest"));
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().setSyncope(props);
-
-        SimpleFilterProvider filterProvider = getParentCasFilterProvider();
-        filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                        CasCoreConfigurationUtils.getPropertyName(
-                                AuthenticationProperties.class,
-                                AuthenticationProperties::getSyncope)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final StaticAuthModuleConf conf) {
-
-        AcceptAuthenticationProperties props = new AcceptAuthenticationProperties();
-        props.setName(authModule);
-        String users = conf.getUsers().entrySet().stream().
-                map(entry -> entry.getKey() + "::" + entry.getValue()).
-                collect(Collectors.joining(","));
-        props.setUsers(users);
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().setAccept(props);
-
-        SimpleFilterProvider filterProvider = getParentCasFilterProvider();
-        filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                        CasCoreConfigurationUtils.getPropertyName(
-                                AuthenticationProperties.class,
-                                AuthenticationProperties::getAccept)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final LDAPAuthModuleConf conf) {
-
-        LdapAuthenticationProperties props = new LdapAuthenticationProperties();
-        props.setName(authModule);
-        props.setLdapUrl(conf.getLdapUrl());
-        props.setBaseDn(conf.getBaseDn());
-        props.setSearchFilter(conf.getSearchFilter());
-        props.setBindDn(conf.getBindDn());
-        props.setBindCredential(conf.getBindCredential());
-        if (StringUtils.isNotBlank(conf.getBindDn()) && StringUtils.isNotBlank(conf.getBindCredential())) {
-            props.setType(AbstractLdapAuthenticationProperties.AuthenticationTypes.AUTHENTICATED);
-        }
-        props.setPrincipalAttributeId(conf.getUserIdAttribute());
-        props.setSubtreeSearch(conf.isSubtreeSearch());
-        props.setPrincipalAttributeList(conf.getPrincipalAttributeList());
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getLdap().add(props);
-
-        SimpleFilterProvider filterProvider = getParentCasFilterProvider();
-        filterProvider.addFilter(
-                AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                        CasCoreConfigurationUtils.getPropertyName(
-                                AuthenticationProperties.class,
-                                AuthenticationProperties::getLdap)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    @SuppressWarnings("deprecation")
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final DuoMfaAuthModuleConf conf) {
-
-        DuoSecurityMultifactorAuthenticationProperties props = new DuoSecurityMultifactorAuthenticationProperties();
-        props.setName(authModule);
-        props.setDuoApiHost(conf.getApiHost());
-        props.setDuoApplicationKey(conf.getApplicationKey());
-        props.setDuoIntegrationKey(conf.getIntegrationKey());
-        props.setDuoSecretKey(conf.getSecretKey());
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getMfa().setDuo(List.of(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::getDuo)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final SimpleMfaAuthModuleConf conf) {
+    protected Map<String, Object> index(final Map<String, Object> map, final Map<String, Integer> prefixes) {
+        Map<String, Object> indexed = map;
 
-        CasSimpleMultifactorAuthenticationProperties props =
-                new CasSimpleMultifactorAuthenticationProperties();
+        if (!map.isEmpty()) {
+            String prefix = map.keySet().iterator().next();
+            if (prefix.contains("[]")) {
+                prefix = StringUtils.substringBefore(prefix, "[]");
+                Integer index = prefixes.getOrDefault(prefix, 0);
 
-        props.setName(authModule);
-        props.setTokenLength(conf.getTokenLength());
-        props.setTimeToKillInSeconds(conf.getTimeToKillInSeconds());
-        props.getMail().setAttributeName(conf.getEmailAttribute());
-        props.getMail().setFrom(conf.getEmailFrom());
-        props.getMail().setSubject(conf.getEmailSubject());
-        props.getMail().setText(conf.getEmailText());
+                indexed = map.entrySet().stream().
+                        map(e -> Pair.of(e.getKey().replace("[]", "[" + index + "]"), e.getValue())).
+                        collect(Collectors.toMap(Pair::getKey, Pair::getValue));
 
-        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);
+                prefixes.put(prefix, index + 1);
             }
         }
-        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)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final GoogleMfaAuthModuleConf conf) {
-
-        GoogleAuthenticatorMultifactorProperties props = new GoogleAuthenticatorMultifactorProperties();
-        props.setName(authModule);
-        props.getCore().setIssuer(conf.getIssuer());
-        props.getCore().setCodeDigits(conf.getCodeDigits());
-        props.getCore().setLabel(conf.getLabel());
-        props.getCore().setTimeStepSize(conf.getTimeStepSize());
-        props.getCore().setWindowSize(conf.getWindowSize());
-
-        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());
-            props.setLdap(ldapProps);
-        }
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getMfa().setGauth(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::getGauth)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final U2FAuthModuleConf conf) {
-
-        U2FMultifactorAuthenticationProperties props = new U2FMultifactorAuthenticationProperties();
-        props.setName(authModule);
-        props.getCore().setExpireDevices(conf.getExpireDevices());
-        props.getCore().setExpireDevicesTimeUnit(TimeUnit.valueOf(conf.getExpireDevicesTimeUnit()));
-        props.getCore().setExpireRegistrations(conf.getExpireRegistrations());
-        props.getCore().setExpireRegistrationsTimeUnit(TimeUnit.valueOf(conf.getExpireRegistrationsTimeUnit()));
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getMfa().setU2f(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::getU2f)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final JaasAuthModuleConf conf) {
-
-        JaasAuthenticationProperties props = new JaasAuthenticationProperties();
-        props.setName(authModule);
-        props.setLoginConfigType(conf.getLoginConfigType());
-        props.setKerberosKdcSystemProperty(conf.getKerberosKdcSystemProperty());
-        props.setKerberosRealmSystemProperty(conf.getKerberosRealmSystemProperty());
-        props.setLoginConfigType(conf.getLoginConfigurationFile());
-        props.setRealm(conf.getRealm());
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getJaas().add(props);
-
-        SimpleFilterProvider filterProvider = getParentCasFilterProvider();
-        filterProvider.addFilter(AuthenticationProperties.class.getSimpleName(),
-                SimpleBeanPropertyFilter.filterOutAllExcept(
-                        CasCoreConfigurationUtils.getPropertyName(
-                                AuthenticationProperties.class,
-                                AuthenticationProperties::getJaas)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final JDBCAuthModuleConf conf) {
-
-        QueryJdbcAuthenticationProperties props = new QueryJdbcAuthenticationProperties();
-        props.setName(authModule);
-        props.setSql(conf.getSql());
-        props.setFieldDisabled(conf.getFieldDisabled());
-        props.setFieldExpired(conf.getFieldExpired());
-        props.setFieldPassword(conf.getFieldPassword());
-        props.setDialect(conf.getDialect());
-        props.setDriverClass(conf.getDriverClass());
-        props.setPassword(conf.getPassword());
-        props.setUrl(conf.getUrl());
-        props.setUser(conf.getUser());
-        props.setPrincipalAttributeList(conf.getPrincipalAttributeList());
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getJdbc().getQuery().add(props);
-
-        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)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final OIDCAuthModuleConf conf) {
-
-        Pac4jGenericOidcClientProperties props = new Pac4jGenericOidcClientProperties();
-        props.setId(conf.getId());
-        props.setCustomParams(conf.getCustomParams());
-        props.setDiscoveryUri(conf.getDiscoveryUri());
-        props.setMaxClockSkew(conf.getMaxClockSkew());
-        props.setClientName(authModule);
-        props.setPreferredJwsAlgorithm(conf.getPreferredJwsAlgorithm());
-        props.setResponseMode(conf.getResponseMode());
-        props.setResponseType(conf.getResponseType());
-        props.setScope(conf.getScope());
-        props.setSecret(conf.getSecret());
-        props.setPrincipalAttributeId(conf.getUserIdAttribute());
-        Pac4jOidcClientProperties client = new Pac4jOidcClientProperties();
-        client.setGeneric(props);
-
-        CasConfigurationProperties casProperties = new CasConfigurationProperties();
-        casProperties.getAuthn().getPac4j().getOidc().add(client);
-
-        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)));
-        return filterCasProperties(casProperties, filterProvider);
-    }
-
-    protected Map<String, Object> mapAuthModule(
-            final String authModule,
-            final SAML2IdPAuthModuleConf conf) {
-
-        Pac4jSamlClientProperties props = new Pac4jSamlClientProperties();
-        props.setClientName(authModule);
-        props.setAcceptedSkew(conf.getAcceptedSkew());
-        props.setAssertionConsumerServiceIndex(conf.getAssertionConsumerServiceIndex());
-        props.setAttributeConsumingServiceIndex(conf.getAttributeConsumingServiceIndex());
-        props.setAuthnContextClassRef(conf.getAuthnContextClassRefs());
-        props.setAuthnContextComparisonType(conf.getAuthnContextComparisonType());
-        props.setBlockedSignatureSigningAlgorithms(conf.getBlockedSignatureSigningAlgorithms());
-        props.setDestinationBinding(conf.getDestinationBinding().getUri());
-        props.setIdentityProviderMetadataPath(conf.getIdentityProviderMetadataPath());
-        props.setKeystoreAlias(conf.getKeystoreAlias());
-        props.setKeystorePassword(conf.getKeystorePassword());
-        props.setMaximumAuthenticationLifetime(conf.getMaximumAuthenticationLifetime());
-        props.setNameIdPolicyFormat(conf.getNameIdPolicyFormat());
-        props.setPrivateKeyPassword(conf.getPrivateKeyPassword());
-        props.setProviderName(conf.getProviderName());
-        props.setServiceProviderEntityId(conf.getServiceProviderEntityId());
-        props.setSignatureAlgorithms(conf.getSignatureAlgorithms());
-        props.setSignatureCanonicalizationAlgorithm(conf.getSignatureCanonicalizationAlgorithm());
-        props.setSignatureReferenceDigestMethods(conf.getSignatureReferenceDigestMethods());
-        props.setPrincipalAttributeId(conf.getUserIdAttribute());
-        props.setNameIdPolicyAllowCreate(StringUtils.isBlank(conf.getNameIdPolicyAllowCreate())
-                ? 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)));
-        return filterCasProperties(casProperties, filterProvider);
+        return indexed;
     }
 
     @Override
@@ -469,39 +77,27 @@ public class WAPropertySourceLocator implements PropertySourceLocator {
 
         LOG.info("Bootstrapping WA configuration");
         Map<String, Object> properties = new TreeMap<>();
+        Map<String, Integer> prefixes = new HashMap<>();
 
         syncopeClient.getService(AuthModuleService.class).list().forEach(authModuleTO -> {
-            AuthModuleConf authConf = authModuleTO.getConf();
             LOG.debug("Mapping auth module {} ", authModuleTO.getKey());
 
-            if (authConf instanceof LDAPAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (LDAPAuthModuleConf) authConf));
-            } else if (authConf instanceof StaticAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (StaticAuthModuleConf) authConf));
-            } else if (authConf instanceof SyncopeAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(),
-                        (SyncopeAuthModuleConf) authConf, syncopeClient.getAddress()));
-            } else if (authConf instanceof GoogleMfaAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (GoogleMfaAuthModuleConf) authConf));
-            } else if (authConf instanceof SimpleMfaAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (SimpleMfaAuthModuleConf) authConf));
-            } else if (authConf instanceof DuoMfaAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (DuoMfaAuthModuleConf) authConf));
-            } else if (authConf instanceof JaasAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (JaasAuthModuleConf) authConf));
-            } else if (authConf instanceof JDBCAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (JDBCAuthModuleConf) authConf));
-            } else if (authConf instanceof OIDCAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (OIDCAuthModuleConf) authConf));
-            } else if (authConf instanceof SAML2IdPAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (SAML2IdPAuthModuleConf) authConf));
-            } else if (authConf instanceof U2FAuthModuleConf) {
-                properties.putAll(mapAuthModule(authModuleTO.getKey(), (U2FAuthModuleConf) authConf));
-            }
+            Map<String, Object> map = authModuleTO.getConf().map(
+                    new AuthModulePropertySourceMapper(syncopeClient.getAddress(), authModuleTO));
+            properties.putAll(index(map, prefixes));
+        });
+
+        syncopeClient.getService(AttrRepoService.class).list().forEach(attrRepoTO -> {
+            LOG.debug("Mapping attr repo {} ", attrRepoTO.getKey());
+
+            Map<String, Object> map = attrRepoTO.getConf().map(
+                    new AttrRepoPropertySourceMapper(syncopeClient.getAddress(), attrRepoTO));
+            properties.putAll(index(map, prefixes));
         });
 
         syncopeClient.getService(WAConfigService.class).list().forEach(attr -> properties.put(
                 attr.getSchema(), attr.getValues().stream().collect(Collectors.joining(","))));
+
         LOG.debug("Collected WA properties: {}", properties);
         return new MapPropertySource(getClass().getName(), properties);
     }
diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml
index 61d2c2b2df..43862b245d 100644
--- a/wa/starter/pom.xml
+++ b/wa/starter/pom.xml
@@ -138,6 +138,10 @@ under the License.
       <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-support-actions</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-jpa-util</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-support-ldap</artifactId>
@@ -428,6 +432,11 @@ under the License.
           <artifactId>syncope-common-keymaster-client-self</artifactId>
           <version>${project.version}</version>
         </dependency>
+
+        <dependency>
+          <groupId>com.h2database</groupId>
+          <artifactId>h2</artifactId>
+        </dependency>        
       </dependencies>
 
       <build>
@@ -439,6 +448,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
                 -Xdebug -Xrunjdwp:transport=dt_socket,address=8001,server=y,suspend=n
               </jvmArguments>
               <profiles>
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AttrReleaseMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AttrReleaseMapper.java
index 8a63943fac..8517c46c5e 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AttrReleaseMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/AttrReleaseMapper.java
@@ -18,11 +18,12 @@
  */
 package org.apache.syncope.wa.starter.mapping;
 
+import java.util.Map;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 
 @FunctionalInterface
 public interface AttrReleaseMapper {
 
-    RegisteredServiceAttributeReleasePolicy build(AttrReleasePolicyTO policy);
+    RegisteredServiceAttributeReleasePolicy build(AttrReleasePolicyTO policy, Map<String, Object> releaseAttrs);
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
index cbaa5f3977..e1146ef929 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/CASSPClientAppTOMapper.java
@@ -26,12 +26,14 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.springframework.context.ConfigurableApplicationContext;
 
 @ClientAppMapFor(clientAppClass = CASSPClientAppTO.class)
 public class CASSPClientAppTOMapper extends AbstractClientAppMapper {
 
     @Override
     public RegisteredService map(
+            final ConfigurableApplicationContext ctx,
             final WAClientApp clientApp,
             final RegisteredServiceAuthenticationPolicy authPolicy,
             final RegisteredServiceMultifactorPolicy mfaPolicy,
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
index acb38a6f9e..dfd9c798d4 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/ClientAppMapper.java
@@ -24,11 +24,13 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
+import org.springframework.context.ConfigurableApplicationContext;
 
 @FunctionalInterface
 public interface ClientAppMapper {
 
     RegisteredService map(
+            ConfigurableApplicationContext ctx,
             WAClientApp clientApp,
             RegisteredServiceAuthenticationPolicy authPolicy,
             RegisteredServiceMultifactorPolicy mfaPolicy,
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAttrReleaseMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAttrReleaseMapper.java
index 9b39c95d41..ae508d6e96 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAttrReleaseMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/DefaultAttrReleaseMapper.java
@@ -19,11 +19,19 @@
 package org.apache.syncope.wa.starter.mapping;
 
 import java.util.HashSet;
+import java.util.Map;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
 import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
+import org.apereo.cas.authentication.principal.DefaultPrincipalAttributesRepository;
+import org.apereo.cas.authentication.principal.cache.AbstractPrincipalAttributesRepository;
+import org.apereo.cas.authentication.principal.cache.CachingPrincipalAttributesRepository;
+import org.apereo.cas.configuration.model.core.authentication.PrincipalAttributesCoreProperties;
+import org.apereo.cas.services.AbstractRegisteredServiceAttributeReleasePolicy;
+import org.apereo.cas.services.ChainingAttributeReleasePolicy;
 import org.apereo.cas.services.DenyAllAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
+import org.apereo.cas.services.ReturnMappedAttributeReleasePolicy;
 import org.apereo.cas.services.consent.DefaultRegisteredServiceConsentPolicy;
 import org.apereo.cas.util.model.TriStateBoolean;
 
@@ -31,23 +39,62 @@ import org.apereo.cas.util.model.TriStateBoolean;
 public class DefaultAttrReleaseMapper implements AttrReleaseMapper {
 
     @Override
-    public RegisteredServiceAttributeReleasePolicy build(final AttrReleasePolicyTO policy) {
-        DefaultAttrReleasePolicyConf aarpc = (DefaultAttrReleasePolicyConf) policy.getConf();
+    public RegisteredServiceAttributeReleasePolicy build(
+            final AttrReleasePolicyTO policy, final Map<String, Object> releaseAttrs) {
 
-        if (aarpc.getAllowedAttrs().isEmpty()) {
-            return new DenyAllAttributeReleasePolicy();
+        DefaultAttrReleasePolicyConf conf = (DefaultAttrReleasePolicyConf) policy.getConf();
+
+        ReturnMappedAttributeReleasePolicy returnMapped = null;
+        if (!releaseAttrs.isEmpty()) {
+            returnMapped = new ReturnMappedAttributeReleasePolicy(releaseAttrs);
+        }
+
+        ReturnAllowedAttributeReleasePolicy returnAllowed = null;
+        if (!conf.getAllowedAttrs().isEmpty()) {
+            returnAllowed = new ReturnAllowedAttributeReleasePolicy();
+        }
+
+        AbstractRegisteredServiceAttributeReleasePolicy attributeReleasePolicy;
+        if (returnMapped == null && returnAllowed == null) {
+            attributeReleasePolicy = new DenyAllAttributeReleasePolicy();
+        } else if (returnMapped != null) {
+            attributeReleasePolicy = returnMapped;
+        } else {
+            attributeReleasePolicy = returnAllowed;
         }
 
         DefaultRegisteredServiceConsentPolicy consentPolicy = new DefaultRegisteredServiceConsentPolicy(
-                new HashSet<>(aarpc.getExcludedAttrs()), new HashSet<>(aarpc.getIncludeOnlyAttrs()));
+                new HashSet<>(conf.getExcludedAttrs()), new HashSet<>(conf.getIncludeOnlyAttrs()));
         consentPolicy.setOrder(policy.getOrder());
-        consentPolicy.setStatus(
-                policy.getStatus() == null ? TriStateBoolean.UNDEFINED
+        consentPolicy.setStatus(policy.getStatus() == null
+                ? TriStateBoolean.UNDEFINED
                 : TriStateBoolean.fromBoolean(policy.getStatus()));
-
-        ReturnAllowedAttributeReleasePolicy attributeReleasePolicy = new ReturnAllowedAttributeReleasePolicy();
-        attributeReleasePolicy.setAllowedAttributes((aarpc.getAllowedAttrs()));
         attributeReleasePolicy.setConsentPolicy(consentPolicy);
+
+        if (conf.getPrincipalIdAttr() != null) {
+            attributeReleasePolicy.setPrincipalIdAttribute(conf.getPrincipalIdAttr());
+        }
+
+        if (conf.getPrincipalAttrRepoConf() != null && !conf.getPrincipalAttrRepoConf().getAttrRepos().isEmpty()) {
+            DefaultAttrReleasePolicyConf.PrincipalAttrRepoConf parc = conf.getPrincipalAttrRepoConf();
+
+            AbstractPrincipalAttributesRepository par = parc.getExpiration() > 0
+                    ? new CachingPrincipalAttributesRepository(parc.getTimeUnit().name(), parc.getExpiration())
+                    : new DefaultPrincipalAttributesRepository();
+
+            par.setMergingStrategy(
+                    PrincipalAttributesCoreProperties.MergingStrategyTypes.valueOf(parc.getMergingStrategy().name()));
+            par.setIgnoreResolvedAttributes(par.isIgnoreResolvedAttributes());
+            par.setAttributeRepositoryIds(new HashSet<>(parc.getAttrRepos()));
+            attributeReleasePolicy.setPrincipalAttributesRepository(par);
+        }
+
+        if (returnMapped != null && returnAllowed != null) {
+            ChainingAttributeReleasePolicy chain = new ChainingAttributeReleasePolicy();
+            chain.addPolicies(returnMapped, returnAllowed);
+            return chain;
+        }
+
         return attributeReleasePolicy;
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
index 614df260b4..8a03a25ba6 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java
@@ -41,21 +41,16 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
-import org.apereo.cas.services.ReturnMappedAttributeReleasePolicy;
-import org.apereo.cas.util.spring.ApplicationContextProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
 
 @ClientAppMapFor(clientAppClass = OIDCRPClientAppTO.class)
 public class OIDCRPClientAppTOMapper extends AbstractClientAppMapper {
 
-    private static final Logger LOG = LoggerFactory.getLogger(OIDCRPClientAppTOMapper.class);
-
     private static final String CUSTOM_SCOPE = "syncope";
 
     @Override
     public RegisteredService map(
+            final ConfigurableApplicationContext ctx,
             final WAClientApp clientApp,
             final RegisteredServiceAuthenticationPolicy authPolicy,
             final RegisteredServiceMultifactorPolicy mfaPolicy,
@@ -85,13 +80,12 @@ public class OIDCRPClientAppTOMapper extends AbstractClientAppMapper {
         }
         service.setLogoutUrl(rp.getLogoutUri());
 
-        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
-        if (attributeReleasePolicy != null) {
-            ChainingAttributeReleasePolicy chain = new ChainingAttributeReleasePolicy();
-            if (attributeReleasePolicy instanceof ReturnMappedAttributeReleasePolicy) {
-                chain.addPolicy(attributeReleasePolicy);
-            } else {
-                chain.addPolicy(new ReturnMappedAttributeReleasePolicy(clientApp.getReleaseAttrs()));
+        ChainingAttributeReleasePolicy chain;
+        if (attributeReleasePolicy instanceof ChainingAttributeReleasePolicy) {
+            chain = (ChainingAttributeReleasePolicy) attributeReleasePolicy;
+        } else {
+            chain = new ChainingAttributeReleasePolicy();
+            if (attributeReleasePolicy != null) {
                 chain.addPolicy(attributeReleasePolicy);
             }
 
@@ -107,26 +101,21 @@ public class OIDCRPClientAppTOMapper extends AbstractClientAppMapper {
             customClaims.removeAll(OidcAddressScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
             customClaims.removeAll(OidcPhoneScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
             if (!customClaims.isEmpty()) {
-                ApplicationContext ctx = ApplicationContextProvider.getApplicationContext();
-                if (ctx == null) {
-                    LOG.warn("Could not locate the application context to add custom claims {}", customClaims);
-                } else {
-                    CasConfigurationProperties properties = ctx.getBean(CasConfigurationProperties.class);
-                    List<String> supportedClaims = properties.getAuthn().getOidc().getDiscovery().getClaims();
-                    if (!supportedClaims.containsAll(customClaims)) {
-                        properties.getAuthn().getOidc().getDiscovery().setClaims(
-                                Stream.concat(supportedClaims.stream(), customClaims.stream()).
-                                        distinct().collect(Collectors.toList()));
-                    }
-
-                    chain.addPolicy(new OidcCustomScopeAttributeReleasePolicy(
-                            CUSTOM_SCOPE, customClaims.stream().collect(Collectors.toList())));
+                CasConfigurationProperties properties = ctx.getBean(CasConfigurationProperties.class);
+                List<String> supportedClaims = properties.getAuthn().getOidc().getDiscovery().getClaims();
+                if (!supportedClaims.containsAll(customClaims)) {
+                    properties.getAuthn().getOidc().getDiscovery().setClaims(
+                            Stream.concat(supportedClaims.stream(), customClaims.stream()).
+                                    distinct().collect(Collectors.toList()));
                 }
-            }
 
-            service.setAttributeReleasePolicy(chain);
+                chain.addPolicy(new OidcCustomScopeAttributeReleasePolicy(
+                        CUSTOM_SCOPE, customClaims.stream().collect(Collectors.toList())));
+            }
         }
 
+        setPolicies(service, authPolicy, mfaPolicy, accessStrategy, chain);
+
         return service;
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
index d316e02b9a..ef550ad377 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/RegisteredServiceMapper.java
@@ -21,6 +21,8 @@ package org.apache.syncope.wa.starter.mapping;
 import java.util.Map;
 import java.util.Optional;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
+import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf;
 import org.apache.syncope.common.lib.wa.WAClientApp;
 import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
 import org.apereo.cas.services.RegisteredService;
@@ -28,7 +30,6 @@ import org.apereo.cas.services.RegisteredServiceAccessStrategy;
 import org.apereo.cas.services.RegisteredServiceAttributeReleasePolicy;
 import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
-import org.apereo.cas.services.ReturnMappedAttributeReleasePolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.ObjectProvider;
@@ -96,21 +97,23 @@ public class RegisteredServiceMapper {
             AccessMapper accessPolicyConfMapper = accessPolicyConfMappers.get(
                     clientApp.getAccessPolicy().getConf().getClass().getName());
             accessStrategy = Optional.ofNullable(accessPolicyConfMapper).
-                    map(mapper -> mapper.build(clientApp.getAccessPolicy())).orElse(null);
+                    map(mapper -> mapper.build(clientApp.getAccessPolicy())).
+                    orElse(null);
         }
 
-        RegisteredServiceAttributeReleasePolicy attributeReleasePolicy = null;
-        if (!clientApp.getReleaseAttrs().isEmpty()) {
-            if (clientApp.getAttrReleasePolicy() == null) {
-                attributeReleasePolicy = new ReturnMappedAttributeReleasePolicy(clientApp.getReleaseAttrs());
-            } else {
-                AttrReleaseMapper attrReleasePolicyConfMapper = attrReleasePolicyConfMappers.get(
-                        clientApp.getAttrReleasePolicy().getConf().getClass().getName());
-                attributeReleasePolicy = Optional.ofNullable(attrReleasePolicyConfMapper).
-                        map(mapper -> mapper.build(clientApp.getAttrReleasePolicy())).orElse(null);
-            }
-        }
-
-        return clientAppMapper.map(clientApp, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
+        AttrReleasePolicyTO attrReleasePolicyTO = Optional.ofNullable(clientApp.getAttrReleasePolicy()).
+                orElseGet(() -> {
+                    AttrReleasePolicyTO arpTO = new AttrReleasePolicyTO();
+                    arpTO.setConf(new DefaultAttrReleasePolicyConf());
+                    return arpTO;
+                });
+        AttrReleaseMapper attrReleasePolicyConfMapper = attrReleasePolicyConfMappers.get(
+                attrReleasePolicyTO.getConf().getClass().getName());
+        RegisteredServiceAttributeReleasePolicy attributeReleasePolicy =
+                Optional.ofNullable(attrReleasePolicyConfMapper).
+                        map(mapper -> mapper.build(attrReleasePolicyTO, clientApp.getReleaseAttrs())).
+                        orElse(null);
+
+        return clientAppMapper.map(ctx, clientApp, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy);
     }
 }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
index 464677115b..d90dd0d510 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/SAML2SPClientAppTOMapper.java
@@ -29,12 +29,14 @@ import org.apereo.cas.services.RegisteredServiceAuthenticationPolicy;
 import org.apereo.cas.services.RegisteredServiceMultifactorPolicy;
 import org.apereo.cas.support.saml.services.SamlRegisteredService;
 import org.apereo.cas.util.model.TriStateBoolean;
+import org.springframework.context.ConfigurableApplicationContext;
 
 @ClientAppMapFor(clientAppClass = SAML2SPClientAppTO.class)
 public class SAML2SPClientAppTOMapper extends AbstractClientAppMapper {
 
     @Override
     public RegisteredService map(
+            final ConfigurableApplicationContext ctx,
             final WAClientApp clientApp,
             final RegisteredServiceAuthenticationPolicy authPolicy,
             final RegisteredServiceMultifactorPolicy mfaPolicy,
diff --git a/wa/starter/src/main/resources/wa.properties b/wa/starter/src/main/resources/wa.properties
index c45f82a376..f994a60f5e 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,authenticationHandlers,authenticationPolicies
+management.endpoints.web.exposure.include=info,health,env,loggers,ssoSessions,registeredServices,refresh,authenticationHandlers,authenticationPolicies,resolveAttributes
 management.endpoint.health.show-details=ALWAYS
 spring.cloud.discovery.client.health-indicator.enabled=false
 
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
index 43e9731040..5bc78c0f74 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
@@ -44,6 +44,7 @@ import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
 import org.apereo.cas.services.AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria;
 import org.apereo.cas.services.ChainingAttributeReleasePolicy;
+import org.apereo.cas.services.DenyAllAttributeReleasePolicy;
 import org.apereo.cas.services.OidcRegisteredService;
 import org.apereo.cas.services.RegisteredService;
 import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;
@@ -117,6 +118,8 @@ public class WAServiceRegistryTest extends AbstractTest {
         if (withAttrReleasePolicy) {
             DefaultAttrReleasePolicyConf attrReleasePolicyConf = new DefaultAttrReleasePolicyConf();
             attrReleasePolicyConf.getAllowedAttrs().add("cn");
+            attrReleasePolicyConf.getPrincipalAttrRepoConf().getAttrRepos().add("TestAttrRepo");
+
             AttrReleasePolicyTO attrReleasePolicy = new AttrReleasePolicyTO();
             attrReleasePolicy.setConf(attrReleasePolicyConf);
             waClientApp.setAttrReleasePolicy(attrReleasePolicy);
@@ -200,6 +203,6 @@ public class WAServiceRegistryTest extends AbstractTest {
         assertEquals(5, load.size());
 
         found = servicesManager.findServiceBy(clientAppId);
-        assertTrue(found.getAttributeReleasePolicy() instanceof ReturnAllowedAttributeReleasePolicy);
+        assertTrue(found.getAttributeReleasePolicy() instanceof DenyAllAttributeReleasePolicy);
     }
 }
diff --git a/wa/starter/src/test/resources/debug/wa-debug.properties b/wa/starter/src/test/resources/debug/wa-debug.properties
index 243d97d38e..bad535ac69 100644
--- a/wa/starter/src/test/resources/debug/wa-debug.properties
+++ b/wa/starter/src/test/resources/debug/wa-debug.properties
@@ -16,7 +16,8 @@
 # under the License.
 spring.main.allow-circular-references=true
 
-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}