[SYNCOPE-1618] Spring Boot configuration refactoring (#280)

diff --git a/.github/workflows/crosschecks.yml b/.github/workflows/crosschecks.yml
index 2f0d504..55a5f4d 100644
--- a/.github/workflows/crosschecks.yml
+++ b/.github/workflows/crosschecks.yml
@@ -59,4 +59,4 @@
     - name: Test
       run: mvn -T 1C clean test '-Dinvoker.streamLogs=true' '-Dmodernizer.skip=true' '-Dianal.phase=none' '-Drat.skip=true' '-Dcheckstyle.skip=true' '-Dsass.skip=true'
     - name: Verify
-      run: mvn -f fit/core-reference/pom.xml verify '-Dit.test=RESTITCase' '-Dinvoker.streamLogs=true' '-Dmodernizer.skip=true' '-Dianal.phase=none' '-Drat.skip=true' '-Dcheckstyle.skip=true' '-Djacoco.skip=true'
+      run: mvn -f fit/core-reference/pom.xml verify '-Dit.test=RESTITCase' '-Dinvoker.streamLogs=true' '-Dmodernizer.skip=true' '-Drat.skip=true' '-Dcheckstyle.skip=true' '-Djacoco.skip=true'
diff --git a/.github/workflows/fit_Elasticsearch.yml b/.github/workflows/fit_Elasticsearch.yml
index 9298100..5b983a2 100644
--- a/.github/workflows/fit_Elasticsearch.yml
+++ b/.github/workflows/fit_Elasticsearch.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Elasticsearch / H2 / JSON'
-      run: mvn -f fit/core-reference/pom.xml -P elasticsearch-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P elasticsearch-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/fit_Payara.yml b/.github/workflows/fit_Payara.yml
index d1ba5c5..4440fe8 100644
--- a/.github/workflows/fit_Payara.yml
+++ b/.github/workflows/fit_Payara.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Payara / H2 / JSON'
-      run: mvn -f fit/core-reference/pom.xml -P payara-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P payara-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/fit_Tomcat_H2_JSON.yml b/.github/workflows/fit_Tomcat_H2_JSON.yml
index bb5e737..b701b03 100644
--- a/.github/workflows/fit_Tomcat_H2_JSON.yml
+++ b/.github/workflows/fit_Tomcat_H2_JSON.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Tomcat / H2 / JSON'
-      run: mvn -f fit/core-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/fit_Tomcat_H2_XML.yml b/.github/workflows/fit_Tomcat_H2_XML.yml
index 3729b14..5064ca4 100644
--- a/.github/workflows/fit_Tomcat_H2_XML.yml
+++ b/.github/workflows/fit_Tomcat_H2_XML.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Tomcat / H2 / XML'
-      run: mvn -f fit/core-reference/pom.xml verify -Djaxrs.content.type=application/xml -Dit.test=org.apache.syncope.fit.core.*ITCase -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml verify -Djaxrs.content.type=application/xml -Dit.test=org.apache.syncope.fit.core.*ITCase -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/fit_Tomcat_H2_YAML.yml b/.github/workflows/fit_Tomcat_H2_YAML.yml
index 8591b47..f3b399c 100644
--- a/.github/workflows/fit_Tomcat_H2_YAML.yml
+++ b/.github/workflows/fit_Tomcat_H2_YAML.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Tomcat / H2 / YAML'
-      run: mvn -f fit/core-reference/pom.xml verify -Djaxrs.content.type=application/yaml -Dit.test=org.apache.syncope.fit.core.*ITCase -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml verify -Djaxrs.content.type=application/yaml -Dit.test=org.apache.syncope.fit.core.*ITCase -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/fit_WA_SAML2PS4UI_OIDCC4UI.yml b/.github/workflows/fit_WA_SAML2PS4UI_OIDCC4UI.yml
index fcdd2c5..99a7d7a 100644
--- a/.github/workflows/fit_WA_SAML2PS4UI_OIDCC4UI.yml
+++ b/.github/workflows/fit_WA_SAML2PS4UI_OIDCC4UI.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'WA / SAML2SP4UI, OIDCC4UI'
-      run: mkdir -p fit/core-reference/target/test-classes && cp fit/core-reference/src/test/resources/saml.keystore.jks fit/core-reference/target/test-classes && mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.ui.*ITCase
+      run: mkdir -p fit/core-reference/target/test-classes && cp fit/core-reference/src/test/resources/saml.keystore.jks fit/core-reference/target/test-classes && mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.ui.*ITCase
diff --git a/.github/workflows/fit_WA_SRA_CASClient.yml b/.github/workflows/fit_WA_SRA_CASClient.yml
index 13940f5..61cec56 100644
--- a/.github/workflows/fit_WA_SRA_CASClient.yml
+++ b/.github/workflows/fit_WA_SRA_CASClient.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'WA / SRA / CAS Client'
-      run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.CASSRAITCase
+      run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.CASSRAITCase
diff --git a/.github/workflows/fit_WA_SRA_OAuth2.yml b/.github/workflows/fit_WA_SRA_OAuth2.yml
index 86bbc37..58e4059 100644
--- a/.github/workflows/fit_WA_SRA_OAuth2.yml
+++ b/.github/workflows/fit_WA_SRA_OAuth2.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'WA / SRA / OAuth 2.0'
-      run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.OAUTH2SRAITCase
+      run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.OAUTH2SRAITCase
diff --git a/.github/workflows/fit_WA_SRA_OIDC.yml b/.github/workflows/fit_WA_SRA_OIDC.yml
index e664af7..662de6a 100644
--- a/.github/workflows/fit_WA_SRA_OIDC.yml
+++ b/.github/workflows/fit_WA_SRA_OIDC.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'WA / SRA / OpenID Connect 1.0'
-      run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.OIDCSRAITCase
+      run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.OIDCSRAITCase
diff --git a/.github/workflows/fit_WA_SRA_SAML2.yml b/.github/workflows/fit_WA_SRA_SAML2.yml
index 1874c78..f05d96a 100644
--- a/.github/workflows/fit_WA_SRA_SAML2.yml
+++ b/.github/workflows/fit_WA_SRA_SAML2.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'WA / SRA / SAML 2.0'
-      run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.SAML2SRAITCase
+      run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=org.apache.syncope.fit.sra.SAML2SRAITCase
diff --git a/.github/workflows/fit_Wildfly.yml b/.github/workflows/fit_Wildfly.yml
index f226424..7cc9e18 100644
--- a/.github/workflows/fit_Wildfly.yml
+++ b/.github/workflows/fit_Wildfly.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Wildfly / H2 / JSON'
-      run: mvn -f fit/core-reference/pom.xml -P wildfly-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P wildfly-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/fit_Zookeeper.yml b/.github/workflows/fit_Zookeeper.yml
index c65ee9a..f1724d3 100644
--- a/.github/workflows/fit_Zookeeper.yml
+++ b/.github/workflows/fit_Zookeeper.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Zookeeper / H2 / JSON'
-      run: mvn -f fit/core-reference/pom.xml -P zookeeper-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P zookeeper-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/mariadb.yml b/.github/workflows/mariadb.yml
index ef8e752..58af3e7 100644
--- a/.github/workflows/mariadb.yml
+++ b/.github/workflows/mariadb.yml
@@ -48,4 +48,4 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Integration Tests: MariaDB'
-      run: mvn -f fit/core-reference/pom.xml -P mariadb-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P mariadb-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/mysql.yml b/.github/workflows/mysql.yml
index 3e2755d..c8d5087 100644
--- a/.github/workflows/mysql.yml
+++ b/.github/workflows/mysql.yml
@@ -48,8 +48,8 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Unit Tests: MySQL JPA JSON'
-      run: mvn -f core/persistence-jpa-json/pom.xml -P mysql-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f core/persistence-jpa-json/pom.xml -P myjson -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
     - name: 'Integration Tests: MySQL'
-      run: mvn -f fit/core-reference/pom.xml -P mysql-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P mysql-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
     - name: 'Integration Tests: MySQL JPA JSON'
-      run: mvn -f fit/core-reference/pom.xml -P myjson-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P myjson-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/postgresql.yml b/.github/workflows/postgresql.yml
index d075170..c796c03 100644
--- a/.github/workflows/postgresql.yml
+++ b/.github/workflows/postgresql.yml
@@ -48,8 +48,8 @@
     - name: Build
       run: mvn -U -T 1C -P 'skipTests,all'
     - name: 'Unit Tests: PostgreSQL JPA JSON'
-      run: mvn -f core/persistence-jpa-json/pom.xml -P postgres-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f core/persistence-jpa-json/pom.xml -P pgjsonb -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
     - name: 'Integration Tests: PostgreSQL'
-      run: mvn -f fit/core-reference/pom.xml -P postgres-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P postgres-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
     - name: 'Integration Tests: PostgreSQL JPA JSON'
-      run: mvn -f fit/core-reference/pom.xml -P pgjsonb-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+      run: mvn -f fit/core-reference/pom.xml -P pgjsonb-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/archetype/pom.xml b/archetype/pom.xml
index 1315eef..499f45a 100644
--- a/archetype/pom.xml
+++ b/archetype/pom.xml
@@ -129,7 +129,7 @@
       <resource>
         <directory>../core/starter/src/main/resources</directory>
         <includes>
-          <include>application.properties</include>
+          <include>core.properties</include>
         </includes>
         <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources</targetPath>
       </resource>
@@ -157,58 +157,17 @@
         <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources/domains</targetPath>
       </resource>
       <resource>
-        <directory>../core/logic/src/main/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources</targetPath>
-      </resource>
-      <resource>
-        <directory>../core/spring/src/main/resources</directory>
-        <includes>
-          <include>security.properties</include>
-        </includes>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources</targetPath>
-      </resource>
-      <resource>
         <directory>../fit/core-reference/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources</targetPath>
         <includes>
-          <include>application-embedded.properties</include>
           <include>log4j2.xml</include>
         </includes>
       </resource>
       <resource>
-        <directory>../ext/self-keymaster/client/src/main/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources</targetPath>
-        <includes>
-          <include>keymaster.properties</include>
-        </includes>
-      </resource>
-      <resource>
-        <directory>../fit/core-reference/src/main/resources/all</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources/all</targetPath>
-        <excludes>
-          <exclude>provisioning.properties</exclude>
-        </excludes>
-      </resource>
-      <resource>
-        <directory>../ext/camel/provisioning-camel/src/main/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources/all</targetPath>
-        <includes>
-          <include>provisioning.properties</include>
-        </includes>
-      </resource>      
-      <resource>
-        <directory>../fit/core-reference/src/main/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources</targetPath>
-        <includes>
-          <include>keymaster.properties</include>
-        </includes>
-      </resource>
-      <resource>
         <directory>../fit/core-reference/src/test/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources</targetPath>
         <includes>
           <include>keystore</include>
-          <include>mail.properties</include>
         </includes>
       </resource>
       <resource>
@@ -220,127 +179,109 @@
         <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources/rest</targetPath>
       </resource>
       <resource>
+        <directory>../fit/core-reference/src/main/webapp/WEB-INF</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/webapp/WEB-INF</targetPath>
+      </resource>
+      <resource>
         <directory>../fit/core-reference/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources</targetPath>
         <includes>
-          <include>connid.properties</include>
+          <include>core-embedded.properties</include>
+          <include>core-all.properties</include>
           <include>userWorkflow.bpmn20.xml</include>
         </includes>
       </resource>
-      <resource>
-        <directory>../fit/core-reference/src/main/webapp/WEB-INF</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/webapp/WEB-INF</targetPath>
-      </resource>
             
       <resource>
         <directory>../client/idrepo/console/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/resources</targetPath>
         <includes>
-          <include>application.properties</include>
           <include>console.properties</include>
         </includes>
       </resource>
       <resource>
-        <directory>../ext/self-keymaster/client/src/main/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/resources</targetPath>
-        <includes>
-          <include>keymaster.properties</include>
-        </includes>
-      </resource>
-      <resource>
         <directory>../fit/console-reference/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/resources</targetPath>
         <includes>
-          <include>application-embedded.properties</include>
           <include>log4j2.xml</include>
         </includes>
       </resource>
       <resource>
-        <directory>../fit/console-reference/src/main/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/test/resources</targetPath>
-        <includes>
-          <include>console.properties</include>
-          <include>keymaster.properties</include>
-        </includes>
-      </resource>
-      <resource>
         <directory>../fit/console-reference/src/main/webapp/WEB-INF</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/main/webapp/WEB-INF</targetPath>
       </resource>
+      <resource>
+        <directory>../fit/console-reference/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/console/src/test/resources</targetPath>
+        <includes>
+          <include>console-embedded.properties</include>
+        </includes>
+      </resource>
       
       <resource>
         <directory>../client/idrepo/enduser/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/resources</targetPath>
         <includes>
-          <include>application.properties</include>
           <include>enduser.properties</include>
-          <include>customFormAttributes.json</include>
-          <include>customTemplate.json</include>
-        </includes>
-      </resource>
-      <resource>
-        <directory>../ext/self-keymaster/client/src/main/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/resources</targetPath>
-        <includes>
-          <include>keymaster.properties</include>
+          <include>customFormLayout.json</include>
         </includes>
       </resource>
       <resource>
         <directory>../fit/enduser-reference/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/resources</targetPath>
         <includes>
-          <include>application-embedded.properties</include>
           <include>log4j2.xml</include>
         </includes>
       </resource>
       <resource>
-        <directory>../fit/enduser-reference/src/main/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/test/resources</targetPath>
-        <includes>
-          <include>enduser.properties</include>
-          <include>customFormAttributes.json</include>
-          <include>customTemplate.json</include>
-          <include>keymaster.properties</include>
-        </includes>
-      </resource>
-      <resource>
         <directory>../fit/enduser-reference/src/main/webapp/WEB-INF</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/main/webapp/WEB-INF</targetPath>
       </resource>
+      <resource>
+        <directory>../fit/enduser-reference/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/enduser/src/test/resources</targetPath>
+        <includes>
+          <include>enduser-embedded.properties</include>
+        </includes>
+      </resource>
       
       <resource>
         <directory>../sra/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/sra/src/main/resources</targetPath>
       </resource>
       <resource>
-        <directory>../sra/src/test/resources</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/sra/src/main/resources</targetPath>
+        <directory>../fit/wa-reference/src/test/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/sra/src/test/resources</targetPath>
         <includes>
-          <include>keymaster.properties</include>          
+          <include>sra-*.properties</include>
         </includes>
       </resource>
 
       <resource>
         <directory>../wa/starter/src/main/resources</directory>
         <includes>
-          <include>application.properties</include>
           <include>cas-theme-default.properties</include>
           <include>wa.properties</include>
-          <include>log4j2.xml</include>
         </includes>
         <targetPath>${project.build.outputDirectory}/archetype-resources/wa/src/main/resources</targetPath>
       </resource>
       <resource>
+        <directory>../fit/wa-reference/src/main/webapp/WEB-INF</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/wa/src/main/webapp/WEB-INF</targetPath>
+      </resource>
+      <resource>
         <directory>../fit/wa-reference/src/main/resources</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/wa/src/main/resources</targetPath>
         <includes>
-          <include>application-embedded.properties</include>
-          <include>keymaster.properties</include>
+          <include>log4j2.xml</include>
         </includes>
       </resource>
       <resource>
-        <directory>../fit/wa-reference/src/main/webapp/WEB-INF</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/wa/src/main/webapp/WEB-INF</targetPath>
+        <directory>../fit/wa-reference/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/wa/src/test/resources</targetPath>
+        <includes>
+          <include>wa-embedded.properties</include>
+        </includes>
       </resource>
     </resources>
   </build>
diff --git a/archetype/src/main/resources/archetype-resources/console/pom.xml b/archetype/src/main/resources/archetype-resources/console/pom.xml
index 9a5b879..85f1c8c 100644
--- a/archetype/src/main/resources/archetype-resources/console/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/console/pom.xml
@@ -43,10 +43,14 @@
       <groupId>org.apache.syncope.client.idm</groupId>
       <artifactId>syncope-client-idm-console</artifactId>
     </dependency>
-    
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.client.am</groupId>
+      <artifactId>syncope-client-am-console</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
diff --git a/archetype/src/main/resources/archetype-resources/core/pom.xml b/archetype/src/main/resources/archetype-resources/core/pom.xml
index b7fe116..12c5d22 100644
--- a/archetype/src/main/resources/archetype-resources/core/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/core/pom.xml
@@ -53,13 +53,10 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-rest-cxf</artifactId>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-self-keymaster-starter</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-persistence-jpa</artifactId>
-    </dependency>
+
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
       <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
diff --git a/archetype/src/main/resources/archetype-resources/enduser/pom.xml b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
index ac8a728..c6cf90c 100644
--- a/archetype/src/main/resources/archetype-resources/enduser/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
@@ -43,10 +43,10 @@
       <groupId>org.apache.syncope.client.idrepo</groupId>
       <artifactId>syncope-client-idrepo-enduser</artifactId>
     </dependency>
-    
+
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
diff --git a/archetype/src/main/resources/archetype-resources/fit/pom.xml b/archetype/src/main/resources/archetype-resources/fit/pom.xml
index 03437a6..83c6528 100644
--- a/archetype/src/main/resources/archetype-resources/fit/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/fit/pom.xml
@@ -33,11 +33,18 @@
   <packaging>war</packaging>
 
   <properties>
+    <spring.profiles.active>embedded</spring.profiles.active>
     <exec.skip>true</exec.skip>
   </properties>
 
   <dependencies>
     <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
       <groupId>org.apache.syncope.fit</groupId>
       <artifactId>syncope-fit-build-tools</artifactId>
       <version>${syncope.version}</version>
@@ -128,28 +135,22 @@
                 <copy todir="../core/target/syncope/WEB-INF/classes/domains">
                   <fileset dir="../core/target/test-classes/domains"/>
                 </copy>
-                <copy file="../core/target/test-classes/connid.properties" 
+                <copy file="../core/target/test-classes/core-embedded.properties" 
                       todir="../core/target/syncope/WEB-INF/classes" 
                       overwrite="true"/>
-                <copy file="../core/target/test-classes/keymaster.properties" 
+                <copy file="../core/target/test-classes/core-all.properties" 
                       todir="../core/target/syncope/WEB-INF/classes" 
                       overwrite="true"/>
 
-                <copy file="../console/target/test-classes/keymaster.properties" 
+                <copy file="../console/target/test-classes/console-embedded.properties" 
                       todir="../console/target/syncope-console/WEB-INF/classes" 
                       overwrite="true"/>
 
-                <copy file="../enduser/target/test-classes/keymaster.properties" 
+                <copy file="../enduser/target/test-classes/enduser-embedded.properties" 
                       todir="../enduser/target/syncope-enduser/WEB-INF/classes" 
                       overwrite="true"/>                
-                <copy file="../enduser/target/test-classes/customFormAttributes.json" 
-                      todir="../enduser/target/syncope-enduser/WEB-INF/classes" 
-                      overwrite="true"/>
-                <copy file="../enduser/target/test-classes/customTemplate.json" 
-                      todir="../enduser/target/syncope-enduser/WEB-INF/classes" 
-                      overwrite="true"/>
 
-                <copy file="../wa/target/test-classes/keymaster.properties" 
+                <copy file="../wa/target/test-classes/wa-embedded.properties" 
                       todir="../wa/target/syncope-wa/WEB-INF/classes" 
                       overwrite="true"/>
               </target>
@@ -188,6 +189,7 @@
               </arguments>
               <environmentVariables>
                 <LOADER_PATH>${basedir}/../sra/target/test-classes</LOADER_PATH>
+                <SPRING_PROFILES_ACTIVE>cas</SPRING_PROFILES_ACTIVE>
               </environmentVariables>
               <async>true</async>
               <asyncDestroyOnShutdown>true</asyncDestroyOnShutdown>
@@ -211,11 +213,17 @@
             <timeout>300000</timeout>
             <log>${cargo.log}</log>
             <output>${cargo.output}</output> 
+            <dependencies>
+              <dependency>
+                <groupId>com.h2database</groupId>
+                <artifactId>h2</artifactId>
+              </dependency>
+            </dependencies>
           </container>
           <configuration>
             <properties>
               <cargo.jvmargs>
-                -Dspring.profiles.active=embedded
+                -Dspring.profiles.active=${spring.profiles.active}
                 -XX:+CMSClassUnloadingEnabled -Xmx1024m -Xms512m</cargo.jvmargs>
             </properties>
           </configuration>
@@ -270,5 +278,17 @@
         <defaultGoal>clean verify cargo:run</defaultGoal>  
       </build>
     </profile>
+
+    <profile>
+      <id>all</id>
+
+      <properties>
+        <spring.profiles.active>embedded,all</spring.profiles.active>
+      </properties>
+
+      <build>
+        <defaultGoal>clean verify cargo:run</defaultGoal>  
+      </build>
+    </profile>
   </profiles>
 </project>
diff --git a/archetype/src/main/resources/archetype-resources/sra/pom.xml b/archetype/src/main/resources/archetype-resources/sra/pom.xml
index 48376d9..1547499 100644
--- a/archetype/src/main/resources/archetype-resources/sra/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/sra/pom.xml
@@ -45,8 +45,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
@@ -81,5 +81,12 @@
         <filtering>true</filtering>
       </resource>
     </resources>
+
+    <testResources>
+      <testResource>
+        <directory>src/test/resources</directory>
+        <filtering>true</filtering>
+      </testResource>
+    </testResources>
   </build>
 </project>
diff --git a/archetype/src/main/resources/archetype-resources/sra/src/test/resources/keymaster.properties b/archetype/src/main/resources/archetype-resources/sra/src/test/resources/keymaster.properties
deleted file mode 100644
index 033fe3b..0000000
--- a/archetype/src/main/resources/archetype-resources/sra/src/test/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
diff --git a/archetype/src/main/resources/archetype-resources/wa/pom.xml b/archetype/src/main/resources/archetype-resources/wa/pom.xml
index cfafdbc..7a634d2 100644
--- a/archetype/src/main/resources/archetype-resources/wa/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/wa/pom.xml
@@ -45,8 +45,8 @@
     </dependency>
     
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
diff --git a/archetype/src/main/resources/archetype-resources/wa/src/test/resources/keymaster.properties b/archetype/src/main/resources/archetype-resources/wa/src/test/resources/keymaster.properties
deleted file mode 100644
index 033fe3b..0000000
--- a/archetype/src/main/resources/archetype-resources/wa/src/test/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
diff --git a/archetype/src/main/resources/meta-pom.xml b/archetype/src/main/resources/meta-pom.xml
index 96518c0..0a453ee 100644
--- a/archetype/src/main/resources/meta-pom.xml
+++ b/archetype/src/main/resources/meta-pom.xml
@@ -72,6 +72,11 @@
       </dependency>
 
       <dependency>
+        <groupId>org.apache.syncope.client.am</groupId>
+        <artifactId>syncope-client-am-console</artifactId>
+        <version>${syncope.version}</version>
+      </dependency>
+      <dependency>
         <groupId>org.apache.syncope.client.idm</groupId>
         <artifactId>syncope-client-idm-console</artifactId>
         <version>${syncope.version}</version>
@@ -96,23 +101,19 @@
       </dependency>
 
       <dependency>
-        <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-        <artifactId>syncope-ext-self-keymaster-rest-cxf</artifactId>
+        <groupId>org.apache.syncope.core</groupId>
+        <artifactId>syncope-core-self-keymaster-starter</artifactId>
         <version>${syncope.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-        <artifactId>syncope-ext-self-keymaster-persistence-jpa</artifactId>
-        <version>${syncope.version}</version>
-      </dependency>
+
       <dependency>
         <groupId>org.apache.syncope.common.keymaster</groupId>
         <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
         <version>${syncope.version}</version>
       </dependency>        
       <dependency>
-        <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-        <artifactId>syncope-ext-self-keymaster-client</artifactId>
+        <groupId>org.apache.syncope.common.keymaster.self</groupId>
+        <artifactId>syncope-common-keymaster-client-self</artifactId>
         <version>${syncope.version}</version>
       </dependency>
     </dependencies>
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java
index 3182de3..6f0050c 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java
@@ -100,9 +100,7 @@
     public CSVPullWizardBuilder(final CSVPullSpec defaultItem, final PageReference pageRef) {
         super(defaultItem, pageRef);
 
-        this.maxUploadSize = SyncopeWebApplication.get().getMaxUploadFileSizeMB() == null
-                ? null
-                : Bytes.megabytes(SyncopeWebApplication.get().getMaxUploadFileSizeMB());
+        this.maxUploadSize = Bytes.megabytes(SyncopeWebApplication.get().getMaxUploadFileSizeMB());
     }
 
     @Override
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/CommonUIProperties.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/CommonUIProperties.java
new file mode 100644
index 0000000..cd47af7
--- /dev/null
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/CommonUIProperties.java
@@ -0,0 +1,102 @@
+/*
+ * 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.ui.commons;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.syncope.common.lib.SyncopeProperties;
+
+public abstract class CommonUIProperties extends SyncopeProperties {
+
+    private boolean xForward = true;
+
+    private String xForwardProtocolHeader = "X-Forwarded-Proto";
+
+    private int xForwardHttpPort = 80;
+
+    private int xForwardHttpsPort = 443;
+
+    private boolean csrf = true;
+
+    private int maxUploadFileSizeMB = 5;
+
+    private long maxWaitTimeOnApplyChanges = 30L;
+
+    private final Map<String, String> securityHeaders = new HashMap<>();
+
+    public boolean isxForward() {
+        return xForward;
+    }
+
+    public void setxForward(final boolean xForward) {
+        this.xForward = xForward;
+    }
+
+    public String getxForwardProtocolHeader() {
+        return xForwardProtocolHeader;
+    }
+
+    public void setxForwardProtocolHeader(final String xForwardProtocolHeader) {
+        this.xForwardProtocolHeader = xForwardProtocolHeader;
+    }
+
+    public int getxForwardHttpPort() {
+        return xForwardHttpPort;
+    }
+
+    public void setxForwardHttpPort(final int xForwardHttpPort) {
+        this.xForwardHttpPort = xForwardHttpPort;
+    }
+
+    public int getxForwardHttpsPort() {
+        return xForwardHttpsPort;
+    }
+
+    public void setxForwardHttpsPort(final int xForwardHttpsPort) {
+        this.xForwardHttpsPort = xForwardHttpsPort;
+    }
+
+    public boolean isCsrf() {
+        return csrf;
+    }
+
+    public void setCsrf(final boolean csrf) {
+        this.csrf = csrf;
+    }
+
+    public int getMaxUploadFileSizeMB() {
+        return maxUploadFileSizeMB;
+    }
+
+    public void setMaxUploadFileSizeMB(final int maxUploadFileSizeMB) {
+        this.maxUploadFileSizeMB = maxUploadFileSizeMB;
+    }
+
+    public long getMaxWaitTimeOnApplyChanges() {
+        return maxWaitTimeOnApplyChanges;
+    }
+
+    public void setMaxWaitTimeOnApplyChanges(final long maxWaitTimeOnApplyChanges) {
+        this.maxWaitTimeOnApplyChanges = maxWaitTimeOnApplyChanges;
+    }
+
+    public Map<String, String> getSecurityHeaders() {
+        return securityHeaders;
+    }
+}
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java
index e7b6190..3ac380f 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java
@@ -25,8 +25,6 @@
 import org.apache.syncope.common.rest.api.service.UserSelfService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.actuate.health.Health;
 import org.springframework.boot.actuate.health.HealthIndicator;
 import org.springframework.boot.actuate.health.Status;
@@ -35,17 +33,25 @@
 
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeCoreHealthIndicator.class);
 
-    @Autowired
-    protected ServiceOps serviceOps;
+    protected final ServiceOps serviceOps;
 
-    @Value("${anonymousUser}")
-    protected String anonymousUser;
+    protected final String anonymousUser;
 
-    @Value("${anonymousKey}")
-    protected String anonymousKey;
+    protected final String anonymousKey;
 
-    @Value("${useGZIPCompression:false}")
-    protected boolean useGZIPCompression;
+    protected final boolean useGZIPCompression;
+
+    public SyncopeCoreHealthIndicator(
+            final ServiceOps serviceOps,
+            final String anonymousUser,
+            final String anonymousKey,
+            final boolean useGZIPCompression) {
+
+        this.serviceOps = serviceOps;
+        this.anonymousUser = anonymousUser;
+        this.anonymousKey = anonymousKey;
+        this.useGZIPCompression = useGZIPCompression;
+    }
 
     @Override
     public Health health() {
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BaseBinaryFieldPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BaseBinaryFieldPanel.java
index b779b14..75d644f 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BaseBinaryFieldPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BaseBinaryFieldPanel.java
@@ -33,6 +33,4 @@
     }
 
     protected abstract void sendError(Exception exception);
-
-    protected abstract Integer getMaxUploadFileSizeMB();
 }
diff --git a/client/idrepo/console/pom.xml b/client/idrepo/console/pom.xml
index 06e9391..31ca830 100644
--- a/client/idrepo/console/pom.xml
+++ b/client/idrepo/console/pom.xml
@@ -305,8 +305,8 @@
         </dependency>
 
         <dependency>
-          <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-          <artifactId>syncope-ext-self-keymaster-client</artifactId>
+          <groupId>org.apache.syncope.common.keymaster.self</groupId>
+          <artifactId>syncope-common-keymaster-client-self</artifactId>
           <version>${project.version}</version>
         </dependency>
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/BookmarkablePageLinkBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/BookmarkablePageLinkBuilder.java
index 4d7cd0b..87089a9 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/BookmarkablePageLinkBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/BookmarkablePageLinkBuilder.java
@@ -18,11 +18,10 @@
  */
 package org.apache.syncope.client.console;
 
+import java.util.Optional;
 import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
 
-import java.util.Optional;
-
 public final class BookmarkablePageLinkBuilder {
 
     public static <T extends BasePage> BookmarkablePageLink<T> build(
@@ -36,9 +35,7 @@
 
         @SuppressWarnings("unchecked")
         Class<T> pageClass = (Class<T>) SyncopeWebApplication.get().getPageClass(key);
-        return new BookmarkablePageLink<>(
-                id,
-            Optional.ofNullable(pageClass).orElse(defaultPageClass));
+        return new BookmarkablePageLink<>(id, Optional.ofNullable(pageClass).orElse(defaultPageClass));
     }
 
     private BookmarkablePageLinkBuilder() {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java
new file mode 100644
index 0000000..d2ef419
--- /dev/null
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.AnyPanel;
+import org.apache.syncope.client.ui.commons.CommonUIProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("console")
+public class ConsoleProperties extends CommonUIProperties {
+
+    public static class Topology {
+
+        private int corePoolSize = 10;
+
+        private int maxPoolSize = 20;
+
+        private int queueCapacity = 50;
+
+        public int getCorePoolSize() {
+            return corePoolSize;
+        }
+
+        public void setCorePoolSize(final int corePoolSize) {
+            this.corePoolSize = corePoolSize;
+        }
+
+        public int getMaxPoolSize() {
+            return maxPoolSize;
+        }
+
+        public void setMaxPoolSize(final int maxPoolSize) {
+            this.maxPoolSize = maxPoolSize;
+        }
+
+        public int getQueueCapacity() {
+            return queueCapacity;
+        }
+
+        public void setQueueCapacity(final int queueCapacity) {
+            this.queueCapacity = queueCapacity;
+        }
+    }
+
+    private String reconciliationReportKey;
+
+    private final Map<String, Class<? extends BasePage>> page = new HashMap<>();
+
+    private String defaultAnyPanelClass = AnyPanel.class.getName();
+
+    private final Topology topology = new Topology();
+
+    public String getReconciliationReportKey() {
+        return reconciliationReportKey;
+    }
+
+    public void setReconciliationReportKey(final String reconciliationReportKey) {
+        this.reconciliationReportKey = reconciliationReportKey;
+    }
+
+    public String getDefaultAnyPanelClass() {
+        return defaultAnyPanelClass;
+    }
+
+    public void setDefaultAnyPanelClass(final String defaultAnyPanelClass) {
+        this.defaultAnyPanelClass = defaultAnyPanelClass;
+    }
+
+    public Map<String, Class<? extends BasePage>> getPage() {
+        return page;
+    }
+
+    public Topology getTopology() {
+        return topology;
+    }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SecurityConfig.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SecurityConfig.java
index 075570b..a77f5cc 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SecurityConfig.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SecurityConfig.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.client.console;
 
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
-import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -31,17 +31,14 @@
 @Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
-    @Value("${anonymousUser}")
-    private String anonymousUser;
-
-    @Value("${anonymousKey}")
-    private String anonymousKey;
+    @Autowired
+    private ConsoleProperties props;
 
     @Override
     protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
         auth.inMemoryAuthentication().
-                withUser(anonymousUser).
-                password("{noop}" + anonymousKey).
+                withUser(props.getAnonymousUser()).
+                password("{noop}" + props.getAnonymousKey()).
                 roles(IdRepoEntitlement.ANONYMOUS);
     }
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
index 9b4823e..4348523 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
@@ -19,6 +19,8 @@
 package org.apache.syncope.client.console;
 
 import com.giffing.wicket.spring.boot.starter.web.config.WicketWebInitializerAutoConfig.WebSocketWicketWebInitializerAutoConfiguration;
+import java.util.Map;
+import org.apache.syncope.client.console.actuate.SyncopeConsoleInfoContributor;
 import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionLinksProvider;
 import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionsProvider;
 import org.apache.syncope.client.console.commons.AnyWizardBuilderAdditionalSteps;
@@ -44,39 +46,48 @@
 import org.apache.syncope.client.ui.commons.ApplicationContextProvider;
 import org.apache.syncope.client.ui.commons.MIMETypesLoader;
 import org.apache.syncope.client.ui.commons.actuate.SyncopeCoreHealthIndicator;
+import org.apache.syncope.common.keymaster.client.api.ServiceOps;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStart;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
 import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
 import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.PropertySource;
 
-@PropertySource("classpath:console.properties")
-@PropertySource(value = "file:${console.directory}/console.properties", ignoreResourceNotFound = true)
 @SpringBootApplication(exclude = {
     ErrorMvcAutoConfiguration.class,
     HttpMessageConvertersAutoConfiguration.class })
+@EnableConfigurationProperties(ConsoleProperties.class)
 public class SyncopeConsoleApplication extends SpringBootServletInitializer {
 
+    public static void main(final String[] args) {
+        new SpringApplicationBuilder(SyncopeConsoleApplication.class).
+                properties("spring.config.name:console").
+                build().run(args);
+    }
+
+    @Autowired
+    private ServiceOps serviceOps;
+
+    @Autowired
+    private ConsoleProperties props;
+
     @Autowired
     private ApplicationContext ctx;
 
-    public static void main(final String[] args) {
-        SpringApplication.run(SyncopeConsoleApplication.class, args);
-    }
-
     @Override
     protected SpringApplicationBuilder configure(final SpringApplicationBuilder builder) {
-        builder.properties(WebSocketWicketWebInitializerAutoConfiguration.REGISTER_SERVER_ENDPOINT_ENABLED + "=false");
-        return super.configure(builder);
+        return builder.properties(Map.of(
+                WebSocketWicketWebInitializerAutoConfiguration.REGISTER_SERVER_ENDPOINT_ENABLED, false,
+                "spring.config.name", "console")).
+                sources(SyncopeConsoleApplication.class);
     }
 
     @Bean
@@ -87,7 +98,17 @@
     @ConditionalOnMissingBean
     @Bean
     public SyncopeCoreHealthIndicator syncopeCoreHealthIndicator() {
-        return new SyncopeCoreHealthIndicator();
+        return new SyncopeCoreHealthIndicator(
+                serviceOps,
+                props.getAnonymousUser(),
+                props.getAnonymousKey(),
+                props.isUseGZIPCompression());
+    }
+
+    @ConditionalOnMissingBean
+    @Bean
+    public SyncopeConsoleInfoContributor syncopeConsoleInfoContributor() {
+        return new SyncopeConsoleInfoContributor();
     }
 
     @ConditionalOnMissingBean(name = "classPathScanImplementationLookup")
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
index 2ea6ef7..4633004 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
@@ -43,7 +43,6 @@
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.syncope.client.console.commons.RealmsUtils;
-import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.client.lib.batch.BatchRequest;
@@ -129,20 +128,13 @@
         super(request);
 
         clientFactory = SyncopeWebApplication.get().newClientFactory();
-        anonymousClient = clientFactory.create(new AnonymousAuthenticationHandler(
-                SyncopeWebApplication.get().getAnonymousUser(),
-                SyncopeWebApplication.get().getAnonymousKey()));
+        anonymousClient = SyncopeWebApplication.get().newAnonymousClient();
 
         gitAndBuildInfo = anonymousClient.gitAndBuildInfo();
         platformInfo = anonymousClient.platform();
         systemInfo = anonymousClient.system();
 
-        executor = new ThreadPoolTaskExecutor();
-        executor.setWaitForTasksToCompleteOnShutdown(false);
-        executor.setCorePoolSize(SyncopeWebApplication.get().getCorePoolSize());
-        executor.setMaxPoolSize(SyncopeWebApplication.get().getMaxPoolSize());
-        executor.setQueueCapacity(SyncopeWebApplication.get().getQueueCapacity());
-        executor.initialize();
+        executor = SyncopeWebApplication.get().newThreadPoolTaskExecutor();
     }
 
     protected String message(final SyncopeClientException sce) {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
index 6d1ec5b..182c114 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
@@ -19,30 +19,19 @@
 package org.apache.syncope.client.console;
 
 import com.giffing.wicket.spring.boot.starter.app.WicketBootSecuredWebApplication;
-import com.google.common.net.HttpHeaders;
 import de.agilecoders.wicket.core.Bootstrap;
 import de.agilecoders.wicket.core.settings.BootstrapSettings;
 import de.agilecoders.wicket.core.settings.IBootstrapSettings;
 import de.agilecoders.wicket.core.settings.SingleThemeProvider;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import org.apache.commons.lang3.BooleanUtils;
-import org.apache.commons.lang3.ClassUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.ui.commons.annotations.Resource;
 import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionLinksProvider;
 import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionsProvider;
 import org.apache.syncope.client.console.commons.AnyWizardBuilderAdditionalSteps;
 import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup;
-import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.pages.Dashboard;
 import org.apache.syncope.client.console.pages.Login;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
-import org.apache.syncope.common.lib.PropertyUtils;
 import org.apache.wicket.Page;
 import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession;
 import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
@@ -63,8 +52,10 @@
 import org.apache.syncope.client.console.commons.PolicyTabProvider;
 import org.apache.syncope.client.console.commons.StatusProvider;
 import org.apache.syncope.client.console.commons.VirSchemaDetailsPanelProvider;
+import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.pages.MustChangePassword;
-import org.apache.syncope.client.console.panels.AnyPanel;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.ui.commons.themes.AdminLTE;
 import org.apache.syncope.client.ui.commons.SyncopeUIRequestCycleListener;
 import org.apache.syncope.client.ui.commons.Constants;
@@ -75,8 +66,8 @@
 import org.apache.wicket.request.cycle.IRequestCycleListener;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.ApplicationContext;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -84,45 +75,19 @@
 
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeWebApplication.class);
 
-    private static final String CONSOLE_PROPERTIES = "console.properties";
-
     public static SyncopeWebApplication get() {
         return (SyncopeWebApplication) WebApplication.get();
     }
 
     @Autowired
+    protected ConsoleProperties props;
+
+    @Autowired
     protected ClassPathScanImplementationLookup lookup;
 
     @Autowired
     protected ServiceOps serviceOps;
 
-    @Value("${anonymousUser}")
-    protected String anonymousUser;
-
-    @Value("${anonymousKey}")
-    protected String anonymousKey;
-
-    @Value("${useGZIPCompression:false}")
-    protected boolean useGZIPCompression;
-
-    @Value("${maxUploadFileSizeMB:#{null}}")
-    protected Integer maxUploadFileSizeMB;
-
-    @Value("${maxWaitTime:30}")
-    protected Integer maxWaitTime;
-
-    @Value("${corePoolSize:5}")
-    protected Integer corePoolSize;
-
-    @Value("${maxPoolSize:10}")
-    protected Integer maxPoolSize;
-
-    @Value("${queueCapacity:50}")
-    protected Integer queueCapacity;
-
-    @Value("${reconciliationReportKey}")
-    protected String reconciliationReportKey;
-
     @Autowired
     protected ExternalResourceProvider resourceProvider;
 
@@ -147,56 +112,10 @@
     @Autowired
     protected ApplicationContext ctx;
 
-    protected Map<String, Class<? extends BasePage>> pageClasses;
-
-    protected String defaultAnyLayoutClass;
-
-    @SuppressWarnings("unchecked")
-    protected void populatePageClasses(final Properties props) {
-        Enumeration<String> propNames = (Enumeration<String>) props.propertyNames();
-        while (propNames.hasMoreElements()) {
-            String className = propNames.nextElement();
-            if (className.startsWith("page.")) {
-                try {
-                    Class<?> clazz = ClassUtils.getClass(props.getProperty(className));
-                    if (BasePage.class.isAssignableFrom(clazz)) {
-                        pageClasses.put(
-                                StringUtils.substringAfter(className, "page."), (Class<? extends BasePage>) clazz);
-                    } else {
-                        LOG.warn("{} does not extend {}, ignoring...", clazz.getName(), BasePage.class.getName());
-                    }
-                } catch (ClassNotFoundException e) {
-                    LOG.error("While looking for class identified by property '{}'", className, e);
-                }
-            }
-        }
-    }
-
-    protected static void setSecurityHeaders(final Properties props, final WebResponse response) {
-        @SuppressWarnings("unchecked")
-        Enumeration<String> propNames = (Enumeration<String>) props.propertyNames();
-        while (propNames.hasMoreElements()) {
-            String name = propNames.nextElement();
-            if (name.startsWith("security.headers.")) {
-                response.setHeader(StringUtils.substringAfter(name, "security.headers."), props.getProperty(name));
-            }
-        }
-    }
-
     @Override
     protected void init() {
         super.init();
 
-        // read console.properties
-        Properties props = PropertyUtils.read(getClass(), CONSOLE_PROPERTIES, "console.directory");
-
-        // process page properties
-        pageClasses = new HashMap<>();
-        populatePageClasses(props);
-        pageClasses = Collections.unmodifiableMap(pageClasses);
-
-        defaultAnyLayoutClass = props.getProperty("default.any.panel.class", AnyPanel.class.getName());
-
         // Application settings
         IBootstrapSettings settings = new BootstrapSettings();
 
@@ -236,38 +155,28 @@
             }
         });
 
-        if (BooleanUtils.toBoolean(props.getProperty("x-forward"))) {
+        if (props.isxForward()) {
             XForwardedRequestWrapperFactory.Config config = new XForwardedRequestWrapperFactory.Config();
-            config.setProtocolHeader(props.getProperty("x-forward.protocol.header", HttpHeaders.X_FORWARDED_PROTO));
-            try {
-                config.setHttpServerPort(Integer.valueOf(props.getProperty("x-forward.http.port", "80")));
-            } catch (NumberFormatException e) {
-                LOG.error("Invalid value provided for 'x-forward.http.port': {}",
-                        props.getProperty("x-forward.http.port"));
-                config.setHttpServerPort(80);
-            }
-            try {
-                config.setHttpsServerPort(Integer.valueOf(props.getProperty("x-forward.https.port", "443")));
-            } catch (NumberFormatException e) {
-                LOG.error("Invalid value provided for 'x-forward.https.port': {}",
-                        props.getProperty("x-forward.https.port"));
-                config.setHttpsServerPort(443);
-            }
+            config.setProtocolHeader(props.getxForwardProtocolHeader());
+            config.setHttpServerPort(props.getxForwardHttpPort());
+            config.setHttpsServerPort(props.getxForwardHttpsPort());
 
             XForwardedRequestWrapperFactory factory = new XForwardedRequestWrapperFactory();
             factory.setConfig(config);
             getFilterFactoryManager().add(factory);
         }
 
-        if (BooleanUtils.toBoolean(props.getProperty("csrf"))) {
+        if (props.isCsrf()) {
             getRequestCycleListeners().add(new WebSocketAwareResourceIsolationRequestCycleListener());
         }
+
         getRequestCycleListeners().add(new IRequestCycleListener() {
 
             @Override
             public void onEndRequest(final RequestCycle cycle) {
                 if (cycle.getResponse() instanceof WebResponse && !(cycle.getResponse() instanceof WebSocketResponse)) {
-                    setSecurityHeaders(props, (WebResponse) cycle.getResponse());
+                    props.getSecurityHeaders().
+                            forEach((name, value) -> ((WebResponse) cycle.getResponse()).setHeader(name, value));
                 }
             }
         });
@@ -322,50 +231,53 @@
         return lookup;
     }
 
-    public Class<? extends BasePage> getPageClass(final String key) {
-        return pageClasses.get(key);
+    public Class<? extends BasePage> getPageClass(final String name) {
+        return props.getPage().get(name);
     }
 
-    public String getAnonymousUser() {
-        return anonymousUser;
+    public ThreadPoolTaskExecutor newThreadPoolTaskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setWaitForTasksToCompleteOnShutdown(false);
+        executor.setCorePoolSize(props.getTopology().getCorePoolSize());
+        executor.setMaxPoolSize(props.getTopology().getMaxPoolSize());
+        executor.setQueueCapacity(props.getTopology().getQueueCapacity());
+        executor.initialize();
+        return executor;
     }
 
-    public String getAnonymousKey() {
-        return anonymousKey;
-    }
-
-    public String getReconciliationReportKey() {
-        return reconciliationReportKey;
-    }
-
-    public Integer getMaxUploadFileSizeMB() {
-        return maxUploadFileSizeMB;
-    }
-
-    public Integer getMaxWaitTimeInSeconds() {
-        return maxWaitTime;
-    }
-
-    public Integer getCorePoolSize() {
-        return corePoolSize;
-    }
-
-    public Integer getMaxPoolSize() {
-        return maxPoolSize;
-    }
-
-    public Integer getQueueCapacity() {
-        return queueCapacity;
-    }
-
-    public String getDefaultAnyLayoutClass() {
-        return defaultAnyLayoutClass;
+    public SyncopeClient newAnonymousClient() {
+        return newClientFactory().create(
+                new AnonymousAuthenticationHandler(props.getAnonymousUser(), props.getAnonymousKey()));
     }
 
     public SyncopeClientFactoryBean newClientFactory() {
         return new SyncopeClientFactoryBean().
                 setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
-                setUseCompression(useGZIPCompression);
+                setUseCompression(props.isUseGZIPCompression());
+    }
+
+    public String getDefaultAnyPanelClass() {
+        return props.getDefaultAnyPanelClass();
+    }
+
+    public String getAnonymousUser() {
+        return props.getAnonymousUser();
+    }
+
+    public String getAnonymousKey() {
+        return props.getAnonymousKey();
+    }
+
+    public String getReconciliationReportKey() {
+        return props.getReconciliationReportKey();
+    }
+
+    public long getMaxWaitTimeInSeconds() {
+        return props.getMaxWaitTimeOnApplyChanges();
+    }
+
+    public int getMaxUploadFileSizeMB() {
+        return props.getMaxUploadFileSizeMB();
     }
 
     public ExternalResourceProvider getResourceProvider() {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/actuate/SyncopeConsoleInfoContributor.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/actuate/SyncopeConsoleInfoContributor.java
new file mode 100644
index 0000000..e0bcd0e
--- /dev/null
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/actuate/SyncopeConsoleInfoContributor.java
@@ -0,0 +1,37 @@
+/*
+ * 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.actuate;
+
+import org.apache.syncope.client.console.ConsoleProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.info.Info;
+import org.springframework.boot.actuate.info.InfoContributor;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+public class SyncopeConsoleInfoContributor implements InfoContributor {
+
+    @Autowired
+    protected ConsoleProperties consoleProperties;
+
+    @PreAuthorize("isAuthenticated()")
+    @Override
+    public void contribute(final Info.Builder builder) {
+        builder.withDetail("consoleProperties", consoleProperties);
+    }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayout.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayout.java
index 9c00390..ebaec56 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayout.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/layout/AnyLayout.java
@@ -41,7 +41,7 @@
     private final Map<String, AnyObjectFormLayoutInfo> anyObjects = new HashMap<>();
 
     public AnyLayout() {
-        this.anyPanelClass = SyncopeWebApplication.get().getDefaultAnyLayoutClass();
+        this.anyPanelClass = SyncopeWebApplication.get().getDefaultAnyPanelClass();
     }
 
     public String getAnyPanelClass() {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
index 25edaba..4fc58f5 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
@@ -36,8 +36,8 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.console.commons.PreviewUtils;
 import org.apache.syncope.client.ui.commons.HttpResourceStream;
@@ -111,9 +111,7 @@
 
         previewer = previewUtils.getPreviewer(mimeType);
 
-        maxUploadSize = SyncopeWebApplication.get().getMaxUploadFileSizeMB() == null
-                ? null
-                : Bytes.megabytes(SyncopeWebApplication.get().getMaxUploadFileSizeMB());
+        maxUploadSize = Bytes.megabytes(SyncopeWebApplication.get().getMaxUploadFileSizeMB());
         uploadForm = new StatelessForm<>("uploadForm");
         uploadForm.setMultiPart(true);
         add(uploadForm);
@@ -297,11 +295,6 @@
     }
 
     @Override
-    protected Integer getMaxUploadFileSizeMB() {
-        return SyncopeWebApplication.get().getMaxUploadFileSizeMB();
-    }
-
-    @Override
     public FieldPanel<String> setReadOnly(final boolean readOnly) {
         super.setReadOnly(readOnly);
         fileUpload.setEnabled(!readOnly);
diff --git a/client/idrepo/console/src/main/resources/application.properties b/client/idrepo/console/src/main/resources/application.properties
deleted file mode 100644
index 8f4f239..0000000
--- a/client/idrepo/console/src/main/resources/application.properties
+++ /dev/null
@@ -1,32 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} Console
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.servlet.encoding.charset=UTF-8
-server.servlet.encoding.enabled=true
-server.servlet.encoding.force=true
-
-server.servlet.contextPath=/syncope-console
-
-management.endpoints.web.exposure.include=info,health,loggers
-management.endpoint.health.show-details=ALWAYS
-
-service.discovery.address=http://localhost:8080/syncope-console/
-
-wicket.core.csrf.enabled=false
diff --git a/client/idrepo/console/src/main/resources/console.properties b/client/idrepo/console/src/main/resources/console.properties
index 3be739e..bf17a78 100644
--- a/client/idrepo/console/src/main/resources/console.properties
+++ b/client/idrepo/console/src/main/resources/console.properties
@@ -14,42 +14,59 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-console.directory=${conf.directory}
+spring.application.name=Apache Syncope ${syncope.version} Console
+spring.groovy.template.check-template-location=false
+spring.main.banner-mode=log
 
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
+server.servlet.encoding.charset=UTF-8
+server.servlet.encoding.enabled=true
+server.servlet.encoding.force=true
 
-useGZIPCompression=true
-maxUploadFileSizeMB=5
+server.servlet.contextPath=/syncope-console
+
+management.endpoints.web.exposure.include=info,health,loggers
+management.endpoint.health.show-details=ALWAYS
+
+service.discovery.address=http://localhost:8080/syncope-console/
+
+wicket.core.csrf.enabled=false
+
+logging.config=classpath:/log4j2.xml
+
+console.anonymousUser=${anonymousUser}
+console.anonymousKey=${anonymousKey}
+
+console.useGZIPCompression=true
+console.maxUploadFileSizeMB=5
 
 # Max wait time on apply changes from modals/wizards (given in seconds)
-maxWaitTimeOnApplyChanges=30
+console.maxWaitTimeOnApplyChanges=30
 
-reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
+console.reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
 
-page.dashboard=org.apache.syncope.client.console.pages.Dashboard
-page.realms=org.apache.syncope.client.console.pages.Realms
-page.reports=org.apache.syncope.client.console.pages.Reports
-page.audit=org.apache.syncope.client.console.pages.Audit
-page.implementations=org.apache.syncope.client.console.pages.Implementations
-page.logs=org.apache.syncope.client.console.pages.Logs
-page.security=org.apache.syncope.client.console.pages.Security
-page.types=org.apache.syncope.client.console.pages.Types
-page.policies=org.apache.syncope.client.console.pages.Policies
-page.notifications=org.apache.syncope.client.console.pages.Notifications
-page.parameters=org.apache.syncope.client.console.pages.Parameters
+console.page.dashboard=org.apache.syncope.client.console.pages.Dashboard
+console.page.realms=org.apache.syncope.client.console.pages.Realms
+console.page.reports=org.apache.syncope.client.console.pages.Reports
+console.page.audit=org.apache.syncope.client.console.pages.Audit
+console.page.implementations=org.apache.syncope.client.console.pages.Implementations
+console.page.logs=org.apache.syncope.client.console.pages.Logs
+console.page.security=org.apache.syncope.client.console.pages.Security
+console.page.types=org.apache.syncope.client.console.pages.Types
+console.page.policies=org.apache.syncope.client.console.pages.Policies
+console.page.notifications=org.apache.syncope.client.console.pages.Notifications
+console.page.parameters=org.apache.syncope.client.console.pages.Parameters
 
-default.any.panel.class=org.apache.syncope.client.console.panels.AnyPanel
+console.default-any-panel-class=org.apache.syncope.client.console.panels.AnyPanel
 
-topology.corePoolSize=10
-topology.maxPoolSize=20
-topology.queueCapacity=50
+console.topology.corePoolSize=10
+console.topology.maxPoolSize=20
+console.topology.queueCapacity=50
 
-x-forward=true
-csrf=true
+console.x-forward=true
+console.csrf=true
 
-security.headers.X-XSS-Protection=1; mode=block
-security.headers.Strict-Transport-Security=max-age=31536000; includeSubDomains; preload
-security.headers.X-Content-Type-Options=nosniff
-security.headers.X-Frame-Options=sameorigin
-#security.headers.Content-Security-Policy=default-src https:
+console.security-headers.X-XSS-Protection=1; mode=block
+console.security-headers.Strict-Transport-Security=max-age=31536000; includeSubDomains; preload
+console.security-headers.X-Content-Type-Options=nosniff
+console.security-headers.X-Frame-Options=sameorigin
+#console.security-headers.Content-Security-Policy=default-src https:
diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java
index 6797501..a3d45e2 100644
--- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java
+++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java
@@ -29,10 +29,8 @@
 import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties;
 import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator.WicketEndpointRepositoryDefault;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Properties;
 import java.util.Set;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.commons.lang3.tuple.Triple;
@@ -91,6 +89,21 @@
     public static class SyncopeConsoleWebApplicationTestConfig {
 
         @Bean
+        public ConsoleProperties consoleProperties() {
+            ConsoleProperties consoleProperties = new ConsoleProperties();
+
+            consoleProperties.getSecurityHeaders().put("X-XSS-Protection", "1; mode=block");
+            consoleProperties.getSecurityHeaders().
+                    put("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
+            consoleProperties.getSecurityHeaders().put("X-Content-Type-Options", "nosniff");
+            consoleProperties.getSecurityHeaders().put("X-Frame-Options", "sameorigin");
+
+            consoleProperties.setAnonymousUser("anonymousUser");
+
+            return consoleProperties;
+        }
+
+        @Bean
         public ServiceOps selfServiceOps() {
             return mock(ServiceOps.class);
         }
@@ -294,25 +307,18 @@
         }
     }
 
-    protected static Properties PROPS;
+    protected static ConsoleProperties PROPS;
 
     protected static WicketTester TESTER;
 
     @BeforeAll
-    public static void loadProps() throws IOException {
-        PROPS = new Properties();
-        try (InputStream is = AbstractTest.class.getResourceAsStream("/console.properties")) {
-            PROPS.load(is);
-        }
-    }
-
-    @BeforeAll
     public static void setupTester() throws IOException {
         AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
         ctx.register(SyncopeConsoleWebApplicationTestConfig.class);
         ctx.register(TestSyncopeWebApplication.class);
         ctx.refresh();
 
+        PROPS = ctx.getBean(ConsoleProperties.class);
         TESTER = new WicketTester(ctx.getBean(SyncopeWebApplication.class));
     }
 }
diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java
index d4c4bdb..da07d3d 100644
--- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java
+++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java
@@ -25,8 +25,6 @@
 
 import java.io.IOException;
 import java.security.AccessControlException;
-import java.util.Enumeration;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import javax.ws.rs.BadRequestException;
@@ -42,24 +40,9 @@
 
 public class SyncopeConsoleApplicationTest extends AbstractTest {
 
-    private Map<String, String> getConfiguredSecurityHeaders() throws IOException {
-        Map<String, String> securityHeaders = new HashMap<>();
-
-        @SuppressWarnings("unchecked")
-        Enumeration<String> propNames = (Enumeration<String>) PROPS.propertyNames();
-        while (propNames.hasMoreElements()) {
-            String name = propNames.nextElement();
-            if (name.startsWith("security.headers.")) {
-                securityHeaders.put(StringUtils.substringAfter(name, "security.headers."), PROPS.getProperty(name));
-            }
-        }
-
-        return securityHeaders;
-    }
-
     @Test
     public void securityHeaders() throws IOException {
-        Map<String, String> securityHeaders = getConfiguredSecurityHeaders();
+        Map<String, String> securityHeaders = PROPS.getSecurityHeaders();
         assertEquals(4, securityHeaders.size());
 
         // 1. anonymous
diff --git a/client/idrepo/console/src/test/resources/application-debug.properties b/client/idrepo/console/src/test/resources/console-debug.properties
similarity index 93%
rename from client/idrepo/console/src/test/resources/application-debug.properties
rename to client/idrepo/console/src/test/resources/console-debug.properties
index 1ac160d..990acb0 100644
--- a/client/idrepo/console/src/test/resources/application-debug.properties
+++ b/client/idrepo/console/src/test/resources/console-debug.properties
@@ -20,3 +20,5 @@
 
 server.port=9090
 service.discovery.address=http://localhost:9090/syncope-console/
+
+logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
diff --git a/client/idrepo/console/src/test/resources/log4j2.xml b/client/idrepo/console/src/test/resources/log4j2.xml
index 28257a5..c57064d 100644
--- a/client/idrepo/console/src/test/resources/log4j2.xml
+++ b/client/idrepo/console/src/test/resources/log4j2.xml
@@ -21,34 +21,33 @@
 
   <appenders>
 
-    <Console name="main" target="SYSTEM_OUT">
-      <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger - %msg%n"/>
+    <Console name="console" target="SYSTEM_OUT" follow="true">
+      <PatternLayout pattern="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} [%11.11t] %style{%-60.60c{60}}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
     </Console>
     
   </appenders>
-
+  
   <loggers>
-
+    
     <asyncLogger name="org.apache.syncope.client.lib" additivity="false" level="OFF">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
 
-    <asyncLogger name="org.apache.syncope.client.console" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
+    <asyncLogger name="org.apache.syncope.client.console" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
     </asyncLogger>
 
     <asyncLogger name="org.apache.wicket" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
     
     <asyncLogger name="org.apache.cxf" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
     
-    <root level="ERROR">
-      <appender-ref ref="main"/>
+    <root level="INFO">
+      <appender-ref ref="console"/>
     </root>
-  
+    
   </loggers>
-  
 </configuration>
diff --git a/client/idrepo/enduser/pom.xml b/client/idrepo/enduser/pom.xml
index b1ca673..6c127c7 100644
--- a/client/idrepo/enduser/pom.xml
+++ b/client/idrepo/enduser/pom.xml
@@ -297,8 +297,8 @@
         </dependency>
 
         <dependency>
-          <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-          <artifactId>syncope-ext-self-keymaster-client</artifactId>
+          <groupId>org.apache.syncope.common.keymaster.self</groupId>
+          <artifactId>syncope-common-keymaster-client-self</artifactId>
           <version>${project.version}</version>
         </dependency>
 
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/EnduserProperties.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/EnduserProperties.java
new file mode 100644
index 0000000..95fd53f
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/EnduserProperties.java
@@ -0,0 +1,76 @@
+/*
+ * 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.enduser;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.syncope.client.enduser.pages.BasePage;
+import org.apache.syncope.client.enduser.panels.Sidebar;
+import org.apache.syncope.client.ui.commons.CommonUIProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("enduser")
+public class EnduserProperties extends CommonUIProperties {
+
+    private String adminUser;
+
+    private Class<? extends Sidebar> sidebar = Sidebar.class;
+
+    private String customFormLayout = "classpath:/customFormLayout.json";
+
+    private boolean captcha;
+
+    private final Map<String, Class<? extends BasePage>> page = new HashMap<>();
+
+    public String getAdminUser() {
+        return adminUser;
+    }
+
+    public void setAdminUser(final String adminUser) {
+        this.adminUser = adminUser;
+    }
+
+    public Class<? extends Sidebar> getSidebar() {
+        return sidebar;
+    }
+
+    public void setSidebar(final Class<? extends Sidebar> sidebar) {
+        this.sidebar = sidebar;
+    }
+
+    public String getCustomFormLayout() {
+        return customFormLayout;
+    }
+
+    public void setCustomFormLayout(final String customFormLayout) {
+        this.customFormLayout = customFormLayout;
+    }
+
+    public boolean isCaptcha() {
+        return captcha;
+    }
+
+    public void setCaptcha(final boolean captcha) {
+        this.captcha = captcha;
+    }
+
+    public Map<String, Class<? extends BasePage>> getPage() {
+        return page;
+    }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SecurityConfig.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SecurityConfig.java
index 507bb68..a9537a6 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SecurityConfig.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SecurityConfig.java
@@ -19,7 +19,7 @@
 package org.apache.syncope.client.enduser;
 
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
-import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -31,17 +31,14 @@
 @Configuration
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
-    @Value("${anonymousUser}")
-    private String anonymousUser;
-
-    @Value("${anonymousKey}")
-    private String anonymousKey;
+    @Autowired
+    private EnduserProperties props;
 
     @Override
     protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
         auth.inMemoryAuthentication().
-                withUser(anonymousUser).
-                password("{noop}" + anonymousKey).
+                withUser(props.getAnonymousUser()).
+                password("{noop}" + props.getAnonymousKey()).
                 roles(IdRepoEntitlement.ANONYMOUS);
     }
 
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
index 58556d7..2dfe2a6 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
@@ -19,39 +19,51 @@
 package org.apache.syncope.client.enduser;
 
 import com.giffing.wicket.spring.boot.starter.web.config.WicketWebInitializerAutoConfig.WebSocketWicketWebInitializerAutoConfiguration;
+import java.util.Map;
+import org.apache.syncope.client.enduser.actuate.SyncopeEnduserInfoContributor;
 import org.apache.syncope.client.enduser.commons.PreviewUtils;
 import org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
 import org.apache.syncope.client.ui.commons.ApplicationContextProvider;
 import org.apache.syncope.client.ui.commons.MIMETypesLoader;
 import org.apache.syncope.client.ui.commons.actuate.SyncopeCoreHealthIndicator;
+import org.apache.syncope.common.keymaster.client.api.ServiceOps;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStart;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop;
-import org.springframework.boot.SpringApplication;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
 import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
 import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.PropertySource;
 
-@PropertySource("classpath:enduser.properties")
-@PropertySource(value = "file:${console.directory}/enduser.properties", ignoreResourceNotFound = true)
 @SpringBootApplication(exclude = {
     ErrorMvcAutoConfiguration.class,
     HttpMessageConvertersAutoConfiguration.class })
+@EnableConfigurationProperties(EnduserProperties.class)
 public class SyncopeEnduserApplication extends SpringBootServletInitializer {
 
     public static void main(final String[] args) {
-        SpringApplication.run(SyncopeEnduserApplication.class, args);
+        new SpringApplicationBuilder(SyncopeEnduserApplication.class).
+                properties("spring.config.name:enduser").
+                build().run(args);
     }
 
+    @Autowired
+    private ServiceOps serviceOps;
+
+    @Autowired
+    private EnduserProperties props;
+
     @Override
     protected SpringApplicationBuilder configure(final SpringApplicationBuilder builder) {
-        builder.properties(WebSocketWicketWebInitializerAutoConfiguration.REGISTER_SERVER_ENDPOINT_ENABLED + "=false");
-        return super.configure(builder);
+        return builder.properties(Map.of(
+                WebSocketWicketWebInitializerAutoConfiguration.REGISTER_SERVER_ENDPOINT_ENABLED, false,
+                "spring.config.name", "enduser")).
+                sources(SyncopeEnduserApplication.class);
     }
 
     @Bean
@@ -62,7 +74,17 @@
     @ConditionalOnMissingBean
     @Bean
     public SyncopeCoreHealthIndicator syncopeCoreHealthIndicator() {
-        return new SyncopeCoreHealthIndicator();
+        return new SyncopeCoreHealthIndicator(
+                serviceOps,
+                props.getAnonymousUser(),
+                props.getAnonymousKey(),
+                props.isUseGZIPCompression());
+    }
+
+    @ConditionalOnMissingBean
+    @Bean
+    public SyncopeEnduserInfoContributor syncopeEnduserInfoContributor() {
+        return new SyncopeEnduserInfoContributor();
     }
 
     @ConditionalOnMissingBean(name = "classPathScanImplementationLookup")
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
index cc15893..f5bca47 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserSession.java
@@ -22,7 +22,6 @@
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.cxf.jaxrs.client.WebClient;
-import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.client.ui.commons.BaseSession;
@@ -39,8 +38,6 @@
 import org.apache.wicket.request.Request;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.core.task.TaskRejectedException;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import javax.ws.rs.BadRequestException;
 import javax.ws.rs.ForbiddenException;
 import javax.ws.rs.core.EntityTag;
@@ -97,8 +94,6 @@
 
     private final Map<Class<?>, Object> services = Collections.synchronizedMap(new HashMap<>());
 
-    private final ThreadPoolTaskExecutor executor;
-
     private String domain;
 
     private SyncopeClient client;
@@ -113,17 +108,10 @@
         super(request);
 
         clientFactory = SyncopeWebApplication.get().newClientFactory();
-        anonymousClient = clientFactory.
-                create(new AnonymousAuthenticationHandler(
-                        SyncopeWebApplication.get().getAnonymousUser(),
-                        SyncopeWebApplication.get().getAnonymousKey()));
+        anonymousClient = SyncopeWebApplication.get().newAnonymousClient();
 
         platformInfo = anonymousClient.platform();
         systemInfo = anonymousClient.system();
-
-        executor = new ThreadPoolTaskExecutor();
-        executor.setWaitForTasksToCompleteOnShutdown(false);
-        executor.initialize();
     }
 
     protected String message(final SyncopeClientException sce) {
@@ -176,23 +164,14 @@
         return clientFactory.getContentType().getMediaType();
     }
 
-    public void execute(final Runnable command) {
-        try {
-            executor.execute(command);
-        } catch (TaskRejectedException e) {
-            LOG.error("Could not execute {}", command, e);
-        }
-    }
-
     @Override
     public <T> Future<T> execute(final Callable<T> command) {
         try {
-            return executor.submit(command);
-        } catch (TaskRejectedException e) {
+            return CompletableFuture.completedFuture(command.call());
+        } catch (Exception e) {
             LOG.error("Could not execute {}", command, e);
-
-            return new CompletableFuture<>();
         }
+        return new CompletableFuture<>();
     }
 
     @Override
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
index a849506..95a0d5e 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
@@ -21,29 +21,13 @@
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.giffing.wicket.spring.boot.starter.app.WicketBootStandardWebApplication;
-import com.google.common.net.HttpHeaders;
 import de.agilecoders.wicket.core.Bootstrap;
 import de.agilecoders.wicket.core.settings.BootstrapSettings;
 import de.agilecoders.wicket.core.settings.IBootstrapSettings;
 import de.agilecoders.wicket.core.settings.SingleThemeProvider;
-import java.io.File;
-import java.io.IOException;
 import java.io.InputStream;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.monitor.FileAlterationListener;
-import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
-import org.apache.commons.io.monitor.FileAlterationMonitor;
-import org.apache.commons.io.monitor.FileAlterationObserver;
-import org.apache.commons.lang3.BooleanUtils;
-import org.apache.commons.lang3.ClassUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
 import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
 import org.apache.syncope.client.enduser.pages.BasePage;
@@ -52,13 +36,14 @@
 import org.apache.syncope.client.enduser.pages.MustChangePassword;
 import org.apache.syncope.client.enduser.pages.SelfConfirmPasswordReset;
 import org.apache.syncope.client.enduser.panels.Sidebar;
+import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.client.ui.commons.SyncopeUIRequestCycleListener;
 import org.apache.syncope.client.ui.commons.annotations.Resource;
 import org.apache.syncope.client.ui.commons.themes.AdminLTE;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
-import org.apache.syncope.common.lib.PropertyUtils;
 import org.apache.wicket.Page;
 import org.apache.wicket.Session;
 import org.apache.wicket.WicketRuntimeException;
@@ -79,188 +64,41 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.ResourceLoader;
 import org.springframework.stereotype.Component;
 
 @Component
 public class SyncopeWebApplication extends WicketBootStandardWebApplication {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWebApplication.class);
-
-    private static final String ENDUSER_PROPERTIES = "enduser.properties";
-
-    private static final String CUSTOM_FORM_LAYOUT_FILE = "customFormLayout.json";
+    protected static final Logger LOG = LoggerFactory.getLogger(SyncopeWebApplication.class);
 
     public static final List<Locale> SUPPORTED_LOCALES = List.of(
             Locale.ENGLISH, Locale.ITALIAN, new Locale("pt", "BR"), new Locale("ru"), Locale.JAPANESE);
 
-    private static final ObjectMapper MAPPER = new ObjectMapper();
+    protected static final ObjectMapper MAPPER = new ObjectMapper();
 
     public static SyncopeWebApplication get() {
         return (SyncopeWebApplication) WebApplication.get();
     }
 
     @Autowired
-    private ClassPathScanImplementationLookup lookup;
+    protected ResourceLoader resourceLoader;
 
     @Autowired
-    private ServiceOps serviceOps;
+    protected EnduserProperties props;
 
-    @Value("${adminUser}")
-    private String adminUser;
+    @Autowired
+    protected ClassPathScanImplementationLookup lookup;
 
-    @Value("${anonymousUser}")
-    protected String anonymousUser;
+    @Autowired
+    protected ServiceOps serviceOps;
 
-    @Value("${anonymousKey}")
-    protected String anonymousKey;
-
-    @Value("${useGZIPCompression:false}")
-    protected boolean useGZIPCompression;
-
-    @Value("${captchaEnabled:false}")
-    private boolean captchaEnabled;
-
-    @Value("${maxUploadFileSizeMB:#{null}}")
-    protected Integer maxUploadFileSizeMB;
-
-    @Value("${maxWaitTime:30}")
-    protected Integer maxWaitTime;
-
-    @Value("${corePoolSize:5}")
-    protected Integer corePoolSize;
-
-    @Value("${maxPoolSize:10}")
-    protected Integer maxPoolSize;
-
-    @Value("${queueCapacity:50}")
-    protected Integer queueCapacity;
-
-    private FileAlterationMonitor customFormLayoutMonitor;
-
-    private Map<String, Class<? extends BasePage>> pageClasses;
-
-    private Class<? extends Sidebar> sidebar;
-
-    private UserFormLayoutInfo customFormLayout;
-
-    @SuppressWarnings("unchecked")
-    protected void populatePageClasses(final Properties props) {
-        Enumeration<String> propNames = (Enumeration<String>) props.propertyNames();
-        while (propNames.hasMoreElements()) {
-            String className = propNames.nextElement();
-            if (className.startsWith("page.")) {
-                try {
-                    Class<?> clazz = ClassUtils.getClass(props.getProperty(className));
-                    if (BasePage.class.isAssignableFrom(clazz)) {
-                        pageClasses.put(
-                                StringUtils.substringAfter(className, "page."), (Class<? extends BasePage>) clazz);
-                    } else {
-                        LOG.warn("{} does not extend {}, ignoring...", clazz.getName(), BasePage.class.getName());
-                    }
-                } catch (ClassNotFoundException e) {
-                    LOG.error("While looking for class identified by property '{}'", className, e);
-                }
-            }
-        }
-    }
-
-    protected static void setSecurityHeaders(final Properties props, final WebResponse response) {
-        @SuppressWarnings("unchecked")
-        Enumeration<String> propNames = (Enumeration<String>) props.propertyNames();
-        while (propNames.hasMoreElements()) {
-            String name = propNames.nextElement();
-            if (name.startsWith("security.headers.")) {
-                response.setHeader(StringUtils.substringAfter(name, "security.headers."), props.getProperty(name));
-            }
-        }
-    }
+    protected UserFormLayoutInfo customFormLayout;
 
     @Override
     protected void init() {
         super.init();
 
-        // read enduser.properties
-        Properties props = PropertyUtils.read(getClass(), ENDUSER_PROPERTIES, "enduser.directory");
-
-        // read customFormLayout.json
-        try (InputStream is = SyncopeWebApplication.class.getResourceAsStream('/' + CUSTOM_FORM_LAYOUT_FILE)) {
-            customFormLayout = MAPPER.readValue(is, new TypeReference<>() {
-            });
-            File enduserDir = new File(props.getProperty("enduser.directory"));
-            boolean existsEnduserDir = enduserDir.exists() && enduserDir.canRead() && enduserDir.isDirectory();
-            if (existsEnduserDir) {
-                File customFormLayoutFile = FileUtils.getFile(enduserDir, CUSTOM_FORM_LAYOUT_FILE);
-                if (customFormLayoutFile.exists()
-                        && customFormLayoutFile.canRead()
-                        && customFormLayoutFile.isFile()) {
-                    customFormLayout = MAPPER.readValue(FileUtils.openInputStream(customFormLayoutFile),
-                        new TypeReference<>() {
-                        });
-                }
-            }
-            FileAlterationObserver observer = existsEnduserDir
-                    ? new FileAlterationObserver(
-                            enduserDir,
-                            pathname -> StringUtils.contains(pathname.getPath(), CUSTOM_FORM_LAYOUT_FILE))
-                    : new FileAlterationObserver(
-                            SyncopeWebApplication.class.getResource('/' + CUSTOM_FORM_LAYOUT_FILE).getFile(),
-                            pathname -> StringUtils.contains(pathname.getPath(), CUSTOM_FORM_LAYOUT_FILE));
-
-            customFormLayoutMonitor = new FileAlterationMonitor(5000);
-
-            FileAlterationListener listener = new FileAlterationListenerAdaptor() {
-
-                @Override
-                public void onFileChange(final File file) {
-                    try {
-                        LOG.trace("{} has changed. Reloading form attributes customization configuration.",
-                                CUSTOM_FORM_LAYOUT_FILE);
-                        customFormLayout = MAPPER.readValue(FileUtils.openInputStream(file),
-                            new TypeReference<>() {
-                            });
-                    } catch (IOException e) {
-                        LOG.error("{} While reading app customization configuration.",
-                                CUSTOM_FORM_LAYOUT_FILE, e);
-                    }
-                }
-
-                @Override
-                public void onFileCreate(final File file) {
-                    try {
-                        LOG.trace("{} has been created. Loading form attributes customization configuration.",
-                                CUSTOM_FORM_LAYOUT_FILE);
-                        customFormLayout = MAPPER.readValue(FileUtils.openInputStream(file),
-                            new TypeReference<>() {
-                            });
-                    } catch (IOException e) {
-                        LOG.error("{} While reading app customization configuration.",
-                                CUSTOM_FORM_LAYOUT_FILE, e);
-                    }
-                }
-
-                @Override
-                public void onFileDelete(final File file) {
-                    LOG.trace("{} has been deleted. Resetting form attributes customization configuration.",
-                            CUSTOM_FORM_LAYOUT_FILE);
-                    customFormLayout = null;
-                }
-            };
-
-            observer.addListener(listener);
-            customFormLayoutMonitor.addObserver(observer);
-            customFormLayoutMonitor.start();
-        } catch (Exception e) {
-            throw new WicketRuntimeException("Could not read " + CUSTOM_FORM_LAYOUT_FILE, e);
-        }
-
-        // process page properties
-        pageClasses = new HashMap<>();
-        populatePageClasses(props);
-        pageClasses = Collections.unmodifiableMap(pageClasses);
-
-        buildSidebarClass(props);
-
         // Application settings
         IBootstrapSettings settings = new BootstrapSettings();
 
@@ -295,38 +133,28 @@
             }
         });
 
-        if (BooleanUtils.toBoolean(props.getProperty("x-forward"))) {
+        if (props.isxForward()) {
             XForwardedRequestWrapperFactory.Config config = new XForwardedRequestWrapperFactory.Config();
-            config.setProtocolHeader(props.getProperty("x-forward.protocol.header", HttpHeaders.X_FORWARDED_PROTO));
-            try {
-                config.setHttpServerPort(Integer.valueOf(props.getProperty("x-forward.http.port", "80")));
-            } catch (NumberFormatException e) {
-                LOG.error("Invalid value provided for 'x-forward.http.port': {}",
-                        props.getProperty("x-forward.http.port"));
-                config.setHttpServerPort(80);
-            }
-            try {
-                config.setHttpsServerPort(Integer.valueOf(props.getProperty("x-forward.https.port", "443")));
-            } catch (NumberFormatException e) {
-                LOG.error("Invalid value provided for 'x-forward.https.port': {}",
-                        props.getProperty("x-forward.https.port"));
-                config.setHttpsServerPort(443);
-            }
+            config.setProtocolHeader(props.getxForwardProtocolHeader());
+            config.setHttpServerPort(props.getxForwardHttpPort());
+            config.setHttpsServerPort(props.getxForwardHttpsPort());
 
             XForwardedRequestWrapperFactory factory = new XForwardedRequestWrapperFactory();
             factory.setConfig(config);
             getFilterFactoryManager().add(factory);
         }
 
-        if (BooleanUtils.toBoolean(props.getProperty("csrf"))) {
+        if (props.isCsrf()) {
             getRequestCycleListeners().add(new ResourceIsolationRequestCycleListener());
         }
+
         getRequestCycleListeners().add(new IRequestCycleListener() {
 
             @Override
             public void onEndRequest(final RequestCycle cycle) {
                 if (cycle.getResponse() instanceof WebResponse) {
-                    setSecurityHeaders(props, (WebResponse) cycle.getResponse());
+                    props.getSecurityHeaders().
+                            forEach((name, value) -> ((WebResponse) cycle.getResponse()).setHeader(name, value));
                 }
             }
         });
@@ -354,6 +182,13 @@
             }
         }
 
+        try (InputStream is = resourceLoader.getResource(props.getCustomFormLayout()).getInputStream()) {
+            customFormLayout = MAPPER.readValue(is, new TypeReference<>() {
+            });
+        } catch (Exception e) {
+            throw new WicketRuntimeException("Could not read " + props.getCustomFormLayout(), e);
+        }
+
         // enable component path
         if (getDebugSettings().isAjaxDebugModeEnabled()) {
             getDebugSettings().setComponentPathAttributeName("syncope-path");
@@ -361,17 +196,6 @@
     }
 
     @Override
-    protected void onDestroy() {
-        if (customFormLayoutMonitor != null) {
-            try {
-                customFormLayoutMonitor.stop(0);
-            } catch (Exception e) {
-                LOG.error("{} While stopping file monitor", CUSTOM_FORM_LAYOUT_FILE, e);
-            }
-        }
-    }
-
-    @Override
     public Class<? extends Page> getHomePage() {
         return SyncopeEnduserSession.get().isAuthenticated()
                 && SyncopeEnduserSession.get().isMustChangePassword()
@@ -385,26 +209,12 @@
         return lookup;
     }
 
-    @SuppressWarnings("unchecked")
-    private void buildSidebarClass(final Properties props) {
-        try {
-            Class<?> clazz = ClassUtils.getClass(props.getProperty("sidebar", Sidebar.class.getCanonicalName()));
-            if (Sidebar.class.isAssignableFrom(clazz)) {
-                sidebar = (Class<? extends Sidebar>) clazz;
-            } else {
-                LOG.warn("{} does not extend {}, ignoring...", clazz.getName(), Sidebar.class.getName());
-            }
-        } catch (ClassNotFoundException e) {
-            LOG.error("While looking for class identified by property 'sidebar'", e);
-        }
-    }
-
     public UserFormLayoutInfo getCustomFormLayout() {
         return customFormLayout;
     }
 
     public Class<? extends Sidebar> getSidebar() {
-        return sidebar;
+        return props.getSidebar();
     }
 
     @Override
@@ -412,18 +222,23 @@
         return new SyncopeEnduserSession(request);
     }
 
+    public SyncopeClient newAnonymousClient() {
+        return newClientFactory().create(
+                new AnonymousAuthenticationHandler(props.getAnonymousUser(), props.getAnonymousKey()));
+    }
+
     public SyncopeClientFactoryBean newClientFactory() {
         return new SyncopeClientFactoryBean().
                 setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
-                setUseCompression(useGZIPCompression);
+                setUseCompression(props.isUseGZIPCompression());
     }
 
-    public Class<? extends BasePage> getPageClass(final String key) {
-        return pageClasses.get(key);
+    public Class<? extends BasePage> getPageClass(final String name) {
+        return props.getPage().get(name);
     }
 
-    public Class<? extends BasePage> getPageClass(final String key, final Class<? extends BasePage> defaultValue) {
-        return pageClasses.getOrDefault(key, defaultValue);
+    public Class<? extends BasePage> getPageClass(final String name, final Class<? extends BasePage> defaultValue) {
+        return props.getPage().getOrDefault(name, defaultValue);
     }
 
     protected Class<? extends WebPage> getSignInPageClass() {
@@ -431,39 +246,26 @@
     }
 
     public String getAdminUser() {
-        return adminUser;
+        return props.getAdminUser();
     }
 
     public String getAnonymousUser() {
-        return anonymousUser;
+        return props.getAnonymousUser();
     }
 
     public String getAnonymousKey() {
-        return anonymousKey;
+        return props.getAnonymousKey();
     }
 
     public boolean isCaptchaEnabled() {
-        return captchaEnabled;
+        return props.isCaptcha();
+    }
+
+    public long getMaxWaitTimeInSeconds() {
+        return props.getMaxWaitTimeOnApplyChanges();
     }
 
     public Integer getMaxUploadFileSizeMB() {
-        return maxUploadFileSizeMB;
+        return props.getMaxUploadFileSizeMB();
     }
-
-    public Integer getCorePoolSize() {
-        return corePoolSize;
-    }
-
-    public Integer getMaxPoolSize() {
-        return maxPoolSize;
-    }
-
-    public Integer getQueueCapacity() {
-        return queueCapacity;
-    }
-
-    public Integer getMaxWaitTimeInSeconds() {
-        return maxWaitTime;
-    }
-
 }
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/actuate/SyncopeEnduserInfoContributor.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/actuate/SyncopeEnduserInfoContributor.java
new file mode 100644
index 0000000..6d4db1b
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/actuate/SyncopeEnduserInfoContributor.java
@@ -0,0 +1,37 @@
+/*
+ * 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.enduser.actuate;
+
+import org.apache.syncope.client.enduser.EnduserProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.info.Info;
+import org.springframework.boot.actuate.info.InfoContributor;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+public class SyncopeEnduserInfoContributor implements InfoContributor {
+
+    @Autowired
+    protected EnduserProperties enduserProperties;
+
+    @PreAuthorize("isAuthenticated()")
+    @Override
+    public void contribute(final Info.Builder builder) {
+        builder.withDetail("enduserProperties", enduserProperties);
+    }
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
index dbd60c2..751e76e 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
@@ -296,11 +296,6 @@
     }
 
     @Override
-    protected Integer getMaxUploadFileSizeMB() {
-        return SyncopeWebApplication.get().getMaxUploadFileSizeMB();
-    }
-
-    @Override
     public FieldPanel<String> setReadOnly(final boolean readOnly) {
         super.setReadOnly(readOnly);
         fileUpload.setEnabled(!readOnly);
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff
deleted file mode 100644
index 2132b5e..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff2
deleted file mode 100644
index 943f826..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300.woff2
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff
deleted file mode 100644
index aa25cd3..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff2
deleted file mode 100644
index 441997f..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-300italic.woff2
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff
deleted file mode 100644
index 24d2824..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff2
deleted file mode 100644
index 9ec7d25..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600.woff2
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff
deleted file mode 100644
index ce5a1cc..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff2
deleted file mode 100644
index 7ed2f82..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-600italic.woff2
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff
deleted file mode 100644
index 9fbfe68..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff2
deleted file mode 100644
index 096dcb1..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-700.woff2
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff
deleted file mode 100644
index c1cf1ea..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff2
deleted file mode 100644
index ff006be..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-italic.woff2
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff
deleted file mode 100644
index e8a1ac7..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff2 b/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff2
deleted file mode 100644
index 1b0bc46..0000000
--- a/client/idrepo/enduser/src/main/resources/META-INF/resources/css/fonts/source-sans-pro-v11-latin-ext_cyrillic_latin_cyrillic-ext-regular.woff2
+++ /dev/null
Binary files differ
diff --git a/client/idrepo/enduser/src/main/resources/application.properties b/client/idrepo/enduser/src/main/resources/application.properties
deleted file mode 100644
index 719fd4b..0000000
--- a/client/idrepo/enduser/src/main/resources/application.properties
+++ /dev/null
@@ -1,32 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} Enduser
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.servlet.encoding.charset=UTF-8
-server.servlet.encoding.enabled=true
-server.servlet.encoding.force=true
-
-server.servlet.contextPath=/syncope-enduser
-
-management.endpoints.web.exposure.include=info,health,loggers
-management.endpoint.health.show-details=ALWAYS
-
-service.discovery.address=http://localhost:8080/syncope-enduser/
-
-wicket.core.csrf.enabled=false
diff --git a/client/idrepo/enduser/src/main/resources/enduser.properties b/client/idrepo/enduser/src/main/resources/enduser.properties
index b4061af..a10cbce 100644
--- a/client/idrepo/enduser/src/main/resources/enduser.properties
+++ b/client/idrepo/enduser/src/main/resources/enduser.properties
@@ -14,29 +14,49 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-enduser.directory=${conf.directory}
+spring.application.name=Apache Syncope ${syncope.version} Enduser
+spring.groovy.template.check-template-location=false
+spring.main.banner-mode=log
 
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-adminUser=${adminUser}
-useGZIPCompression=true
-maxUploadFileSizeMB=5
+server.servlet.encoding.charset=UTF-8
+server.servlet.encoding.enabled=true
+server.servlet.encoding.force=true
 
-x-forward=true
-captcha=true
-csrf=true
+server.servlet.contextPath=/syncope-enduser
+
+management.endpoints.web.exposure.include=info,health,loggers
+management.endpoint.health.show-details=ALWAYS
+
+service.discovery.address=http://localhost:8080/syncope-enduser/
+
+wicket.core.csrf.enabled=false
+
+logging.config=classpath:/log4j2.xml
+
+enduser.anonymousUser=${anonymousUser}
+enduser.anonymousKey=${anonymousKey}
+enduser.adminUser=${adminUser}
+enduser.useGZIPCompression=true
+enduser.maxUploadFileSizeMB=5
+
+enduser.x-forward=true
+enduser.captcha=true
+enduser.csrf=true
 
 # Sidebar
-sidebar=org.apache.syncope.client.enduser.panels.Sidebar
+enduser.sidebar=org.apache.syncope.client.enduser.panels.Sidebar
 
 # Page
-page.profile=org.apache.syncope.client.enduser.pages.Dashboard
-page.edituser=org.apache.syncope.client.enduser.pages.EditUser
-page.editchangepassword=org.apache.syncope.client.enduser.pages.EditChangePassword
-page.editsecurityquestion=org.apache.syncope.client.enduser.pages.EditSecurityQuestion
+enduser.page.profile=org.apache.syncope.client.enduser.pages.Dashboard
+enduser.page.edituser=org.apache.syncope.client.enduser.pages.EditUser
+enduser.page.editchangepassword=org.apache.syncope.client.enduser.pages.EditChangePassword
+enduser.page.editsecurityquestion=org.apache.syncope.client.enduser.pages.EditSecurityQuestion
 
-security.headers.X-XSS-Protection=1; mode=block
-security.headers.Strict-Transport-Security=max-age=31536000; includeSubDomains; preload
-security.headers.X-Content-Type-Options=nosniff
-security.headers.X-Frame-Options=sameorigin
-#security.headers.Content-Security-Policy=default-src https:
+# Custom Form layout
+enduser.custom-form-layout=classpath:/customFormLayout.json
+
+enduser.security.headers.X-XSS-Protection=1; mode=block
+enduser.security.headers.Strict-Transport-Security=max-age=31536000; includeSubDomains; preload
+enduser.security.headers.X-Content-Type-Options=nosniff
+enduser.security.headers.X-Frame-Options=sameorigin
+#enduser.security.headers.Content-Security-Policy=default-src https:
diff --git a/client/idrepo/enduser/src/main/resources/oidcclient-agent.properties b/client/idrepo/enduser/src/main/resources/oidcclient-agent.properties
deleted file mode 100644
index 17775f8..0000000
--- a/client/idrepo/enduser/src/main/resources/oidcclient-agent.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-conf.directory=${conf.directory}
-
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-
-scheme=https
-host=localhost
-port=8443
-rootPath=/syncope/rest/
-useGZIPCompression=true
diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/AbstractTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/AbstractTest.java
index 14cdd07..9ae41d8 100644
--- a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/AbstractTest.java
+++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/AbstractTest.java
@@ -29,10 +29,8 @@
 import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties;
 import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator.WicketEndpointRepositoryDefault;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Properties;
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.cxf.jaxrs.client.Client;
 import org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
@@ -70,6 +68,23 @@
     public static class SyncopeEnduserWebApplicationTestConfig {
 
         @Bean
+        public EnduserProperties enduserProperties() {
+            EnduserProperties enduserProperties = new EnduserProperties();
+
+            enduserProperties.getSecurityHeaders().put("X-XSS-Protection", "1; mode=block");
+            enduserProperties.getSecurityHeaders().
+                    put("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
+            enduserProperties.getSecurityHeaders().put("X-Content-Type-Options", "nosniff");
+            enduserProperties.getSecurityHeaders().put("X-Frame-Options", "sameorigin");
+
+            enduserProperties.setAdminUser("admin");
+
+            enduserProperties.setAnonymousUser("anonymousUser");
+
+            return enduserProperties;
+        }
+
+        @Bean
         public ServiceOps selfServiceOps() {
             return mock(ServiceOps.class);
         }
@@ -223,25 +238,18 @@
         }
     }
 
-    protected static Properties PROPS;
+    protected static EnduserProperties PROPS;
 
     protected static WicketTester TESTER;
 
     @BeforeAll
-    public static void loadProps() throws IOException {
-        PROPS = new Properties();
-        try (InputStream is = AbstractTest.class.getResourceAsStream("/enduser.properties")) {
-            PROPS.load(is);
-        }
-    }
-
-    @BeforeAll
     public static void setupTester() throws IOException {
         AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
         ctx.register(SyncopeEnduserWebApplicationTestConfig.class);
         ctx.register(TestSyncopeWebApplication.class);
         ctx.refresh();
 
+        PROPS = ctx.getBean(EnduserProperties.class);
         TESTER = new WicketTester(ctx.getBean(SyncopeWebApplication.class));
     }
 }
diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/SyncopeEnduserApplicationTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/SyncopeEnduserApplicationTest.java
index 1323395..fa678ca 100644
--- a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/SyncopeEnduserApplicationTest.java
+++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/SyncopeEnduserApplicationTest.java
@@ -42,24 +42,9 @@
 
 public class SyncopeEnduserApplicationTest extends AbstractTest {
 
-    private Map<String, String> getConfiguredSecurityHeaders() throws IOException {
-        Map<String, String> securityHeaders = new HashMap<>();
-
-        @SuppressWarnings("unchecked")
-        Enumeration<String> propNames = (Enumeration<String>) PROPS.propertyNames();
-        while (propNames.hasMoreElements()) {
-            String name = propNames.nextElement();
-            if (name.startsWith("security.headers.")) {
-                securityHeaders.put(StringUtils.substringAfter(name, "security.headers."), PROPS.getProperty(name));
-            }
-        }
-
-        return securityHeaders;
-    }
-
     @Test
     public void securityHeaders() throws IOException {
-        Map<String, String> securityHeaders = getConfiguredSecurityHeaders();
+        Map<String, String> securityHeaders = PROPS.getSecurityHeaders();
         assertEquals(4, securityHeaders.size());
 
         // 1. anonymous
diff --git a/client/idrepo/enduser/src/test/resources/application-debug.properties b/client/idrepo/enduser/src/test/resources/application-debug.properties
deleted file mode 100644
index fc1b6e4..0000000
--- a/client/idrepo/enduser/src/test/resources/application-debug.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
-
-server.port=9091
-service.discovery.address=http://localhost:9091/syncope-enduser/
diff --git a/client/idrepo/console/src/test/resources/application-debug.properties b/client/idrepo/enduser/src/test/resources/enduser-debug.properties
similarity index 91%
copy from client/idrepo/console/src/test/resources/application-debug.properties
copy to client/idrepo/enduser/src/test/resources/enduser-debug.properties
index 1ac160d..f03153d 100644
--- a/client/idrepo/console/src/test/resources/application-debug.properties
+++ b/client/idrepo/enduser/src/test/resources/enduser-debug.properties
@@ -18,5 +18,7 @@
 keymaster.username=${anonymousUser}
 keymaster.password=${anonymousKey}
 
-server.port=9090
+server.port=9091
 service.discovery.address=http://localhost:9090/syncope-console/
+
+logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
diff --git a/client/idrepo/enduser/src/test/resources/log4j2.xml b/client/idrepo/enduser/src/test/resources/log4j2.xml
index 1da7c54..debaf21 100644
--- a/client/idrepo/enduser/src/test/resources/log4j2.xml
+++ b/client/idrepo/enduser/src/test/resources/log4j2.xml
@@ -21,8 +21,8 @@
 
   <appenders>
 
-    <Console name="main" target="SYSTEM_OUT">
-      <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger - %msg%n"/>
+    <Console name="console" target="SYSTEM_OUT" follow="true">
+      <PatternLayout pattern="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} [%11.11t] %style{%-60.60c{60}}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
     </Console>
     
   </appenders>
@@ -30,23 +30,23 @@
   <loggers>
 
     <asyncLogger name="org.apache.syncope.client.lib" additivity="false" level="OFF">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
 
-    <asyncLogger name="org.apache.syncope.client.enduser" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
+    <asyncLogger name="org.apache.syncope.client.enduser" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
     </asyncLogger>
 
     <asyncLogger name="org.apache.wicket" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
     
     <asyncLogger name="org.apache.cxf" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
     
-    <root level="ERROR">
-      <appender-ref ref="main"/>
+    <root level="INFO">
+      <appender-ref ref="console"/>
     </root>
   
   </loggers>
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/PropertyUtils.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/PropertyUtils.java
deleted file mode 100644
index 619c07c..0000000
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/PropertyUtils.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.common.lib;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.util.Properties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Utility class for manipulating properties files.
- */
-public final class PropertyUtils {
-
-    private static final Logger LOG = LoggerFactory.getLogger(PropertyUtils.class);
-
-    /**
-     * Reads the property file with the given name from the directory assigned to the given property, or
-     * from the classpath if not found.
-     *
-     * @param clazz calling class, to access classpath
-     * @param propertiesFileName property file name
-     * @param confDirProp property name for the configuration directory
-     * @return properties, either from configuration directory, or from classpath
-     */
-    public static Properties read(
-            final Class<?> clazz, final String propertiesFileName, final String confDirProp) {
-
-        Properties props = new Properties();
-
-        try (InputStream is = clazz.getResourceAsStream('/' + propertiesFileName)) {
-            props.load(is);
-
-            String confDirName = props.getProperty(confDirProp);
-            if (confDirName != null) {
-                File confDir = new File(confDirName);
-                if (confDir.exists() && confDir.canRead() && confDir.isDirectory()) {
-                    File confDirProps = new File(confDir, propertiesFileName);
-                    if (confDirProps.exists() && confDirProps.canRead() && confDirProps.isFile()) {
-                        props.clear();
-                        try (FileInputStream fis = new FileInputStream(confDirProps)) {
-                            props.load(fis);
-                        }
-                    }
-                } else {
-                    LOG.warn("{} not existing, unreadable or not a directory, ignoring", confDirName);
-                }
-            }
-        } catch (Exception e) {
-            throw new RuntimeException("Could not read " + propertiesFileName, e);
-        }
-
-        return props;
-    }
-
-    /**
-     * Private default constructor, for static-only classes.
-     */
-    private PropertyUtils() {
-    }
-}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/SyncopeProperties.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/SyncopeProperties.java
new file mode 100644
index 0000000..b3c3654
--- /dev/null
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/SyncopeProperties.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib;
+
+public abstract class SyncopeProperties {
+
+    private String anonymousUser;
+
+    private String anonymousKey;
+
+    private boolean useGZIPCompression = true;
+
+    public String getAnonymousUser() {
+        return anonymousUser;
+    }
+
+    public void setAnonymousUser(final String anonymousUser) {
+        this.anonymousUser = anonymousUser;
+    }
+
+    public String getAnonymousKey() {
+        return anonymousKey;
+    }
+
+    public void setAnonymousKey(final String anonymousKey) {
+        this.anonymousKey = anonymousKey;
+    }
+
+    public boolean isUseGZIPCompression() {
+        return useGZIPCompression;
+    }
+
+    public void setUseGZIPCompression(final boolean useGZIPCompression) {
+        this.useGZIPCompression = useGZIPCompression;
+    }
+}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractStartEndBean.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractStartEndBean.java
index 5a36ebb..f88e54a 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractStartEndBean.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractStartEndBean.java
@@ -23,6 +23,8 @@
 import java.util.Optional;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
 import org.apache.syncope.common.lib.BaseBean;
 
 public class AbstractStartEndBean implements BaseBean {
@@ -76,4 +78,12 @@
                 append(end, other.end).
                 build();
     }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.SIMPLE_STYLE).
+                append(start).
+                append(end).
+                build();
+    }
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/MembershipTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/MembershipTO.java
index d129b21..591f2ad 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/MembershipTO.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/MembershipTO.java
@@ -186,8 +186,8 @@
     @Override
     public String toString() {
         return new ToStringBuilder(this, ToStringStyle.SIMPLE_STYLE).
-                append(this.groupKey).
-                append(this.groupName).
+                append(groupKey).
+                append(groupName).
                 build();
     }
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/PropagationTaskTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/PropagationTaskTO.java
index ecf8f5a..b02a396 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/PropagationTaskTO.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/PropagationTaskTO.java
@@ -23,6 +23,8 @@
 import io.swagger.v3.oas.annotations.media.Schema;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 
@@ -178,4 +180,20 @@
                 append(entityKey, other.entityKey).
                 build();
     }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.SIMPLE_STYLE).
+                appendSuper(super.toString()).
+                append(operation).
+                append(connObjectKey).
+                append(oldConnObjectKey).
+                append(attributes).
+                append(resource).
+                append(objectClassName).
+                append(anyTypeKind).
+                append(anyType).
+                append(entityKey).
+                build();
+    }
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/TaskTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/TaskTO.java
index f10b0ca..0f55ada 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/TaskTO.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/TaskTO.java
@@ -29,6 +29,8 @@
 import javax.ws.rs.PathParam;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "_class")
 @JsonPropertyOrder(value = { "_class", "key" })
@@ -128,4 +130,16 @@
                 append(lastExecutor, other.lastExecutor).
                 build();
     }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.SIMPLE_STYLE).
+                appendSuper(super.toString()).
+                append(key).
+                append(discriminator).
+                append(executions).
+                append(latestExecStatus).
+                append(lastExecutor).
+                build();
+    }
 }
diff --git a/common/keymaster/client-api/pom.xml b/common/keymaster/client-api/pom.xml
index 37fdb6b..201420b 100644
--- a/common/keymaster/client-api/pom.xml
+++ b/common/keymaster/client-api/pom.xml
@@ -45,8 +45,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-context</artifactId>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot</artifactId>
     </dependency>
   </dependencies>
 
diff --git a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/KeymasterProperties.java b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/KeymasterProperties.java
new file mode 100644
index 0000000..1878eba
--- /dev/null
+++ b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/KeymasterProperties.java
@@ -0,0 +1,75 @@
+/*
+ * 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.keymaster.client.api;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("keymaster")
+public class KeymasterProperties {
+
+    private String address;
+
+    private String username;
+
+    private String password;
+
+    private int baseSleepTimeMs = 100;
+
+    private int maxRetries = 3;
+
+    public String getAddress() {
+        return address;
+    }
+
+    public void setAddress(final String address) {
+        this.address = address;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(final String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(final String password) {
+        this.password = password;
+    }
+
+    public int getBaseSleepTimeMs() {
+        return baseSleepTimeMs;
+    }
+
+    public void setBaseSleepTimeMs(final int baseSleepTimeMs) {
+        this.baseSleepTimeMs = baseSleepTimeMs;
+    }
+
+    public int getMaxRetries() {
+        return maxRetries;
+    }
+
+    public void setMaxRetries(final int maxRetries) {
+        this.maxRetries = maxRetries;
+    }
+}
diff --git a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperKeymasterClientContext.java b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperKeymasterClientContext.java
index 3a56aa5..079d8ea 100644
--- a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperKeymasterClientContext.java
+++ b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperKeymasterClientContext.java
@@ -21,6 +21,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
 import javax.security.auth.login.AppConfigurationEntry;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.curator.framework.CuratorFramework;
@@ -29,41 +30,46 @@
 import org.apache.curator.retry.ExponentialBackoffRetry;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
+import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
 import org.apache.zookeeper.ZooDefs;
 import org.apache.zookeeper.data.ACL;
 import org.apache.zookeeper.server.auth.DigestLoginModule;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
+import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.context.annotation.Conditional;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.type.AnnotatedTypeMetadata;
 
-@PropertySource("classpath:keymaster.properties")
-@PropertySource(value = "file:${conf.directory}/keymaster.properties", ignoreResourceNotFound = true)
+@EnableConfigurationProperties(KeymasterProperties.class)
 @Configuration
 public class ZookeeperKeymasterClientContext {
 
-    @Value("${keymaster.address}")
-    private String address;
+    private static final Pattern IPV4 = Pattern.compile(
+            "^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$");
 
-    @Value("${keymaster.username}")
-    private String username;
+    static class ZookeeperCondition extends SpringBootCondition {
 
-    @Value("${keymaster.password}")
-    private String password;
+        @Override
+        public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
+            String keymasterAddress = context.getEnvironment().getProperty("keymaster.address");
+            return new ConditionOutcome(
+                    keymasterAddress != null && IPV4.matcher(keymasterAddress).matches(),
+                    "Keymaster address not set for Zookeeper: " + keymasterAddress);
+        }
+    }
 
-    @Value("${keymaster.baseSleepTimeMs:100}")
-    private Integer baseSleepTimeMs;
+    @Autowired
+    private KeymasterProperties props;
 
-    @Value("${keymaster.maxRetries:3}")
-    private Integer maxRetries;
-
-    @ConditionalOnExpression("#{'${keymaster.address}' "
-            + "matches '^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$'}")
+    @Conditional(ZookeeperCondition.class)
     @Bean
     public CuratorFramework curatorFramework() throws InterruptedException {
-        if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
+        if (StringUtils.isNotBlank(props.getUsername()) && StringUtils.isNotBlank(props.getPassword())) {
             javax.security.auth.login.Configuration.setConfiguration(new javax.security.auth.login.Configuration() {
 
                 private final AppConfigurationEntry[] entries = {
@@ -71,8 +77,8 @@
                     DigestLoginModule.class.getName(),
                     AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
                     Map.of(
-                    "username", username,
-                    "password", password
+                    "username", props.getUsername(),
+                    "password", props.getPassword()
                     ))
                 };
 
@@ -84,10 +90,10 @@
         }
 
         CuratorFrameworkFactory.Builder clientBuilder = CuratorFrameworkFactory.builder().
-                connectString(address).
-                retryPolicy(new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries));
-        if (StringUtils.isNotBlank(username)) {
-            clientBuilder.authorization("digest", username.getBytes()).aclProvider(new ACLProvider() {
+                connectString(props.getAddress()).
+                retryPolicy(new ExponentialBackoffRetry(props.getBaseSleepTimeMs(), props.getMaxRetries()));
+        if (StringUtils.isNotBlank(props.getUsername())) {
+            clientBuilder.authorization("digest", props.getUsername().getBytes()).aclProvider(new ACLProvider() {
 
                 @Override
                 public List<ACL> getDefaultAcl() {
@@ -107,23 +113,20 @@
         return client;
     }
 
-    @ConditionalOnExpression("#{'${keymaster.address}' "
-            + "matches '^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$'}")
+    @Conditional(ZookeeperCondition.class)
     @Bean
     public ConfParamOps selfConfParamOps() {
         return new ZookeeperConfParamOps();
     }
 
-    @ConditionalOnExpression("#{'${keymaster.address}' "
-            + "matches '^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$'}")
+    @Conditional(ZookeeperCondition.class)
     @Bean
     public ServiceOps serviceOps() {
         return new ZookeeperServiceDiscoveryOps();
         //return new ZookeeperServiceOps();
     }
 
-    @ConditionalOnExpression("#{'${keymaster.address}' "
-            + "matches '^((\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})|[a-z\\.]+):[0-9]+$'}")
+    @Conditional(ZookeeperCondition.class)
     @Bean
     public DomainOps domainOps() {
         return new ZookeeperDomainOps();
diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOpsTest.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOpsTest.java
index e147085..4308478 100644
--- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOpsTest.java
+++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOpsTest.java
@@ -24,6 +24,6 @@
 
     @BeforeAll
     public static void setUp() throws Exception {
-        ZookeeperTestServer.start();
+        ZookeeperTestingServer.start();
     }
 }
diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsTest.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsTest.java
index a96d2b1..217bd25 100644
--- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsTest.java
+++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsTest.java
@@ -24,6 +24,6 @@
 
     @BeforeAll
     public static void setUp() throws Exception {
-        ZookeeperTestServer.start();
+        ZookeeperTestingServer.start();
     }
 }
diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOpsTest.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOpsTest.java
index 95364ba..a1ecaba 100644
--- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOpsTest.java
+++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOpsTest.java
@@ -24,6 +24,6 @@
 
     @BeforeAll
     public static void setUp() throws Exception {
-        ZookeeperTestServer.start();
+        ZookeeperTestingServer.start();
     }
 }
diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestContext.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestContext.java
index bf453a4..7b43155 100644
--- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestContext.java
+++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestContext.java
@@ -21,7 +21,9 @@
 import org.apache.syncope.common.keymaster.client.api.DomainWatcher;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
 
+@PropertySource("classpath:test.properties")
 @Configuration
 public class ZookeeperTestContext {
 
diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestServer.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestingServer.java
similarity index 94%
rename from common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestServer.java
rename to common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestingServer.java
index 996c9d5..6fe69af 100644
--- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestServer.java
+++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperTestingServer.java
@@ -30,7 +30,7 @@
 import org.apache.curator.test.InstanceSpec;
 import org.apache.curator.test.TestingServer;
 
-public class ZookeeperTestServer {
+public class ZookeeperTestingServer {
 
     private static TestingServer ZK_SERVER;
 
@@ -38,14 +38,14 @@
         if (ZK_SERVER == null) {
             AtomicReference<String> username = new AtomicReference<>();
             AtomicReference<String> password = new AtomicReference<>();
-            try (InputStream propStream = ZookeeperServiceOpsTest.class.getResourceAsStream("/keymaster.properties")) {
+            try (InputStream propStream = ZookeeperServiceOpsTest.class.getResourceAsStream("/test.properties")) {
                 Properties props = new Properties();
                 props.load(propStream);
 
                 username.set(props.getProperty("keymaster.username"));
                 password.set(props.getProperty("keymaster.password"));
             } catch (Exception e) {
-                fail("Could not load /keymaster.properties", e);
+                fail("Could not load /test.properties", e);
             }
 
             Configuration.setConfiguration(new Configuration() {
diff --git a/common/keymaster/client-zookeeper/src/main/resources/keymaster.properties b/common/keymaster/client-zookeeper/src/test/resources/test.properties
similarity index 100%
rename from common/keymaster/client-zookeeper/src/main/resources/keymaster.properties
rename to common/keymaster/client-zookeeper/src/test/resources/test.properties
diff --git a/common/keymaster/pom.xml b/common/keymaster/pom.xml
index d5e1021..901ec64 100644
--- a/common/keymaster/pom.xml
+++ b/common/keymaster/pom.xml
@@ -40,5 +40,6 @@
   <modules>
     <module>client-api</module>
     <module>client-zookeeper</module>
+    <module>self</module>
   </modules>
 </project>
diff --git a/ext/self-keymaster/client/pom.xml b/common/keymaster/self/client-self/pom.xml
similarity index 80%
rename from ext/self-keymaster/client/pom.xml
rename to common/keymaster/self/client-self/pom.xml
index 8ce4722..bd1dee7 100644
--- a/ext/self-keymaster/client/pom.xml
+++ b/common/keymaster/self/client-self/pom.xml
@@ -22,19 +22,19 @@
   <modelVersion>4.0.0</modelVersion>
 
   <parent>
-    <groupId>org.apache.syncope.ext</groupId>
-    <artifactId>syncope-ext-self-keymaster</artifactId>
+    <groupId>org.apache.syncope.common.keymaster</groupId>
+    <artifactId>syncope-common-keymaster-self</artifactId>
     <version>3.0.0-SNAPSHOT</version>
   </parent>
 
-  <name>Apache Syncope Ext: Self Keymaster Client</name>
-  <description>Apache Syncope Ext: Self Keymaster Client</description>
-  <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-  <artifactId>syncope-ext-self-keymaster-client</artifactId>
+  <name>Apache Syncope Common Keymaster Client Self</name>
+  <description>Apache Syncope Common Keymaster Client Self</description>
+  <groupId>org.apache.syncope.common.keymaster.self</groupId>
+  <artifactId>syncope-common-keymaster-client-self</artifactId>
   <packaging>jar</packaging>
   
   <properties>
-    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+    <rootpom.basedir>${basedir}/../../../..</rootpom.basedir>
   </properties>
 
   <dependencies>
@@ -45,8 +45,8 @@
     </dependency>
     
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-rest-api</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-self-rest-api</artifactId>
       <version>${project.version}</version>  
     </dependency>
     
diff --git a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
similarity index 61%
rename from ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
rename to common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
index 6590302..ecd3426 100644
--- a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
+++ b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
@@ -19,70 +19,79 @@
 package org.apache.syncope.common.keymaster.client.self;
 
 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
-
 import java.util.List;
-
+import java.util.regex.Pattern;
 import org.apache.cxf.ext.logging.LoggingFeature;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
+import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.context.annotation.Conditional;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.type.AnnotatedTypeMetadata;
 
-@PropertySource("classpath:keymaster.properties")
-@PropertySource(value = "file:${conf.directory}/keymaster.properties", ignoreResourceNotFound = true)
+@EnableConfigurationProperties(KeymasterProperties.class)
 @Configuration
 public class SelfKeymasterClientContext {
 
-    @Value("${keymaster.address}")
-    private String address;
+    private static final Pattern HTTP = Pattern.compile("^http.+");
 
-    @Value("${keymaster.username}")
-    private String username;
+    static class SelfKeymasterCondition extends SpringBootCondition {
 
-    @Value("${keymaster.password}")
-    private String password;
+        @Override
+        public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
+            String keymasterAddress = context.getEnvironment().getProperty("keymaster.address");
+            return new ConditionOutcome(
+                    keymasterAddress != null && HTTP.matcher(keymasterAddress).matches(),
+                    "Keymaster address not set for Self: " + keymasterAddress);
+        }
+    }
 
-    @ConditionalOnExpression("#{'${keymaster.address}' matches '^http.+'}")
+    @Autowired
+    private KeymasterProperties props;
+
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     @ConditionalOnMissingBean(name = "selfKeymasterRESTClientFactoryBean")
     public JAXRSClientFactoryBean selfKeymasterRESTClientFactoryBean() {
         JAXRSClientFactoryBean restClientFactoryBean = new JAXRSClientFactoryBean();
-        restClientFactoryBean.setAddress(address);
-        restClientFactoryBean.setUsername(username);
-        restClientFactoryBean.setPassword(password);
+        restClientFactoryBean.setAddress(props.getAddress());
+        restClientFactoryBean.setUsername(props.getUsername());
+        restClientFactoryBean.setPassword(props.getPassword());
         restClientFactoryBean.setThreadSafe(true);
         restClientFactoryBean.setInheritHeaders(true);
         restClientFactoryBean.setFeatures(List.of(new LoggingFeature()));
         restClientFactoryBean.setProviders(
-            List.of(new JacksonJsonProvider(), new SelfKeymasterClientExceptionMapper()));
+                List.of(new JacksonJsonProvider(), new SelfKeymasterClientExceptionMapper()));
         return restClientFactoryBean;
     }
 
-    @ConditionalOnExpression("#{'${keymaster.address}' matches '^http.+'}")
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     @ConditionalOnMissingBean(name = "selfConfParamOps")
     public ConfParamOps selfConfParamOps() {
         return new SelfKeymasterConfParamOps(selfKeymasterRESTClientFactoryBean());
     }
 
-    @ConditionalOnExpression("#{'${keymaster.address}' matches '^http.+'}")
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     @ConditionalOnMissingBean(name = "selfServiceOps")
     public ServiceOps selfServiceOps() {
         return new SelfKeymasterServiceOps(selfKeymasterRESTClientFactoryBean(), 5);
     }
 
-    @ConditionalOnExpression("#{'${keymaster.address}' matches '^http.+'}")
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     @ConditionalOnMissingBean(name = "domainOps")
     public DomainOps domainOps() {
         return new SelfKeymasterDomainOps(selfKeymasterRESTClientFactoryBean());
     }
 }
-
diff --git a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientExceptionMapper.java b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientExceptionMapper.java
similarity index 100%
rename from ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientExceptionMapper.java
rename to common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientExceptionMapper.java
diff --git a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java
similarity index 97%
rename from ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java
rename to common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java
index aa3b676..770a442 100644
--- a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java
+++ b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterConfParamOps.java
@@ -28,8 +28,8 @@
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.keymaster.client.api.KeymasterException;
+import org.apache.syncope.common.keymaster.rest.api.service.ConfParamService;
 import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.ext.self.keymaster.api.service.ConfParamService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterDomainOps.java b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterDomainOps.java
similarity index 97%
rename from ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterDomainOps.java
rename to common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterDomainOps.java
index 2a1b759..8c9f268 100644
--- a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterDomainOps.java
+++ b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterDomainOps.java
@@ -25,8 +25,8 @@
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
 import org.apache.syncope.common.keymaster.client.api.KeymasterException;
 import org.apache.syncope.common.keymaster.client.api.model.Domain;
+import org.apache.syncope.common.keymaster.rest.api.service.DomainService;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
-import org.apache.syncope.ext.self.keymaster.api.service.DomainService;
 
 public class SelfKeymasterDomainOps extends SelfKeymasterOps implements DomainOps {
 
diff --git a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterOps.java b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterOps.java
similarity index 100%
rename from ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterOps.java
rename to common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterOps.java
diff --git a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
similarity index 96%
rename from ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
rename to common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
index d52abea..f5b8aa6 100644
--- a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
+++ b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java
@@ -29,8 +29,8 @@
 import org.apache.syncope.common.keymaster.client.api.KeymasterException;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
-import org.apache.syncope.ext.self.keymaster.api.service.NetworkServiceService;
-import org.apache.syncope.ext.self.keymaster.api.service.NetworkServiceService.Action;
+import org.apache.syncope.common.keymaster.rest.api.service.NetworkServiceService;
+import org.apache.syncope.common.keymaster.rest.api.service.NetworkServiceService.Action;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.util.backoff.BackOffExecution;
diff --git a/ext/self-keymaster/client/src/main/resources/META-INF/spring.factories b/common/keymaster/self/client-self/src/main/resources/META-INF/spring.factories
similarity index 100%
rename from ext/self-keymaster/client/src/main/resources/META-INF/spring.factories
rename to common/keymaster/self/client-self/src/main/resources/META-INF/spring.factories
diff --git a/ext/self-keymaster/pom.xml b/common/keymaster/self/pom.xml
similarity index 70%
rename from ext/self-keymaster/pom.xml
rename to common/keymaster/self/pom.xml
index c38f666..693d8d8 100644
--- a/ext/self-keymaster/pom.xml
+++ b/common/keymaster/self/pom.xml
@@ -22,28 +22,23 @@
   <modelVersion>4.0.0</modelVersion>
 
   <parent>
-    <groupId>org.apache.syncope</groupId>
-    <artifactId>syncope-ext</artifactId>
+    <groupId>org.apache.syncope.common</groupId>
+    <artifactId>syncope-common-keymaster</artifactId>
     <version>3.0.0-SNAPSHOT</version>
   </parent>
 
-  <name>Apache Syncope Ext: Self Keymaster</name>
-  <description>Apache Syncope Ext: Self Keymaster</description>
-  <groupId>org.apache.syncope.ext</groupId>
-  <artifactId>syncope-ext-self-keymaster</artifactId>
+  <name>Apache Syncope Common Keymaster Self</name>
+  <description>Apache Syncope Common Keymaster Self</description>
+  <groupId>org.apache.syncope.common.keymaster</groupId>
+  <artifactId>syncope-common-keymaster-self</artifactId>
   <packaging>pom</packaging>
-  
+
   <properties>
-    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
   </properties>
-  
+
   <modules>
     <module>rest-api</module>
-    <module>rest-cxf</module>
-    <module>logic</module>
-    <module>persistence-api</module>
-    <module>persistence-jpa</module>
-    <module>client</module>
+    <module>client-self</module>
   </modules>
-
 </project>
diff --git a/ext/self-keymaster/rest-api/pom.xml b/common/keymaster/self/rest-api/pom.xml
similarity index 80%
rename from ext/self-keymaster/rest-api/pom.xml
rename to common/keymaster/self/rest-api/pom.xml
index 0367873..d507e8e 100644
--- a/ext/self-keymaster/rest-api/pom.xml
+++ b/common/keymaster/self/rest-api/pom.xml
@@ -22,19 +22,19 @@
   <modelVersion>4.0.0</modelVersion>
 
   <parent>
-    <groupId>org.apache.syncope.ext</groupId>
-    <artifactId>syncope-ext-self-keymaster</artifactId>
+    <groupId>org.apache.syncope.common.keymaster</groupId>
+    <artifactId>syncope-common-keymaster-self</artifactId>
     <version>3.0.0-SNAPSHOT</version>
   </parent>
 
-  <name>Apache Syncope Ext: Self Keymaster REST API</name>
-  <description>Apache Syncope Ext: Self Keymaster REST API</description>
-  <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-  <artifactId>syncope-ext-self-keymaster-rest-api</artifactId>
+  <name>Apache Syncope Common Keymaster Self REST API</name>
+  <description>Apache Syncope Common Keymaster Self REST API</description>
+  <groupId>org.apache.syncope.common.keymaster.self</groupId>
+  <artifactId>syncope-common-keymaster-self-rest-api</artifactId>
   <packaging>jar</packaging>
   
   <properties>
-    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+    <rootpom.basedir>${basedir}/../../../..</rootpom.basedir>
   </properties>
 
   <dependencies>
@@ -43,6 +43,7 @@
       <artifactId>syncope-common-idrepo-rest-api</artifactId>
       <version>${project.version}</version>
     </dependency>
+
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
       <artifactId>syncope-common-keymaster-client-api</artifactId>
diff --git a/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/ConfParamService.java b/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/ConfParamService.java
similarity index 96%
rename from ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/ConfParamService.java
rename to common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/ConfParamService.java
index 5d7f9a1..2935d25 100644
--- a/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/ConfParamService.java
+++ b/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/ConfParamService.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.api.service;
+package org.apache.syncope.common.keymaster.rest.api.service;
 
 import java.io.InputStream;
 import java.io.Serializable;
diff --git a/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/DomainService.java b/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/DomainService.java
similarity index 97%
rename from ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/DomainService.java
rename to common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/DomainService.java
index 85e3479..bc0cabb 100644
--- a/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/DomainService.java
+++ b/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/DomainService.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.api.service;
+package org.apache.syncope.common.keymaster.rest.api.service;
 
 import java.io.Serializable;
 import java.util.List;
diff --git a/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/NetworkServiceService.java b/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/NetworkServiceService.java
similarity index 96%
rename from ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/NetworkServiceService.java
rename to common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/NetworkServiceService.java
index d5fcd88..1ac8376 100644
--- a/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/NetworkServiceService.java
+++ b/common/keymaster/self/rest-api/src/main/java/org/apache/syncope/common/keymaster/rest/api/service/NetworkServiceService.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.api.service;
+package org.apache.syncope.common.keymaster.rest.api.service;
 
 import java.io.Serializable;
 import java.util.List;
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 94c70fc..ad4bf4b 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
@@ -27,7 +27,6 @@
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
-import javax.annotation.Resource;
 import javax.ws.rs.InternalServerErrorException;
 import javax.ws.rs.core.HttpHeaders;
 import org.apache.commons.lang3.ArrayUtils;
@@ -56,6 +55,7 @@
 import org.apache.syncope.core.persistence.api.entity.auth.SAML2SPClientApp;
 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.spring.security.SecurityProperties;
 
 @Component
 public class ClientAppLogic extends AbstractTransactionalLogic<ClientAppTO> {
@@ -78,11 +78,8 @@
     @Autowired
     private CASSPDAO casspDAO;
 
-    @Resource(name = "anonymousUser")
-    private String anonymousUser;
-
-    @Resource(name = "anonymousKey")
-    private String anonymousKey;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @PreAuthorize("hasRole('" + AMEntitlement.CLIENTAPP_LIST + "')")
     public <T extends ClientAppTO> List<T> list(final ClientAppType type) {
@@ -260,8 +257,8 @@
             HttpClient.newBuilder().build().send(
                     HttpRequest.newBuilder(URI.create(
                             StringUtils.appendIfMissing(wa.getAddress(), "/") + "actuator/registeredServices")).
-                            header(HttpHeaders.AUTHORIZATION,
-                                    DefaultBasicAuthSupplier.getBasicAuthHeader(anonymousUser, anonymousKey)).
+                            header(HttpHeaders.AUTHORIZATION, DefaultBasicAuthSupplier.getBasicAuthHeader(
+                                    securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey())).
                             GET().build(),
                     HttpResponse.BodyHandlers.discarding());
         } catch (KeymasterException e) {
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 e43b7c6..db4d326 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
@@ -26,7 +26,6 @@
 import java.net.http.HttpResponse;
 import java.util.List;
 import java.util.stream.Collectors;
-import javax.annotation.Resource;
 import javax.ws.rs.InternalServerErrorException;
 import javax.ws.rs.core.HttpHeaders;
 import org.apache.commons.lang3.ArrayUtils;
@@ -42,6 +41,7 @@
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.SRARoute;
 import org.apache.syncope.core.provisioning.api.data.SRARouteDataBinder;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
@@ -61,11 +61,8 @@
     @Autowired
     private ServiceOps serviceOps;
 
-    @Resource(name = "anonymousUser")
-    private String anonymousUser;
-
-    @Resource(name = "anonymousKey")
-    private String anonymousKey;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @PreAuthorize("isAuthenticated()")
     public List<SRARouteTO> list() {
@@ -120,8 +117,8 @@
             HttpClient.newBuilder().build().send(
                     HttpRequest.newBuilder(URI.create(
                             StringUtils.appendIfMissing(sra.getAddress(), "/") + "actuator/gateway/refresh")).
-                            header(HttpHeaders.AUTHORIZATION,
-                                    DefaultBasicAuthSupplier.getBasicAuthHeader(anonymousUser, anonymousKey)).
+                            header(HttpHeaders.AUTHORIZATION, DefaultBasicAuthSupplier.getBasicAuthHeader(
+                                    securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey())).
                             POST(HttpRequest.BodyPublishers.noBody()).build(),
                     HttpResponse.BodyHandlers.discarding());
         } catch (KeymasterException e) {
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 4f5d0d3..b02b3dd 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
@@ -27,7 +27,6 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
-import javax.annotation.Resource;
 import javax.ws.rs.InternalServerErrorException;
 import javax.ws.rs.core.HttpHeaders;
 import org.apache.commons.lang3.StringUtils;
@@ -44,6 +43,7 @@
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.auth.WAConfigDAO;
 import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
@@ -61,11 +61,8 @@
     @Autowired
     private WAConfigDAO waConfigDAO;
 
-    @Resource(name = "anonymousUser")
-    private String anonymousUser;
-
-    @Resource(name = "anonymousKey")
-    private String anonymousKey;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @PreAuthorize("hasRole('" + AMEntitlement.WA_CONFIG_LIST + "') or hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
     @Transactional(readOnly = true)
@@ -98,8 +95,8 @@
             HttpClient.newBuilder().build().send(
                     HttpRequest.newBuilder(URI.create(
                             StringUtils.appendIfMissing(wa.getAddress(), "/") + "actuator/refresh")).
-                            header(HttpHeaders.AUTHORIZATION,
-                                    DefaultBasicAuthSupplier.getBasicAuthHeader(anonymousUser, anonymousKey)).
+                            header(HttpHeaders.AUTHORIZATION, DefaultBasicAuthSupplier.getBasicAuthHeader(
+                                    securityProperties.getAnonymousUser(), securityProperties.getAnonymousKey())).
                             POST(HttpRequest.BodyPublishers.noBody()).build(),
                     HttpResponse.BodyHandlers.discarding());
         } catch (KeymasterException e) {
diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java
index 09757b0..22e90bf 100644
--- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java
@@ -22,11 +22,16 @@
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
 import org.apache.syncope.common.keymaster.client.api.model.Domain;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.core.persistence.api.DomainRegistry;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 @Component
 public class DummyDomainOps implements DomainOps {
 
+    @Autowired
+    private DomainRegistry domainRegistry;
+
     @Override
     public List<Domain> list() {
         return List.of();
@@ -39,7 +44,7 @@
 
     @Override
     public void create(final Domain domain) {
-        // nothing to do
+        domainRegistry.register(domain);
     }
 
     @Override
diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java
index 2b4d0f9..3d51425 100644
--- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.core.logic;
 
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.jpa.PersistenceContext;
 import org.apache.syncope.core.provisioning.java.ProvisioningContext;
@@ -30,27 +28,17 @@
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.Primary;
-import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+import org.springframework.context.annotation.PropertySource;
 
+@PropertySource("classpath:core-test.properties")
 @Import({ SecurityContext.class, PersistenceContext.class, ProvisioningContext.class, WorkflowContext.class })
 @ComponentScan("org.apache.syncope.core.logic")
 @Configuration
 public class IdMLogicTestContext {
 
-    @Bean
-    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
-        PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
-        pspc.setIgnoreResourceNotFound(true);
-        pspc.setIgnoreUnresolvablePlaceholders(true);
-        return pspc;
-    }
-
     @Primary
     @Bean
-    public ImplementationLookup classPathScanImplementationLookup()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
-            NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
-
+    public ImplementationLookup implementationLookup() {
         return new DummyImplementationLookup();
     }
 }
diff --git a/core/idm/logic/src/test/resources/logicTest.xml b/core/idm/logic/src/test/resources/logicTest.xml
deleted file mode 100644
index af40284..0000000
--- a/core/idm/logic/src/test/resources/logicTest.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-                           http://www.springframework.org/schema/beans/spring-beans.xsd">
-    
-  <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
-    <property name="locations">
-      <list>
-        <value>classpath:persistence.properties</value>
-        <value>classpath:domains/*.properties</value>
-        <value>classpath:security.properties</value>
-        <value>classpath:connid.properties</value>
-        <value>classpath:mail.properties</value>
-        <value>classpath:workflow.properties</value>
-        <value>classpath:provisioning.properties</value>
-        <value>classpath:logic.properties</value>
-      </list>
-    </property>
-    <property name="ignoreResourceNotFound" value="true"/>
-    <property name="ignoreUnresolvablePlaceholders" value="true"/>
-  </bean>
-
-  <bean id="jwtIssuer" class="java.lang.String">
-    <constructor-arg value="${jwtIssuer}"/>
-  </bean>
-  <bean id="jwsKey" class="java.lang.String">
-    <constructor-arg value="ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f"/>
-  </bean>
-  <bean id="accessTokenJwsSignatureVerifier"
-        class="org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier">
-    <property name="jwsAlgorithm" value="${jwsAlgorithm}"/>
-    <property name="jwsKey" value="ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f"/>
-  </bean>
-  <bean id="accessTokenJwsSignatureProvider"
-        class="org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider">
-    <property name="jwsAlgorithm" value="${jwsAlgorithm}"/>
-    <property name="jwsKey" value="ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f"/>
-  </bean>
-  <bean id="credentialChecker" class="org.apache.syncope.core.spring.security.DefaultCredentialChecker">
-    <constructor-arg value="ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f" index="0"/>
-    <constructor-arg value="DE088591C00CC98B36F5ADAAF7DA2B004CF7F2FE7BBB45B766B6409876E2F3DB13C7905C6AA59464" index="1"/>
-    <constructor-arg value="anonymousKey" index="2"/>
-  </bean>
-  
-  <import resource="logicContext.xml"/>
-</beans>
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
index 6d8c91c..69b786b 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
@@ -23,7 +23,6 @@
 import java.util.Date;
 import java.util.List;
 import java.util.stream.Collectors;
-import javax.annotation.Resource;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AccessTokenTO;
@@ -38,6 +37,7 @@
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.Encryptor;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
@@ -47,8 +47,8 @@
 
     private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
 
-    @Resource(name = "anonymousUser")
-    private String anonymousUser;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @Autowired
     private AccessTokenDataBinder binder;
@@ -71,9 +71,9 @@
 
     @PreAuthorize("isAuthenticated()")
     public Pair<String, Date> login() {
-        if (anonymousUser.equals(AuthContextUtils.getUsername())) {
+        if (securityProperties.getAnonymousUser().equals(AuthContextUtils.getUsername())) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRequest);
-            sce.getElements().add(anonymousUser + " cannot be granted an access token");
+            sce.getElements().add(securityProperties.getAnonymousUser() + " cannot be granted an access token");
             throw sce;
         }
 
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index 7fd3aea..357bea8 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -25,7 +25,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
-import javax.annotation.Resource;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
@@ -67,6 +66,7 @@
 import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate;
 import org.apache.syncope.core.provisioning.java.job.TaskJob;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.quartz.JobDataMap;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@@ -87,8 +87,8 @@
     @Autowired
     protected GroupDAO groupDAO;
 
-    @Resource(name = "adminUser")
-    protected String adminUser;
+    @Autowired
+    protected SecurityProperties securityProperties;
 
     @Autowired
     protected AnySearchDAO searchDAO;
@@ -130,7 +130,7 @@
     @PreAuthorize("isAuthenticated() and not(hasRole('" + IdRepoEntitlement.ANONYMOUS + "'))")
     @Transactional(readOnly = true)
     public List<GroupTO> own() {
-        if (adminUser.equals(AuthContextUtils.getUsername())) {
+        if (securityProperties.getAdminUser().equals(AuthContextUtils.getUsername())) {
             return List.of();
         }
 
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LogicContext.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LogicContext.java
index 2a6ed43..af4a218 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LogicContext.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LogicContext.java
@@ -20,53 +20,36 @@
 
 import java.lang.reflect.InvocationTargetException;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
-import org.springframework.context.EnvironmentAware;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.env.Environment;
 
-@PropertySource("classpath:logic.properties")
-@PropertySource(value = "file:${conf.directory}/logic.properties", ignoreResourceNotFound = true)
 @ComponentScan("org.apache.syncope.core.logic")
 @EnableAspectJAutoProxy
+@EnableConfigurationProperties(LogicProperties.class)
 @Configuration
-public class LogicContext implements EnvironmentAware {
+public class LogicContext {
 
-    private Environment env;
+    @Autowired
+    private LogicProperties props;
 
-    @Override
-    public void setEnvironment(final Environment env) {
-        this.env = env;
+    @ConditionalOnMissingBean
+    @Bean
+    public LogicInvocationHandler logicInvocationHandler() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+
+        return props.getInvocationHandler().getDeclaredConstructor().newInstance();
     }
 
+    @ConditionalOnMissingBean
     @Bean
-    public String version() {
-        return env.getProperty("version");
-    }
+    public ImplementationLookup implementationLookup() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-    @Bean
-    public String buildNumber() {
-        return env.getProperty("buildNumber");
-    }
-
-    @Bean
-    public LogicInvocationHandler logicInvocationHandler()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, 
-            NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
-
-        return (LogicInvocationHandler) Class.forName(env.getProperty("logicInvocationHandler")).
-                getDeclaredConstructor().newInstance();
-    }
-
-    @Bean
-    public ImplementationLookup classPathScanImplementationLookup()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, 
-            NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
-
-        return (ImplementationLookup) Class.forName(env.getProperty("classPathScanImplementationLookup")).
-                getDeclaredConstructor().newInstance();
+        return props.getImplementationLookup().getDeclaredConstructor().newInstance();
     }
 }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LogicProperties.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LogicProperties.java
new file mode 100644
index 0000000..6c84fcf
--- /dev/null
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LogicProperties.java
@@ -0,0 +1,57 @@
+/*
+ * 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 org.apache.syncope.core.logic.init.ClassPathScanImplementationLookup;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("logic")
+public class LogicProperties {
+
+    private Class<? extends LogicInvocationHandler> invocationHandler = LogicInvocationHandler.class;
+
+    private Class<? extends ImplementationLookup> implementationLookup = ClassPathScanImplementationLookup.class;
+
+    private boolean enableJDBCAuditAppender = true;
+
+    public Class<? extends LogicInvocationHandler> getInvocationHandler() {
+        return invocationHandler;
+    }
+
+    public void setInvocationHandler(final Class<? extends LogicInvocationHandler> invocationHandler) {
+        this.invocationHandler = invocationHandler;
+    }
+
+    public Class<? extends ImplementationLookup> getImplementationLookup() {
+        return implementationLookup;
+    }
+
+    public void setImplementationLookup(final Class<? extends ImplementationLookup> implementationLookup) {
+        this.implementationLookup = implementationLookup;
+    }
+
+    public boolean isEnableJDBCAuditAppender() {
+        return enableJDBCAuditAppender;
+    }
+
+    public void setEnableJDBCAuditAppender(final boolean enableJDBCAuditAppender) {
+        this.enableJDBCAuditAppender = enableJDBCAuditAppender;
+    }
+}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/AuditLoader.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/AuditLoader.java
index 280415a..eaadf81 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/AuditLoader.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/AuditLoader.java
@@ -29,6 +29,7 @@
 import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.core.logic.LogicProperties;
 import org.apache.syncope.core.logic.audit.AuditAppender;
 import org.apache.syncope.core.logic.audit.JdbcAuditAppender;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
@@ -37,7 +38,6 @@
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.stereotype.Component;
 
@@ -50,8 +50,8 @@
     @Autowired
     private ImplementationLookup implementationLookup;
 
-    @Value("${enable.jdbcAuditAppender:true}")
-    private boolean enableJdbcAuditAppender;
+    @Autowired
+    private LogicProperties props;
 
     @Override
     public int getOrder() {
@@ -62,7 +62,7 @@
     public void load(final String domain, final DataSource datasource) {
         LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
 
-        if (enableJdbcAuditAppender) {
+        if (props.isEnableJDBCAuditAppender()) {
             JdbcAuditAppender jdbcAuditAppender = (JdbcAuditAppender) ApplicationContextProvider.getBeanFactory().
                     createBean(JdbcAuditAppender.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
             jdbcAuditAppender.init(domain);
diff --git a/core/idrepo/logic/src/main/resources/logic.properties b/core/idrepo/logic/src/main/resources/logic.properties
deleted file mode 100644
index 3da2e2c..0000000
--- a/core/idrepo/logic/src/main/resources/logic.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-version=${syncope.version}
-buildNumber=${buildNumber}
-logicInvocationHandler=org.apache.syncope.core.logic.LogicInvocationHandler
-classPathScanImplementationLookup=org.apache.syncope.core.logic.init.ClassPathScanImplementationLookup
-enable.jdbcAuditAppender=true
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RESTCXFContext.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RESTCXFContext.java
index 12603b3..d836bcf 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RESTCXFContext.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RESTCXFContext.java
@@ -27,7 +27,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
-import javax.annotation.Resource;
 import javax.servlet.ServletRequestListener;
 import org.apache.cxf.Bus;
 import org.apache.cxf.endpoint.Server;
@@ -65,9 +64,6 @@
     @Autowired
     private ApplicationContext ctx;
 
-    @Resource(name = "version")
-    private String version;
-
     @Bean
     public Executor batchExecutor() {
         ThreadPoolTaskExecutor batchExecutor = new ThreadPoolTaskExecutor();
@@ -153,12 +149,16 @@
         return new AddETagFilter();
     }
 
+    private String version() {
+        return ctx.getEnvironment().getProperty("version");
+    }
+
     @Bean
     public OpenApiFeature openapiFeature() {
         OpenApiFeature openapiFeature = new OpenApiFeature();
         openapiFeature.setTitle("Apache Syncope");
-        openapiFeature.setVersion(version);
-        openapiFeature.setDescription("Apache Syncope " + version);
+        openapiFeature.setVersion(version());
+        openapiFeature.setDescription("Apache Syncope " + version());
         openapiFeature.setContactName("The Apache Syncope community");
         openapiFeature.setContactEmail("dev@syncope.apache.org");
         openapiFeature.setContactUrl("http://syncope.apache.org");
diff --git a/core/persistence-jpa-json/pom.xml b/core/persistence-jpa-json/pom.xml
index f56f7cc..68f7378 100644
--- a/core/persistence-jpa-json/pom.xml
+++ b/core/persistence-jpa-json/pom.xml
@@ -216,7 +216,7 @@
     </profile>
 
     <profile>
-      <id>postgres</id>
+      <id>pgjsonb</id>
       
       <dependencies>
         <dependency>
@@ -266,7 +266,8 @@
               </includes>
               <excludedGroups>multitenancy,plainAttrTable</excludedGroups>
               <systemProperties>
-                <DB_CONTAINER_IP>${docker.container.postgres.ip}</DB_CONTAINER_IP>                
+                <CORE_PROPERTIES>classpath:core-pgjsonb.properties,classpath:core-pgjsonb-test.properties</CORE_PROPERTIES>
+                <DB_CONTAINER_IP>${docker.container.postgres.ip}</DB_CONTAINER_IP>
               </systemProperties>
             </configuration>
           </plugin>
@@ -318,22 +319,15 @@
         
         <testResources>
           <testResource>
-            <directory>src/test/resources/pgjsonb</directory>
+            <directory>src/test/resources</directory>
             <filtering>true</filtering>
           </testResource>
-          <testResource>
-            <directory>src/main/resources/pgjsonb</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>domains/Master.properties</exclude>
-            </excludes>
-          </testResource>
         </testResources>
       </build>
     </profile>
     
     <profile>
-      <id>mysql</id>
+      <id>myjson</id>
       
       <dependencies>
         <dependency>
@@ -346,7 +340,7 @@
 
       <build>
         <defaultGoal>clean verify</defaultGoal>
-        
+
         <plugins>
           <plugin>
             <groupId>org.codehaus.mojo</groupId>
@@ -383,7 +377,8 @@
               </includes>
               <excludedGroups>multitenancy,plainAttrTable</excludedGroups>
               <systemProperties>
-                <DB_CONTAINER_IP>${docker.container.mysql.ip}</DB_CONTAINER_IP>                
+                <CORE_PROPERTIES>classpath:core-myjson.properties,classpath:core-myjson-test.properties</CORE_PROPERTIES>
+                <DB_CONTAINER_IP>${docker.container.mysql.ip}</DB_CONTAINER_IP>
               </systemProperties>
             </configuration>
           </plugin>
@@ -437,16 +432,9 @@
         
         <testResources>
           <testResource>
-            <directory>src/test/resources/myjson</directory>
+            <directory>src/test/resources</directory>
             <filtering>true</filtering>
           </testResource>
-          <testResource>
-            <directory>src/main/resources/myjson</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>domains/Master.properties</exclude>
-            </excludes>
-          </testResource>
         </testResources>
       </build>
     </profile>
diff --git a/core/persistence-jpa-json/src/main/resources/core-myjson.properties b/core/persistence-jpa-json/src/main/resources/core-myjson.properties
new file mode 100644
index 0000000..3899035
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/resources/core-myjson.properties
@@ -0,0 +1,47 @@
+# 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.
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mysql_innodb.sql
+
+persistence.entityFactory=org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory
+persistence.plainSchemaDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONPlainSchemaDAO
+persistence.plainAttrDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
+persistence.plainAttrValueDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO
+persistence.searchCondVisitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
+persistence.userDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
+persistence.groupDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
+persistence.anyObjectDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
+persistence.auditConfDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditConfDAO
+
+persistence.indexesXML=classpath:myjson/indexes.xml
+persistence.viewsXML=classpath:myjson/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=com.mysql.cj.jdbc.Driver
+persistence.domain[0].jdbcURL=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].orm=META-INF/spring-orm-myjson.xml
+persistence.domain[0].auditSql=audit_myjson.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mysql_innodb.sql
diff --git a/core/persistence-jpa-json/src/main/resources/core-pgjsonb.properties b/core/persistence-jpa-json/src/main/resources/core-pgjsonb.properties
new file mode 100644
index 0000000..fe9154c
--- /dev/null
+++ b/core/persistence-jpa-json/src/main/resources/core-pgjsonb.properties
@@ -0,0 +1,44 @@
+# 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.
+
+persistence.entityFactory=org.apache.syncope.core.persistence.jpa.entity.PGJPAJSONEntityFactory
+persistence.plainSchemaDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONPlainSchemaDAO
+persistence.plainAttrDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
+persistence.plainAttrValueDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO
+persistence.searchCondVisitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
+persistence.userDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
+persistence.groupDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
+persistence.anyObjectDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
+persistence.auditConfDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditConfDAO
+
+persistence.indexesXML=classpath:pgjsonb/indexes.xml
+persistence.viewsXML=classpath:pgjsonb/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.postgresql.Driver
+persistence.domain[0].jdbcURL=jdbc:postgresql://localhost:5432/syncope?stringtype=unspecified
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
+persistence.domain[0].orm=META-INF/spring-orm-pgjsonb.xml
+persistence.domain[0].auditSql=audit_pgjsonb.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
+provisioning.quartz.sql=tables_postgres.sql
diff --git a/core/persistence-jpa-json/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/main/resources/domains/MasterContent.xml
deleted file mode 100644
index 1a52f8f..0000000
--- a/core/persistence-jpa-json/src/main/resources/domains/MasterContent.xml
+++ /dev/null
@@ -1,190 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<dataset>
-  <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/"/>  
-
-  <SyncopeSchema id="password.cipher.algorithm"/>
-  <PlainSchema id="password.cipher.algorithm" type="String"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-
-  <!-- notificationjob.cronExpression:
-  + not existing: NotificationJob runs according to NotificationJob.DEFAULT_CRON_EXP
-  + provided as empty string: NotificationJob disabled
-  + provided as non-empty string: NotificationJob runs according to the given value -->
-  <SyncopeSchema id="notificationjob.cronExpression"/>
-  <PlainSchema id="notificationjob.cronExpression" type="String"
-               mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  
-  <SyncopeSchema id="notification.maxRetries"/>
-  <PlainSchema id="notification.maxRetries" type="Long"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-
-  <SyncopeSchema id="token.length"/>
-  <PlainSchema id="token.length" type="Long"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-
-  <SyncopeSchema id="token.expireTime"/>
-  <PlainSchema id="token.expireTime" type="Long"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-
-  <SyncopeSchema id="selfRegistration.allowed"/>
-  <PlainSchema id="selfRegistration.allowed" type="Boolean"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-
-  <SyncopeSchema id="passwordReset.allowed"/>
-  <PlainSchema id="passwordReset.allowed" type="Boolean"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-
-  <SyncopeSchema id="passwordReset.securityQuestion"/>
-  <PlainSchema id="passwordReset.securityQuestion" type="Boolean"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-
-  <SyncopeSchema id="authentication.attributes"/>
-  <PlainSchema id="authentication.attributes" type="String" multivalue="1" uniqueConstraint="0" readonly="0"/>
-
-  <SyncopeSchema id="authentication.statuses"/>
-  <PlainSchema id="authentication.statuses" type="String" multivalue="1" uniqueConstraint="0" readonly="0"/>
-
-  <!-- Save user login date upon successful authentication -->
-  <SyncopeSchema id="log.lastlogindate"/>
-  <PlainSchema id="log.lastlogindate" type="Boolean"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-
-  <!-- Return hashed password values when reading users -->
-  <SyncopeSchema id="return.password.value"/>
-  <PlainSchema id="return.password.value" type="Boolean"
-               mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  
-  <!--  JWT lifetime in minutes -->                   
-  <SyncopeSchema id="jwt.lifetime.minutes"/>
-  <PlainSchema id="jwt.lifetime.minutes" type="Long"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-    
-  <SyncopeConf id="cd64d66f-6fff-4008-b966-a06b1cc1436d"
-               plainAttrs='[{"values":[{"stringValue":"SSHA256"}],"schema":"password.cipher.algorithm"},{"values":[{"stringValue":""}],"schema":"notificationjob.cronExpression"},{"values":[{"longValue":3}],"schema":"notification.maxRetries"},{"values":[{"longValue":256}],"schema":"token.length"},{"values":[{"longValue":60}],"schema":"token.expireTime"},{"values":[{"booleanValue":true}],"schema":"selfRegistration.allowed"},{"values":[{"booleanValue":true}],"schema":"passwordReset.allowed"},{"values":[{"booleanValue":true}],"schema":"passwordReset.securityQuestion"},{"values":[{"stringValue":"username"},{"stringValue":"email"}],"schema":"authentication.attributes"},{"values":[{"stringValue":"created"},{"stringValue":"active"}],"schema":"authentication.statuses"},{"values":[{"booleanValue":true}],"schema":"log.lastlogindate"},{"values":[{"booleanValue":false}],"schema":"return.password.value"},{"values":[{"longValue":120}],"schema":"jwt.lifetime.minutes"}]'/>
-
-  <AnyType id="USER" kind="USER"/>
-  <AnyTypeClass id="BaseUser"/>
-  <AnyType_AnyTypeClass anyType_id="USER" anyTypeClass_id="BaseUser"/>
-
-  <AnyType id="GROUP" kind="GROUP"/>
-  <AnyTypeClass id="BaseGroup"/>
-  <AnyType_AnyTypeClass anyType_id="GROUP" anyTypeClass_id="BaseGroup"/>
-        
-  <!-- Actual plain schemas -->
-  <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
-                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
-  <SyncopeSchema id="email"/>
-  <PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
-               mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
-               validator_id="EmailAddressValidator"/>
-  
-  <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
-                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.BinaryValidator"/>
-
-  <Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
-  <Implementation id="PushJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate"/>
-
-
-  <Implementation id="ExpiredAccessTokenCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/>
-  <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
-  <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/>
-  <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
-
-  <!-- Password reset notifications -->
-  <MailTemplate id="requestPasswordReset"
-                textTemplate="Hi,
-a password reset was requested for ${user.getUsername()}.
-
-In order to complete this request, you need to visit this link:
-
-http://localhost:9080/syncope-enduser/confirmpasswordreset?token=${input.get(0).replaceAll(' ', '%20')}
-
-If you did not request this reset, just ignore the present e-mail.
-
-Best regards."
-                htmlTemplate="&lt;html&gt;
-&lt;body&gt;
-&lt;p&gt;Hi,
-a password reset was requested for ${user.getUsername()}.&lt;/p&gt;
-
-&lt;p&gt;In order to complete this request, you need to visit this 
-&lt;a href=&quot;http://localhost:9080/syncope-enduser/confirmpasswordreset?token=${input.get(0).replaceAll(' ', '%20')}&quot;&gt;link&lt;/a&gt;&lt;/p&gt;.
-
-&lt;p&gt;If you did not request this reset, just ignore the present e-mail.&lt;/p&gt;
-
-&lt;p&gt;Best regards.&lt;/p&gt;
-&lt;/body&gt;
-&lt;/html&gt;"/>
-  <MailTemplate id="confirmPasswordReset"
-                textTemplate="Hi,
-we are happy to inform you that the password request was successfully executed for your account.
-
-Best regards."
-                htmlTemplate="&lt;html&gt;
-&lt;body&gt;
-&lt;p&gt;Hi,&lt;/br&gt;
-we are happy to inform you that the password request was successfully executed for your account.&lt;/p&gt;
-
-&lt;p&gt;Best regards.&lt;/p&gt;
-&lt;/body&gt;
-&lt;/html&gt;"/>
-
-  <Notification id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" active="1" recipientAttrName="email" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset request" template_id="requestPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <AnyAbout id="a328f2e6-25e9-4cc1-badf-7425d7be4b39" anyType_id="USER" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" filter="token!=$null"/>
-  <Notification_events notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
-  
-  <Notification id="bef0c250-e8a7-4848-bb63-2564fc409ce2" active="1" recipientAttrName="email" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset successful" template_id="confirmPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <Notification_events notification_id="bef0c250-e8a7-4848-bb63-2564fc409ce2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
-
-  <ReportTemplate id="empty"/>  
-
-  <Report id="c3520ad9-179f-49e7-b315-d684d216dd97" name="reconciliation" active="1" template_id="empty"/>
-  <Implementation id="ReconciliationReportletConf" type="REPORTLET" engine="JAVA"
-                  body='{"_class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
-  <ReportReportlet report_id="c3520ad9-179f-49e7-b315-d684d216dd97" implementation_id="ReconciliationReportletConf"/>
-
-  <SyncopeRole id="GROUP_OWNER"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_CREATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_DELETE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="RELATIONSHIPTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="REALM_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_DELETE"/>
-</dataset>
diff --git a/docker/core/src/main/resources/domains/MasterContent.xml.myjson b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
similarity index 98%
rename from docker/core/src/main/resources/domains/MasterContent.xml.myjson
rename to core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
index 0e35f39..fde1371 100644
--- a/docker/core/src/main/resources/domains/MasterContent.xml.myjson
+++ b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
@@ -108,7 +108,7 @@
 
   <Report id="c3520ad9-179f-49e7-b315-d684d216dd97" name="reconciliation" active="1" template_id="empty"/>
   <Implementation id="ReconciliationReportletConf" type="REPORTLET" engine="JAVA"
-                  body='{"@class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
+                  body='{"_class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
   <ReportReportlet report_id="c3520ad9-179f-49e7-b315-d684d216dd97" implementation_id="ReconciliationReportletConf"/>
 
   <SyncopeRole id="GROUP_OWNER"/>
diff --git a/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties b/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties
deleted file mode 100644
index 6dfdf26..0000000
--- a/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=com.mysql.cj.jdbc.Driver
-Master.url=jdbc:mysql://localhost:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
-Master.orm=META-INF/spring-orm-myjson.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_myjson.sql
diff --git a/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml
index fa67fdb..eae2a73 100644
--- a/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml
+++ b/core/persistence-jpa-json/src/main/resources/myjson/indexes.xml
@@ -44,6 +44,6 @@
 
   <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
   <entry key="TaskExec_TaskIdIndex">CREATE INDEX TaskExec_TaskIdIndex ON TaskExec(task_id)</entry>
-  <entry key="AnyTemplatePullTask_PullTaskIndex">CREATE INDEX AnyTemplatePullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
-  <entry key="NotificationTask_recipientsIndex">CREATE INDEX NotificationTask_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
+  <entry key="ATPullTask_PullTaskIndex">CREATE INDEX ATPullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
+  <entry key="NT_recipientsIndex">CREATE INDEX NT_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
 </properties>
diff --git a/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties
deleted file mode 100644
index fe430c2..0000000
--- a/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO
-any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditConfDAO
-openjpa.RemoteCommitProvider=sjvm
diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/domains/Master.properties b/core/persistence-jpa-json/src/main/resources/pgjsonb/domains/Master.properties
deleted file mode 100644
index 4ab67b0..0000000
--- a/core/persistence-jpa-json/src/main/resources/pgjsonb/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.postgresql.Driver
-Master.url=jdbc:postgresql://localhost:5432/syncope?stringtype=unspecified
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
-Master.orm=META-INF/spring-orm-pgjsonb.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_pgjsonb.sql
diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml b/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml
index 63cb909..de3758a 100644
--- a/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml
+++ b/core/persistence-jpa-json/src/main/resources/pgjsonb/indexes.xml
@@ -48,6 +48,6 @@
 
   <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
   <entry key="TaskExec_TaskIdIndex">CREATE INDEX TaskExec_TaskIdIndex ON TaskExec(task_id)</entry>
-  <entry key="AnyTemplatePullTaskIndex">CREATE INDEX AnyTemplatePullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
-  <entry key="NotificationTask_recipientsIndex">CREATE INDEX NotificationTask_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
+  <entry key="ATPullTask_PullTaskIndex">CREATE INDEX ATPullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
+  <entry key="NT_recipientsIndex">CREATE INDEX NT_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
 </properties>
diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties b/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties
deleted file mode 100644
index f8eb86e..0000000
--- a/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.PGJPAJSONEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO
-any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditConfDAO
-openjpa.RemoteCommitProvider=sjvm
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/core/persistence-jpa-json/src/test/resources/core-myjson-test.properties
similarity index 70%
copy from ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to core/persistence-jpa-json/src/test/resources/core-myjson-test.properties
index 450ff50..694fe06 100644
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/core/persistence-jpa-json/src/test/resources/core-myjson-test.properties
@@ -14,5 +14,12 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-userDetails=User details
-userForm=Edit User
+
+security.adminUser=${adminUser}
+security.anonymousUser=${anonymousUser}
+security.jwsKey=${jwsKey}
+security.secretKey=${secretKey}
+
+persistence.domain[0].jdbcURL=jdbc:mysql://${DB_CONTAINER_IP}:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
+
+provisioning.connIdLocation=${connid.location}
diff --git a/client/idrepo/console/src/test/resources/application-debug.properties b/core/persistence-jpa-json/src/test/resources/core-pgjsonb-test.properties
similarity index 73%
copy from client/idrepo/console/src/test/resources/application-debug.properties
copy to core/persistence-jpa-json/src/test/resources/core-pgjsonb-test.properties
index 1ac160d..26e61ce 100644
--- a/client/idrepo/console/src/test/resources/application-debug.properties
+++ b/core/persistence-jpa-json/src/test/resources/core-pgjsonb-test.properties
@@ -14,9 +14,12 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
 
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
+security.adminUser=${adminUser}
+security.anonymousUser=${anonymousUser}
+security.jwsKey=${jwsKey}
+security.secretKey=${secretKey}
+
+persistence.domain[0].jdbcURL=jdbc:postgresql://${DB_CONTAINER_IP}:5432/syncope?stringtype=unspecified
+
+provisioning.connIdLocation=${connid.location}
diff --git a/core/persistence-jpa-json/src/test/resources/myjson/domains/Master.properties b/core/persistence-jpa-json/src/test/resources/myjson/domains/Master.properties
deleted file mode 100644
index 5b36d41..0000000
--- a/core/persistence-jpa-json/src/test/resources/myjson/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=com.mysql.cj.jdbc.Driver
-Master.url=jdbc:mysql://${DB_CONTAINER_IP}:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
-Master.orm=META-INF/spring-orm-myjson.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_myjson.sql
diff --git a/core/persistence-jpa-json/src/test/resources/persistenceTest.xml b/core/persistence-jpa-json/src/test/resources/persistenceTest.xml
deleted file mode 100644
index 1e84aea..0000000
--- a/core/persistence-jpa-json/src/test/resources/persistenceTest.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-                           http://www.springframework.org/schema/beans/spring-beans.xsd
-                           http://www.springframework.org/schema/context
-                           http://www.springframework.org/schema/context/spring-context.xsd">
-
-  <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
-    <property name="locations">
-      <list>
-        <value>file:${conf.directory}/persistence.properties</value>
-        <value>file:${conf.directory}/domains/*.properties</value>
-        <value>classpath:security.properties</value>
-      </list>
-    </property>
-    <property name="ignoreResourceNotFound" value="true"/>
-    <property name="ignoreUnresolvablePlaceholders" value="true"/>
-  </bean>
-  
-  <bean class="org.apache.syncope.core.spring.ApplicationContextProvider"/>
-
-  <bean id="adminUser" class="java.lang.String">
-    <constructor-arg value="${adminUser}"/>
-  </bean>
-  <bean id="anonymousUser" class="java.lang.String">
-    <constructor-arg value="${anonymousUser}"/>
-  </bean>
-  
-  <context:component-scan base-package="org.apache.syncope.core.spring.security"/>
-
-  <bean class="org.apache.syncope.core.spring.security.DefaultPasswordGenerator"/>
-
-  <import resource="persistenceContext.xml"/>
-
-</beans>
diff --git a/core/persistence-jpa-json/src/test/resources/pgjsonb/domains/Master.properties b/core/persistence-jpa-json/src/test/resources/pgjsonb/domains/Master.properties
deleted file mode 100644
index 4f42b56..0000000
--- a/core/persistence-jpa-json/src/test/resources/pgjsonb/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.postgresql.Driver
-Master.url=jdbc:postgresql://${DB_CONTAINER_IP}:5432/syncope?stringtype=unspecified
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
-Master.orm=META-INF/spring-orm-pgjsonb.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_pgjsonb.sql
diff --git a/core/persistence-jpa/pom.xml b/core/persistence-jpa/pom.xml
index 221d592..192a4d8 100644
--- a/core/persistence-jpa/pom.xml
+++ b/core/persistence-jpa/pom.xml
@@ -173,7 +173,17 @@
           </execution>
         </executions>
       </plugin>
-      
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <systemProperties>
+            <CORE_PROPERTIES>classpath:core-test.properties</CORE_PROPERTIES>
+          </systemProperties>
+        </configuration>
+      </plugin>
+
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java
new file mode 100644
index 0000000..68bb940
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java
@@ -0,0 +1,189 @@
+/*
+ * 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;
+
+import org.apache.syncope.common.keymaster.client.api.model.Domain;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+
+public class DomainProperties {
+
+    private String key;
+
+    private String jdbcDriver;
+
+    private String jdbcURL;
+
+    private String dbSchema;
+
+    private String dbUsername;
+
+    private String dbPassword;
+
+    private Domain.TransactionIsolation transactionIsolation = Domain.TransactionIsolation.TRANSACTION_READ_COMMITTED;
+
+    private int poolMaxActive = 10;
+
+    private int poolMinIdle = 2;
+
+    private String auditSql = "audit.sql";
+
+    private String orm = "META-INF/spring-orm.xml";
+
+    private String databasePlatform;
+
+    private String adminPassword;
+
+    private CipherAlgorithm adminCipherAlgorithm = CipherAlgorithm.SHA512;
+
+    private String content;
+
+    private String keymasterConfParams;
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    public String getJdbcDriver() {
+        return jdbcDriver;
+    }
+
+    public void setJdbcDriver(final String jdbcDriver) {
+        this.jdbcDriver = jdbcDriver;
+    }
+
+    public String getJdbcURL() {
+        return jdbcURL;
+    }
+
+    public void setJdbcURL(final String jdbcURL) {
+        this.jdbcURL = jdbcURL;
+    }
+
+    public String getDbSchema() {
+        return dbSchema;
+    }
+
+    public void setDbSchema(final String dbSchema) {
+        this.dbSchema = dbSchema;
+    }
+
+    public String getDbUsername() {
+        return dbUsername;
+    }
+
+    public void setDbUsername(final String dbUsername) {
+        this.dbUsername = dbUsername;
+    }
+
+    public String getDbPassword() {
+        return dbPassword;
+    }
+
+    public void setDbPassword(final String dbPassword) {
+        this.dbPassword = dbPassword;
+    }
+
+    public Domain.TransactionIsolation getTransactionIsolation() {
+        return transactionIsolation;
+    }
+
+    public void setTransactionIsolation(final Domain.TransactionIsolation transactionIsolation) {
+        this.transactionIsolation = transactionIsolation;
+    }
+
+    public int getPoolMaxActive() {
+        return poolMaxActive;
+    }
+
+    public void setPoolMaxActive(final int poolMaxActive) {
+        this.poolMaxActive = poolMaxActive;
+    }
+
+    public int getPoolMinIdle() {
+        return poolMinIdle;
+    }
+
+    public void setPoolMinIdle(final int poolMinIdle) {
+        this.poolMinIdle = poolMinIdle;
+    }
+
+    public String getAuditSql() {
+        return auditSql;
+    }
+
+    public void setAuditSql(final String auditSql) {
+        this.auditSql = auditSql;
+    }
+
+    public String getOrm() {
+        return orm;
+    }
+
+    public void setOrm(final String orm) {
+        this.orm = orm;
+    }
+
+    public String getDatabasePlatform() {
+        return databasePlatform;
+    }
+
+    public void setDatabasePlatform(final String databasePlatform) {
+        this.databasePlatform = databasePlatform;
+    }
+
+    public String getAdminPassword() {
+        return adminPassword;
+    }
+
+    public void setAdminPassword(final String adminPassword) {
+        this.adminPassword = adminPassword;
+    }
+
+    public CipherAlgorithm getAdminCipherAlgorithm() {
+        return adminCipherAlgorithm;
+    }
+
+    public void setAdminCipherAlgorithm(final CipherAlgorithm adminCipherAlgorithm) {
+        this.adminCipherAlgorithm = adminCipherAlgorithm;
+    }
+
+    public String getContent() {
+        return content == null
+                ? "classpath:domains/" + key + "Content.xml"
+                : content;
+    }
+
+    public void setContent(final String content) {
+        this.content = content;
+    }
+
+    public String getKeymasterConfParams() {
+        return keymasterConfParams == null
+                ? "classpath:domains/" + key + "KeymasterConfParams.json"
+                : keymasterConfParams;
+    }
+
+    public void setKeymasterConfParams(final String keymasterConfParams) {
+        this.keymasterConfParams = keymasterConfParams;
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MasterDomain.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MasterDomain.java
index 954386e..215bb9f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MasterDomain.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MasterDomain.java
@@ -26,19 +26,17 @@
 import java.util.Objects;
 
 import javax.sql.DataSource;
+import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.persistence.jpa.spring.CommonEntityManagerFactoryConf;
 import org.apache.syncope.core.persistence.jpa.spring.DomainEntityManagerFactoryBean;
-import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.DependsOn;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.env.Environment;
-import org.springframework.core.io.Resource;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.ResourceLoader;
 import org.springframework.jdbc.datasource.init.DataSourceInitializer;
 import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
 import org.springframework.jndi.JndiObjectFactoryBean;
@@ -46,8 +44,7 @@
 import org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter;
 import org.springframework.transaction.PlatformTransactionManager;
 
-@PropertySource("classpath:domains/Master.properties")
-@PropertySource(value = "file:${conf.directory}/domains/Master.properties", ignoreResourceNotFound = true)
+@EnableConfigurationProperties(PersistenceProperties.class)
 @Configuration
 public class MasterDomain {
 
@@ -55,58 +52,22 @@
     private CommonEntityManagerFactoryConf commonEMFConf;
 
     @Autowired
-    private ConfigurableApplicationContext ctx;
+    private ResourceLoader resourceLoader;
 
     @Autowired
-    private Environment env;
+    private PersistenceProperties props;
 
-    @Value("${Master.driverClassName}")
-    private String driverClassName;
-
-    @Value("${Master.url}")
-    private String url;
-
-    @Value("${Master.schema}")
-    private String schema;
-
-    @Value("${Master.username}")
-    private String username;
-
-    @Value("${Master.password}")
-    private String password;
-
-    @Value("${Master.pool.transactionIsolation:TRANSACTION_READ_COMMITTED}")
-    private String transactionIsolation;
-
-    @Value("${Master.pool.maxActive:10}")
-    private int maximumPoolSize;
-
-    @Value("${Master.pool.minIdle:2}")
-    private int minimumIdle;
-
-    @Value("classpath:/audit/${Master.audit.sql}")
-    private Resource auditSql;
-
-    @Value("${Master.orm}")
-    private String orm;
-
-    @Value("${Master.databasePlatform}")
-    private String databasePlatform;
-
-    @Value("${content.directory}")
-    private String contentDirectory;
-
-    @Bean(name = "MasterDataSource")
     @ConditionalOnMissingBean(name = "MasterDataSource")
+    @Bean(name = "MasterDataSource")
     public JndiObjectFactoryBean masterDataSource() {
         HikariConfig hikariConfig = new HikariConfig();
-        hikariConfig.setDriverClassName(driverClassName);
-        hikariConfig.setJdbcUrl(url);
-        hikariConfig.setUsername(username);
-        hikariConfig.setPassword(password);
-        hikariConfig.setTransactionIsolation(transactionIsolation);
-        hikariConfig.setMaximumPoolSize(maximumPoolSize);
-        hikariConfig.setMinimumIdle(minimumIdle);
+        hikariConfig.setDriverClassName(props.getDomain().get(0).getJdbcDriver());
+        hikariConfig.setJdbcUrl(props.getDomain().get(0).getJdbcURL());
+        hikariConfig.setUsername(props.getDomain().get(0).getDbUsername());
+        hikariConfig.setPassword(props.getDomain().get(0).getDbPassword());
+        hikariConfig.setTransactionIsolation(props.getDomain().get(0).getTransactionIsolation().name());
+        hikariConfig.setMaximumPoolSize(props.getDomain().get(0).getPoolMaxActive());
+        hikariConfig.setMinimumIdle(props.getDomain().get(0).getPoolMinIdle());
 
         JndiObjectFactoryBean masterDataSource = new JndiObjectFactoryBean();
         masterDataSource.setJndiName("java:comp/env/jdbc/syncopeMasterDataSource");
@@ -114,19 +75,19 @@
         return masterDataSource;
     }
 
-    @Bean(name = "MasterResourceDatabasePopulator")
     @ConditionalOnMissingBean(name = "MasterResourceDatabasePopulator")
+    @Bean(name = "MasterResourceDatabasePopulator")
     public ResourceDatabasePopulator masterResourceDatabasePopulator() {
         ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
         databasePopulator.setContinueOnError(true);
         databasePopulator.setIgnoreFailedDrops(true);
         databasePopulator.setSqlScriptEncoding("UTF-8");
-        databasePopulator.addScript(auditSql);
+        databasePopulator.addScript(new ClassPathResource("/audit/" + props.getDomain().get(0).getAuditSql()));
         return databasePopulator;
     }
 
-    @Bean(name = "MasterDataSourceInitializer")
     @ConditionalOnMissingBean(name = "MasterDataSourceInitializer")
+    @Bean(name = "MasterDataSourceInitializer")
     public DataSourceInitializer masterDataSourceInitializer() {
         DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
         dataSourceInitializer.setDataSource((DataSource) Objects.requireNonNull(masterDataSource().getObject()));
@@ -135,69 +96,49 @@
         return dataSourceInitializer;
     }
 
-    @Bean(name = "MasterEntityManagerFactory")
-    @DependsOn("commonEMFConf")
     @ConditionalOnMissingBean(name = "MasterEntityManagerFactory")
+    @DependsOn("commonEMFConf")
+    @Bean(name = "MasterEntityManagerFactory")
     public DomainEntityManagerFactoryBean masterEntityManagerFactory() {
         OpenJpaVendorAdapter vendorAdapter = new OpenJpaVendorAdapter();
         vendorAdapter.setShowSql(false);
         vendorAdapter.setGenerateDdl(true);
-        vendorAdapter.setDatabasePlatform(databasePlatform);
-        DomainEntityManagerFactoryBean masterEntityManagerFactory = new DomainEntityManagerFactoryBean();
-        masterEntityManagerFactory.setMappingResources(orm);
-        masterEntityManagerFactory.setPersistenceUnitName("Master");
+        vendorAdapter.setDatabasePlatform(props.getDomain().get(0).getDatabasePlatform());
 
+        DomainEntityManagerFactoryBean masterEntityManagerFactory = new DomainEntityManagerFactoryBean();
+        masterEntityManagerFactory.setMappingResources(props.getDomain().get(0).getOrm());
+        masterEntityManagerFactory.setPersistenceUnitName(SyncopeConstants.MASTER_DOMAIN);
         masterEntityManagerFactory.setDataSource(Objects.requireNonNull((DataSource) masterDataSource().getObject()));
         masterEntityManagerFactory.setJpaVendorAdapter(vendorAdapter);
         masterEntityManagerFactory.setCommonEntityManagerFactoryConf(commonEMFConf);
 
-        if (env.containsProperty("openjpaMetaDataFactory")) {
+        if (props.getMetaDataFactory() != null) {
             masterEntityManagerFactory.setJpaPropertyMap(Map.of(
                     "openjpa.MetaDataFactory",
-                    Objects.requireNonNull(env.getProperty("openjpaMetaDataFactory")).replace("##orm##", orm)));
+                    props.getMetaDataFactory().replace("##orm##", props.getDomain().get(0).getOrm())));
         }
 
         return masterEntityManagerFactory;
     }
 
-    @Bean(name = { "MasterTransactionManager", "Master" })
     @ConditionalOnMissingBean(name = "MasterTransactionManager")
+    @Bean(name = { "MasterTransactionManager", "Master" })
     public PlatformTransactionManager transactionManager() {
         return new JpaTransactionManager(Objects.requireNonNull(masterEntityManagerFactory().getObject()));
     }
 
-    @Bean(name = "MasterProperties")
-    @ConditionalOnMissingBean(name = "MasterProperties")
-    public ResourceWithFallbackLoader masterProperties() {
-        ResourceWithFallbackLoader masterProperties = new ResourceWithFallbackLoader();
-        masterProperties.setPrimary("file:" + contentDirectory + "/domains/Master.properties");
-        masterProperties.setFallback("classpath:domains/Master.properties");
-        return masterProperties;
-    }
-
     @Bean(name = "MasterContentXML")
-    @ConditionalOnMissingBean(name = "MasterContentXML")
     public InputStream masterContentXML() throws IOException {
-        ResourceWithFallbackLoader masterContentXML =
-                ctx.getBeanFactory().createBean(ResourceWithFallbackLoader.class);
-        masterContentXML.setPrimary("file:" + contentDirectory + "/domains/MasterContent.xml");
-        masterContentXML.setFallback("classpath:domains/MasterContent.xml");
-        return masterContentXML.getResource().getInputStream();
+        return resourceLoader.getResource(props.getDomain().get(0).getContent()).getInputStream();
     }
 
     @Bean(name = "MasterKeymasterConfParamsJSON")
-    @ConditionalOnMissingBean(name = "MasterKeymasterConfParamsJSON")
     public InputStream masterKeymasterConfParamsJSON() throws IOException {
-        ResourceWithFallbackLoader keymasterConfParamsJSON =
-                ctx.getBeanFactory().createBean(ResourceWithFallbackLoader.class);
-        keymasterConfParamsJSON.setPrimary("file:" + contentDirectory + "/domains/MasterKeymasterConfParams.json");
-        keymasterConfParamsJSON.setFallback("classpath:domains/MasterKeymasterConfParams.json");
-        return keymasterConfParamsJSON.getResource().getInputStream();
+        return resourceLoader.getResource(props.getDomain().get(0).getKeymasterConfParams()).getInputStream();
     }
 
     @Bean(name = "MasterDatabaseSchema")
-    @ConditionalOnMissingBean(name = "MasterDatabaseSchema")
     public String masterDatabaseSchema() {
-        return schema;
+        return props.getDomain().get(0).getDbSchema();
     }
 }
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 d5825f6..11849ef 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
@@ -25,6 +25,7 @@
 import javax.validation.Validator;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.AuditConfDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
@@ -35,40 +36,37 @@
 import org.apache.syncope.core.persistence.jpa.spring.CommonEntityManagerFactoryConf;
 import org.apache.syncope.core.persistence.jpa.spring.DomainTransactionInterceptorInjector;
 import org.apache.syncope.core.persistence.jpa.spring.MultiJarAwarePersistenceUnitPostProcessor;
-import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.EnvironmentAware;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.env.Environment;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
 import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
-import org.apache.syncope.core.persistence.api.dao.AuditConfDAO;
 
-@PropertySource("classpath:persistence.properties")
-@PropertySource(value = "file:${conf.directory}/persistence.properties", ignoreResourceNotFound = true)
 @ComponentScan("org.apache.syncope.core.persistence.jpa")
+@EnableConfigurationProperties(PersistenceProperties.class)
 @Configuration
-public class PersistenceContext implements EnvironmentAware {
+public class PersistenceContext {
 
     private static final Logger OPENJPA_LOG = LoggerFactory.getLogger("org.apache.openjpa");
 
-    private Environment env;
-
-    @Override
-    public void setEnvironment(final Environment env) {
-        this.env = env;
-    }
-
     @Bean
     public static BeanFactoryPostProcessor domainTransactionInterceptorInjector() {
         return new DomainTransactionInterceptorInjector();
     }
 
+    @Autowired
+    private PersistenceProperties props;
+
+    @Autowired
+    private ResourceLoader resourceLoader;
+
     @ConditionalOnMissingBean
     @Bean
     public CommonEntityManagerFactoryConf commonEMFConf() {
@@ -96,99 +94,80 @@
         jpaPropertyMap.put("openjpa.DataCache", "true");
         jpaPropertyMap.put("openjpa.QueryCache", "true");
 
-        jpaPropertyMap.put("openjpa.RemoteCommitProvider", env.getProperty("openjpa.RemoteCommitProvider", "sjvm"));
+        jpaPropertyMap.put("openjpa.RemoteCommitProvider", props.getRemoteCommitProvider());
 
         commonEMFConf.setJpaPropertyMap(jpaPropertyMap);
         return commonEMFConf;
     }
 
     @Bean
-    public EntityFactory entityFactory()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public EntityFactory entityFactory() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (EntityFactory) Class.forName(env.getProperty("entity.factory")).getConstructor().newInstance();
+        return props.getEntityFactory().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "plainSchemaDAO")
     @Bean
-    public PlainSchemaDAO plainSchemaDAO()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public PlainSchemaDAO plainSchemaDAO() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (PlainSchemaDAO) Class.forName(env.getProperty("plainSchema.dao")).getConstructor().newInstance();
+        return props.getPlainSchemaDAO().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "plainAttrDAO")
     @Bean
-    public PlainAttrDAO plainAttrDAO()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public PlainAttrDAO plainAttrDAO() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (PlainAttrDAO) Class.forName(env.getProperty("plainAttr.dao")).getConstructor().newInstance();
+        return props.getPlainAttrDAO().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "plainAttrValueDAO")
     @Bean
-    public PlainAttrValueDAO plainAttrValueDAO()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public PlainAttrValueDAO plainAttrValueDAO() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (PlainAttrValueDAO) Class.forName(env.getProperty("plainAttrValue.dao")).getConstructor().newInstance();
+        return props.getPlainAttrValueDAO().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "anySearchDAO")
     @Bean
-    public AnySearchDAO anySearchDAO()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public AnySearchDAO anySearchDAO() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (AnySearchDAO) Class.forName(env.getProperty("any.search.dao")).getConstructor().newInstance();
+        return props.getAnySearchDAO().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "anySearchVisitor")
     @Bean
-    public SearchCondVisitor anySearchVisitor()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public SearchCondVisitor searchCondVisitor() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (SearchCondVisitor) Class.forName(env.getProperty("any.search.visitor")).getConstructor().newInstance();
+        return props.getSearchCondVisitor().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "userDAO")
     @Bean
-    public UserDAO userDAO()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public UserDAO userDAO() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (UserDAO) Class.forName(env.getProperty("user.dao")).getConstructor().newInstance();
+        return props.getUserDAO().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "groupDAO")
     @Bean
-    public GroupDAO groupDAO()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public GroupDAO groupDAO() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (GroupDAO) Class.forName(env.getProperty("group.dao")).getConstructor().newInstance();
+        return props.getGroupDAO().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "anyObjectDAO")
     @Bean
-    public AnyObjectDAO anyObjectDAO()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public AnyObjectDAO anyObjectDAO() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (AnyObjectDAO) Class.forName(env.getProperty("anyObject.dao")).getConstructor().newInstance();
+        return props.getAnyObjectDAO().getDeclaredConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "auditDAO")
     @Bean
-    public AuditConfDAO auditDAO()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public AuditConfDAO auditConfDAO() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (AuditConfDAO) Class.forName(env.getProperty("audit.dao")).getConstructor().newInstance();
+        return props.getAuditConfDAO().getDeclaredConstructor().newInstance();
     }
 
     @Bean
@@ -196,21 +175,13 @@
         return new LocalValidatorFactoryBean();
     }
 
-    @ConditionalOnMissingBean(name = "viewsXML")
     @Bean
-    public ResourceWithFallbackLoader viewsXML() {
-        ResourceWithFallbackLoader viewsXML = new ResourceWithFallbackLoader();
-        viewsXML.setPrimary("file:" + env.getProperty("content.directory") + "/views.xml");
-        viewsXML.setFallback("classpath:views.xml");
-        return viewsXML;
+    public Resource viewsXML() {
+        return resourceLoader.getResource(props.getViewsXML());
     }
 
-    @ConditionalOnMissingBean(name = "indexesXML")
     @Bean
-    public ResourceWithFallbackLoader indexesXML() {
-        ResourceWithFallbackLoader indexesXML = new ResourceWithFallbackLoader();
-        indexesXML.setPrimary("file:" + env.getProperty("content.directory") + "/indexes.xml");
-        indexesXML.setFallback("classpath:indexes.xml");
-        return indexesXML;
+    public Resource indexesXML() {
+        return resourceLoader.getResource(props.getIndexesXML());
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java
new file mode 100644
index 0000000..4af0eea
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java
@@ -0,0 +1,194 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.AuditConfDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+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;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.search.SearchCondVisitor;
+import org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAAuditConfDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO;
+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;
+import org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO;
+import org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.NestedConfigurationProperty;
+
+@ConfigurationProperties("persistence")
+public class PersistenceProperties {
+
+    private String remoteCommitProvider = "sjvm";
+
+    private String metaDataFactory;
+
+    private Class<? extends EntityFactory> entityFactory = JPAEntityFactory.class;
+
+    private Class<? extends PlainSchemaDAO> plainSchemaDAO = JPAPlainSchemaDAO.class;
+
+    private Class<? extends PlainAttrDAO> plainAttrDAO = JPAPlainAttrDAO.class;
+
+    private Class<? extends PlainAttrValueDAO> plainAttrValueDAO = JPAPlainAttrValueDAO.class;
+
+    private Class<? extends AnySearchDAO> anySearchDAO = JPAAnySearchDAO.class;
+
+    private Class<? extends SearchCondVisitor> searchCondVisitor = SearchCondVisitor.class;
+
+    private Class<? extends UserDAO> userDAO = JPAUserDAO.class;
+
+    private Class<? extends GroupDAO> groupDAO = JPAGroupDAO.class;
+
+    private Class<? extends AnyObjectDAO> anyObjectDAO = JPAAnyObjectDAO.class;
+
+    private Class<? extends AuditConfDAO> auditConfDAO = JPAAuditConfDAO.class;
+
+    private String viewsXML = "classpath:views.xml";
+
+    private String indexesXML = "classpath:indexes.xml";
+
+    @NestedConfigurationProperty
+    private final List<DomainProperties> domain = new ArrayList<>();
+
+    public String getRemoteCommitProvider() {
+        return remoteCommitProvider;
+    }
+
+    public void setRemoteCommitProvider(final String remoteCommitProvider) {
+        this.remoteCommitProvider = remoteCommitProvider;
+    }
+
+    public String getMetaDataFactory() {
+        return metaDataFactory;
+    }
+
+    public void setMetaDataFactory(final String metaDataFactory) {
+        this.metaDataFactory = metaDataFactory;
+    }
+
+    public Class<? extends EntityFactory> getEntityFactory() {
+        return entityFactory;
+    }
+
+    public void setEntityFactory(final Class<? extends EntityFactory> entityFactory) {
+        this.entityFactory = entityFactory;
+    }
+
+    public Class<? extends PlainSchemaDAO> getPlainSchemaDAO() {
+        return plainSchemaDAO;
+    }
+
+    public void setPlainSchemaDAO(final Class<? extends PlainSchemaDAO> plainSchemaDAO) {
+        this.plainSchemaDAO = plainSchemaDAO;
+    }
+
+    public Class<? extends PlainAttrDAO> getPlainAttrDAO() {
+        return plainAttrDAO;
+    }
+
+    public void setPlainAttrDAO(final Class<? extends PlainAttrDAO> plainAttrDAO) {
+        this.plainAttrDAO = plainAttrDAO;
+    }
+
+    public Class<? extends PlainAttrValueDAO> getPlainAttrValueDAO() {
+        return plainAttrValueDAO;
+    }
+
+    public void setPlainAttrValueDAO(final Class<? extends PlainAttrValueDAO> plainAttrValueDAO) {
+        this.plainAttrValueDAO = plainAttrValueDAO;
+    }
+
+    public Class<? extends AnySearchDAO> getAnySearchDAO() {
+        return anySearchDAO;
+    }
+
+    public void setAnySearchDAO(final Class<? extends AnySearchDAO> anySearchDAO) {
+        this.anySearchDAO = anySearchDAO;
+    }
+
+    public Class<? extends SearchCondVisitor> getSearchCondVisitor() {
+        return searchCondVisitor;
+    }
+
+    public void setSearchCondVisitor(final Class<? extends SearchCondVisitor> searchCondVisitor) {
+        this.searchCondVisitor = searchCondVisitor;
+    }
+
+    public Class<? extends UserDAO> getUserDAO() {
+        return userDAO;
+    }
+
+    public void setUserDAO(final Class<? extends UserDAO> userDAO) {
+        this.userDAO = userDAO;
+    }
+
+    public Class<? extends GroupDAO> getGroupDAO() {
+        return groupDAO;
+    }
+
+    public void setGroupDAO(final Class<? extends GroupDAO> groupDAO) {
+        this.groupDAO = groupDAO;
+    }
+
+    public Class<? extends AnyObjectDAO> getAnyObjectDAO() {
+        return anyObjectDAO;
+    }
+
+    public void setAnyObjectDAO(final Class<? extends AnyObjectDAO> anyObjectDAO) {
+        this.anyObjectDAO = anyObjectDAO;
+    }
+
+    public Class<? extends AuditConfDAO> getAuditConfDAO() {
+        return auditConfDAO;
+    }
+
+    public void setAuditConfDAO(final Class<? extends AuditConfDAO> auditConfDAO) {
+        this.auditConfDAO = auditConfDAO;
+    }
+
+    public String getViewsXML() {
+        return viewsXML;
+    }
+
+    public void setViewsXML(final String viewsXML) {
+        this.viewsXML = viewsXML;
+    }
+
+    public String getIndexesXML() {
+        return indexesXML;
+    }
+
+    public void setIndexesXML(final String indexesXML) {
+        this.indexesXML = indexesXML;
+    }
+
+    public List<DomainProperties> getDomain() {
+        return domain;
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java
index 36ba211..9bfc909 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java
@@ -53,7 +53,7 @@
 
             ApplicationContextProvider.getApplicationContext().getBeansOfType(SyncopeCoreLoader.class).values().
                     stream().sorted(Comparator.comparing(SyncopeCoreLoader::getOrder)).
-                    forEachOrdered(loader -> {
+                    forEach(loader -> {
                         String loaderName = AopUtils.getTargetClass(loader).getName();
 
                         LOG.debug("[{}] Starting on domain '{}'", loaderName, domain);
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java
index e1fed71..b3a6d3e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java
@@ -18,31 +18,21 @@
  */
 package org.apache.syncope.core.persistence.jpa;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.IOException;
-import java.util.Enumeration;
 import java.util.Map;
-import java.util.Properties;
 import java.util.function.Function;
 import java.util.stream.Collectors;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
 import org.apache.syncope.common.keymaster.client.api.model.Domain;
-import org.apache.syncope.common.lib.PropertyUtils;
-import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.DomainHolder;
 import org.apache.syncope.core.persistence.api.DomainRegistry;
 import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
-import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.core.Ordered;
-import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -50,22 +40,20 @@
 
     private static final Logger LOG = LoggerFactory.getLogger(StartupDomainLoader.class);
 
-    private static final ObjectMapper MAPPER = new ObjectMapper();
-
     @Autowired
     private DomainOps domainOps;
 
     @Autowired
-    private ConfigurableApplicationContext ctx;
-
-    @Autowired
     private DomainHolder domainHolder;
 
     @Autowired
-    private DomainRegistry domainRegistry;
+    private PersistenceProperties persistenceProperties;
 
-    @Value("${content.directory}")
-    private String contentDirectory;
+    @Autowired
+    private ResourceLoader resourceLoader;
+
+    @Autowired
+    private DomainRegistry domainRegistry;
 
     @Override
     public int getOrder() {
@@ -74,82 +62,51 @@
 
     @Override
     public void load() {
-        try {
-            Map<String, Domain> keymasterDomains = domainOps.list().stream().
-                    collect(Collectors.toMap(Domain::getKey, Function.identity()));
+        Map<String, Domain> keymasterDomains = domainOps.list().stream().
+                collect(Collectors.toMap(Domain::getKey, Function.identity()));
 
-            for (Resource domainProp : ctx.getResources("classpath:/domains/*.properties")) {
-                String domainPropFile = StringUtils.substringAfterLast(domainProp.getURL().toExternalForm(), "/");
-                String key = StringUtils.substringBefore(domainPropFile, ".");
+        persistenceProperties.getDomain().stream().
+                filter(d -> !domainHolder.getDomains().containsKey(d.getKey())).forEach(domainProps -> {
 
-                if (!domainHolder.getDomains().containsKey(key)) {
-                    if (keymasterDomains.containsKey(key)) {
-                        LOG.info("Domain {} initialization", key);
+            if (keymasterDomains.containsKey(domainProps.getKey())) {
+                LOG.info("Domain {} initialization", domainProps.getKey());
 
-                        domainRegistry.register(keymasterDomains.get(key));
+                domainRegistry.register(keymasterDomains.get(domainProps.getKey()));
 
-                        LOG.info("Domain {} successfully inited", key);
-                    } else {
-                        Domain.Builder builder = new Domain.Builder(key);
+                LOG.info("Domain {} successfully inited", domainProps.getKey());
+            } else {
+                Domain.Builder builder = new Domain.Builder(domainProps.getKey());
 
-                        Properties props = PropertyUtils.read(getClass(),
-                                "domains/" + domainPropFile, contentDirectory);
-                        for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
-                            String prop = (String) e.nextElement();
+                builder.adminPassword(domainProps.getAdminPassword());
+                builder.adminCipherAlgorithm(domainProps.getAdminCipherAlgorithm());
 
-                            if (prop.endsWith(".driverClassName")) {
-                                builder.jdbcDriver(props.getProperty(prop));
-                            } else if (prop.endsWith(".url")) {
-                                builder.jdbcURL(props.getProperty(prop));
-                            } else if (prop.endsWith(".schema")) {
-                                builder.dbSchema(props.getProperty(prop));
-                            } else if (prop.endsWith(".username")) {
-                                builder.dbUsername(props.getProperty(prop));
-                            } else if (prop.endsWith(".password")) {
-                                builder.dbPassword(props.getProperty(prop));
-                            } else if (prop.endsWith(".databasePlatform")) {
-                                builder.databasePlatform(props.getProperty(prop));
-                            } else if (prop.endsWith(".orm")) {
-                                builder.orm(props.getProperty(prop));
-                            } else if (prop.endsWith(".pool.maxActive")) {
-                                builder.poolMaxActive(Integer.parseInt(props.getProperty(prop)));
-                            } else if (prop.endsWith(".pool.minIdle")) {
-                                builder.poolMinIdle(Integer.parseInt(props.getProperty(prop)));
-                            } else if (prop.endsWith(".audit.sql")) {
-                                builder.auditSql(props.getProperty(prop));
-                            }
-                        }
+                builder.jdbcDriver(domainProps.getJdbcDriver());
+                builder.jdbcURL(domainProps.getJdbcURL());
+                builder.dbSchema(domainProps.getDbSchema());
+                builder.dbUsername(domainProps.getDbUsername());
+                builder.dbPassword(domainProps.getDbPassword());
+                builder.databasePlatform(domainProps.getDatabasePlatform());
+                builder.orm(domainProps.getOrm());
+                builder.poolMaxActive(domainProps.getPoolMaxActive());
+                builder.poolMinIdle(domainProps.getPoolMinIdle());
+                builder.auditSql(domainProps.getAuditSql());
 
-                        ResourceWithFallbackLoader content =
-                                ctx.getBeanFactory().createBean(ResourceWithFallbackLoader.class);
-                        content.setPrimary("file:" + contentDirectory + "/domains/" + key + "Content.xml");
-                        content.setFallback("classpath:domains/" + key + "Content.xml");
-                        builder.content(IOUtils.toString(content.getResource().getInputStream()));
-
-                        ResourceWithFallbackLoader keymasterConfParams =
-                                ctx.getBeanFactory().createBean(ResourceWithFallbackLoader.class);
-                        keymasterConfParams.setPrimary(
-                                "file:" + contentDirectory + "/domains/" + key + "KeymasterConfParams.json");
-                        keymasterConfParams.setFallback("classpath:domains/" + key + "KeymasterConfParams.json");
-                        builder.keymasterConfParams(
-                                IOUtils.toString(keymasterConfParams.getResource().getInputStream()));
-
-                        ResourceWithFallbackLoader security =
-                                ctx.getBeanFactory().createBean(ResourceWithFallbackLoader.class);
-                        security.setPrimary("file:" + contentDirectory + "/domains/" + key + "Security.json");
-                        security.setFallback("classpath:domains/" + key + "Security.json");
-
-                        JsonNode securityInfo = MAPPER.readTree(security.getResource().getInputStream());
-                        builder.adminPassword(securityInfo.get("password").asText());
-                        builder.adminCipherAlgorithm(
-                                CipherAlgorithm.valueOf(securityInfo.get("cipherAlgorithm").asText()));
-
-                        domainOps.create(builder.build());
-                    }
+                try {
+                    builder.content(IOUtils.toString(
+                            resourceLoader.getResource(domainProps.getContent()).getInputStream()));
+                } catch (IOException e) {
+                    LOG.error("While loading {}", domainProps.getContent(), e);
                 }
+
+                try {
+                    builder.keymasterConfParams(IOUtils.toString(
+                            resourceLoader.getResource(domainProps.getKeymasterConfParams()).getInputStream()));
+                } catch (IOException e) {
+                    LOG.error("While loading {}", domainProps.getKeymasterConfParams(), e);
+                }
+
+                domainOps.create(builder.build());
             }
-        } catch (IOException | NumberFormatException e) {
-            LOG.error("Error during domain initialization", e);
-        }
+        });
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
index a432a51..d71be3e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
@@ -66,6 +66,7 @@
 import javax.xml.transform.stream.StreamResult;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.cxf.helpers.IOUtils;
 import org.apache.openjpa.lib.util.collections.BidiMap;
 import org.apache.openjpa.lib.util.collections.DualHashBidiMap;
 import org.apache.syncope.core.persistence.api.DomainHolder;
@@ -90,7 +91,6 @@
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.tika.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -216,14 +216,14 @@
                 case Types.LONGVARBINARY:
                     InputStream is = rs.getBinaryStream(columnName);
                     if (is != null) {
-                        res = DatatypeConverter.printHexBinary(IOUtils.toByteArray(is));
+                        res = DatatypeConverter.printHexBinary(IOUtils.toString(is).getBytes());
                     }
                     break;
 
                 case Types.BLOB:
                     Blob blob = rs.getBlob(columnName);
                     if (blob != null) {
-                        res = DatatypeConverter.printHexBinary(IOUtils.toByteArray(blob.getBinaryStream()));
+                        res = DatatypeConverter.printHexBinary(IOUtils.toString(blob.getBinaryStream()).getBytes());
                     }
                     break;
 
@@ -517,9 +517,13 @@
             throw new IllegalArgumentException("Could not find DataSource for domain " + domain);
         }
 
-        String schema = ApplicationContextProvider.getBeanFactory().containsBean(domain + "DatabaseSchema")
-                ? ApplicationContextProvider.getBeanFactory().getBean(domain + "DatabaseSchema", String.class)
-                : null;
+        String schema = null;
+        if (ApplicationContextProvider.getBeanFactory().containsBean(domain + "DatabaseSchema")) {
+            Object schemaBean = ApplicationContextProvider.getBeanFactory().getBean(domain + "DatabaseSchema");
+            if (schemaBean instanceof String) {
+                schema = (String) schemaBean;
+            }
+        }
 
         Connection conn = null;
         ResultSet rs = null;
@@ -527,10 +531,9 @@
             conn = DataSourceUtils.getConnection(dataSource);
             final DatabaseMetaData meta = conn.getMetaData();
 
-            rs = meta.getTables(null, StringUtils.isBlank(schema) ? null : schema, null,
-                    new String[] { "TABLE" });
+            rs = meta.getTables(null, StringUtils.isBlank(schema) ? null : schema, null, new String[] { "TABLE" });
 
-            final Set<String> tableNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+            Set<String> tableNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
 
             while (rs.next()) {
                 String tableName = rs.getString("TABLE_NAME");
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java
index 9c1b502..8347ef6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java
@@ -21,7 +21,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Properties;
-import javax.annotation.Resource;
 import javax.sql.DataSource;
 import javax.xml.XMLConstants;
 import javax.xml.parsers.ParserConfigurationException;
@@ -30,11 +29,11 @@
 import org.apache.syncope.core.persistence.api.content.ContentLoader;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
-import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
+import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PropertiesLoaderUtils;
 import org.springframework.dao.DataAccessException;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -50,11 +49,11 @@
 
     private static final Logger LOG = LoggerFactory.getLogger(XMLContentLoader.class);
 
-    @Resource(name = "viewsXML")
-    private ResourceWithFallbackLoader viewsXML;
+    @javax.annotation.Resource(name = "viewsXML")
+    private Resource viewsXML;
 
-    @Resource(name = "indexesXML")
-    private ResourceWithFallbackLoader indexesXML;
+    @javax.annotation.Resource(name = "indexesXML")
+    private Resource indexesXML;
 
     @Autowired
     private Environment env;
@@ -125,8 +124,8 @@
 
         JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
 
-        Properties views = PropertiesLoaderUtils.loadProperties(viewsXML.getResource());
-        views.stringPropertyNames().stream().sorted().forEachOrdered(idx -> {
+        Properties views = PropertiesLoaderUtils.loadProperties(viewsXML);
+        views.stringPropertyNames().stream().sorted().forEach(idx -> {
             LOG.debug("[{}] Creating view {}", domain, views.get(idx).toString());
             try {
                 jdbcTemplate.execute(views.getProperty(idx).replaceAll("\\n", " "));
@@ -143,8 +142,8 @@
 
         JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
 
-        Properties indexes = PropertiesLoaderUtils.loadProperties(indexesXML.getResource());
-        indexes.stringPropertyNames().stream().sorted().forEachOrdered(idx -> {
+        Properties indexes = PropertiesLoaderUtils.loadProperties(indexesXML);
+        indexes.stringPropertyNames().stream().sorted().forEach(idx -> {
             LOG.debug("[{}] Creating index {}", domain, indexes.get(idx).toString());
             try {
                 jdbcTemplate.execute(indexes.getProperty(idx));
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 65a9bf4..d96848e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -29,7 +29,6 @@
 import java.util.Set;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import javax.annotation.Resource;
 import javax.persistence.NoResultException;
 import javax.persistence.PersistenceException;
 import javax.persistence.Query;
@@ -74,6 +73,7 @@
 import org.apache.syncope.core.spring.policy.AccountPolicyException;
 import org.apache.syncope.core.spring.policy.PasswordPolicyException;
 import org.apache.syncope.core.spring.security.Encryptor;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -83,8 +83,6 @@
     protected static final Pattern USERNAME_PATTERN =
             Pattern.compile('^' + SyncopeConstants.NAME_PATTERN, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
 
-    protected static final Encryptor ENCRYPTOR = Encryptor.getInstance();
-
     @Autowired
     protected RoleDAO roleDAO;
 
@@ -100,11 +98,8 @@
     @Autowired
     protected DelegationDAO delegationDAO;
 
-    @Resource(name = "adminUser")
-    protected String adminUser;
-
-    @Resource(name = "anonymousUser")
-    protected String anonymousUser;
+    @Autowired
+    protected SecurityProperties securityProperties;
 
     @Override
     protected AnyUtils init() {
@@ -201,7 +196,7 @@
     protected void securityChecks(final User user) {
         // Allows anonymous (during self-registration) and self (during self-update) to read own user,
         // otherwise goes through security checks to see if required entitlements are owned
-        if (!AuthContextUtils.getUsername().equals(anonymousUser)
+        if (!AuthContextUtils.getUsername().equals(securityProperties.getAnonymousUser())
                 && !AuthContextUtils.getUsername().equals(user.getUsername())) {
 
             Set<String> authRealms = AuthContextUtils.getAuthorizations().
@@ -348,7 +343,8 @@
                     matching = pwdHistory.subList(policy.getHistoryLength() >= pwdHistory.size()
                             ? 0
                             : pwdHistory.size() - policy.getHistoryLength(), pwdHistory.size()).stream().
-                            map(old -> ENCRYPTOR.verify(user.getClearPassword(), user.getCipherAlgorithm(), old)).
+                            map(old -> Encryptor.getInstance().verify(
+                            user.getClearPassword(), user.getCipherAlgorithm(), old)).
                             reduce(matching, (accumulator, item) -> accumulator | item);
                 }
                 if (matching) {
@@ -394,7 +390,9 @@
                 throw new AccountPolicyException("Null username");
             }
 
-            if (adminUser.equals(user.getUsername()) || anonymousUser.equals(user.getUsername())) {
+            if (securityProperties.getAdminUser().equals(user.getUsername())
+                    || securityProperties.getAnonymousUser().equals(user.getUsername())) {
+
                 throw new AccountPolicyException("Not allowed: " + user.getUsername());
             }
 
diff --git a/core/persistence-jpa/src/main/resources/domains/Master.properties b/core/persistence-jpa/src/main/resources/domains/Master.properties
deleted file mode 100644
index 7b4280b..0000000
--- a/core/persistence-jpa/src/main/resources/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.postgresql.Driver
-Master.url=jdbc:postgresql://localhost:5432/syncope
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit.sql
diff --git a/core/persistence-jpa/src/main/resources/indexes.xml b/core/persistence-jpa/src/main/resources/indexes.xml
index c266f1e..62f3a8d 100644
--- a/core/persistence-jpa/src/main/resources/indexes.xml
+++ b/core/persistence-jpa/src/main/resources/indexes.xml
@@ -82,6 +82,6 @@
 
   <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
   <entry key="TaskExec_TaskIdIndex">CREATE INDEX TaskExec_TaskIdIndex ON TaskExec(task_id)</entry>
-  <entry key="AnyTemplatePullTask_PullTaskIndex">CREATE INDEX AnyTemplatePullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
-  <entry key="NotificationTask_recipientsIndex">CREATE INDEX NotificationTask_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
+  <entry key="ATPullTask_PullTaskIndex">CREATE INDEX ATPullTask_PullTaskIndex ON AnyTemplatePullTask(pullTask_id)</entry>
+  <entry key="NT_recipientsIndex">CREATE INDEX NT_recipientsIndex ON NotificationTask_recipients(notificationTask_id)</entry>
 </properties>
diff --git a/core/persistence-jpa/src/main/resources/persistence.properties b/core/persistence-jpa/src/main/resources/persistence.properties
deleted file mode 100644
index 0ed337d..0000000
--- a/core/persistence-jpa/src/main/resources/persistence.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO
-any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditConfDAO
-openjpa.RemoteCommitProvider=sjvm
diff --git a/fit/core-reference/src/main/resources/postgres/views.xml b/core/persistence-jpa/src/main/resources/sqlserver_views.xml
similarity index 100%
rename from fit/core-reference/src/main/resources/postgres/views.xml
rename to core/persistence-jpa/src/main/resources/sqlserver_views.xml
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
index a25f912..658e929 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
@@ -18,37 +18,45 @@
  */
 package org.apache.syncope.core.persistence.jpa;
 
-import java.io.IOException;
-import org.apache.syncope.core.spring.ApplicationContextProvider;
+import java.util.ArrayList;
+import java.util.List;
 import org.apache.syncope.core.spring.security.DefaultPasswordGenerator;
 import org.apache.syncope.core.spring.security.PasswordGenerator;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
-import org.springframework.context.annotation.PropertySource;
 import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
 
-@PropertySource("classpath:security.properties")
 @Import(PersistenceContext.class)
 @Configuration
 public class PersistenceTestContext {
 
-    @Value("${adminUser}")
+    @Bean
+    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
+        PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
+
+        DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
+
+        List<Resource> locations = new ArrayList<>();
+        for (String location : System.getProperty("CORE_PROPERTIES").split(",")) {
+            locations.add(resourceLoader.getResource(location));
+        }
+        ppc.setLocations(locations.toArray(new Resource[0]));
+
+        return ppc;
+    }
+
+    @Value("${security.adminUser}")
     private String adminUser;
 
-    @Value("${anonymousUser}")
+    @Value("${security.anonymousUser}")
     private String anonymousUser;
 
     @Bean
-    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
-        PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
-        pspc.setIgnoreResourceNotFound(true);
-        pspc.setIgnoreUnresolvablePlaceholders(true);
-        return pspc;
-    }
-
-    @Bean
     public String adminUser() {
         return adminUser;
     }
@@ -59,8 +67,8 @@
     }
 
     @Bean
-    public ApplicationContextProvider applicationContextProvider() {
-        return new ApplicationContextProvider();
+    public SecurityProperties securityProperties() {
+        return new SecurityProperties();
     }
 
     @Bean
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 9067f12..d1da5f7 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
@@ -34,8 +34,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.jpa.AbstractTest;
-import org.apache.tika.io.IOUtils;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.ClassPathResource;
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java
index 7d80175..2bb59ec 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java
@@ -26,10 +26,11 @@
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.stream.Collectors;
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.persistence.api.content.ContentExporter;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
-import org.apache.tika.io.IOUtils;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
@@ -49,13 +50,13 @@
     public void issueSYNCOPE1128() throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
-        exporter.export("Master", baos, null, null, null);
+        exporter.export(SyncopeConstants.MASTER_DOMAIN, baos, null, null, null);
 
         String exported = baos.toString(Charset.defaultCharset());
         assertTrue(StringUtils.isNotBlank(exported));
 
         List<String> realms = IOUtils.readLines(
-                IOUtils.toInputStream(exported), StandardCharsets.UTF_8.name()).stream().
+                IOUtils.toInputStream(exported, StandardCharsets.UTF_8), StandardCharsets.UTF_8.name()).stream().
                 filter(row -> row.trim().startsWith("<Realm")).collect(Collectors.toList());
         assertEquals(4, realms.size());
         assertTrue(realms.get(0).contains("name=\"/\""));
diff --git a/core/persistence-jpa/src/test/resources/core-test.properties b/core/persistence-jpa/src/test/resources/core-test.properties
new file mode 100644
index 0000000..17d1ed7
--- /dev/null
+++ b/core/persistence-jpa/src/test/resources/core-test.properties
@@ -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.
+
+security.adminUser=${adminUser}
+security.anonymousUser=${anonymousUser}
+security.jwsKey=${jwsKey}
+security.secretKey=${secretKey}
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.h2.Driver
+persistence.domain[0].jdbcURL=jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1
+persistence.domain[0].dbUsername=sa
+persistence.domain[0].dbPassword=
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.H2Dictionary
+persistence.domain[0].auditSql=audit.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+persistence.domain[1].key=Two
+persistence.domain[1].jdbcDriver=org.h2.Driver
+persistence.domain[1].jdbcURL=jdbc:h2:mem:syncopetwo;DB_CLOSE_DELAY=-1
+persistence.domain[1].dbUsername=sa
+persistence.domain[1].dbPassword=
+persistence.domain[1].databasePlatform=org.apache.openjpa.jdbc.sql.H2Dictionary
+persistence.domain[1].auditSql=audit.sql
+persistence.domain[1].poolMaxActive=10
+persistence.domain[1].poolMinIdle=2
+persistence.domain[1].adminPassword=2AA60A8FF7FCD473D321E0146AFD9E26DF395147
+persistence.domain[1].adminCipherAlgorithm=SHA
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_h2.sql
+
+provisioning.connIdLocation=${connid.location}
diff --git a/core/persistence-jpa/src/test/resources/domains/Master.properties b/core/persistence-jpa/src/test/resources/domains/Master.properties
deleted file mode 100644
index f3ce3be..0000000
--- a/core/persistence-jpa/src/test/resources/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.h2.Driver
-Master.url=jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1
-Master.schema=
-Master.username=sa
-Master.password=
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.H2Dictionary
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit.sql
diff --git a/core/persistence-jpa/src/test/resources/domains/Two.properties b/core/persistence-jpa/src/test/resources/domains/Two.properties
deleted file mode 100644
index e11614d..0000000
--- a/core/persistence-jpa/src/test/resources/domains/Two.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Two.driverClassName=org.h2.Driver
-Two.url=jdbc:h2:mem:syncopetwo;DB_CLOSE_DELAY=-1
-Two.schema=
-Two.username=sa
-Two.password=
-Two.databasePlatform=org.apache.openjpa.jdbc.sql.H2Dictionary
-Two.orm=META-INF/spring-orm.xml
-
-Two.pool.maxActive=10
-Two.pool.minIdle=2
-
-Two.audit.sql=audit.sql
diff --git a/core/pom.xml b/core/pom.xml
index d410761..b81dcbd 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -89,5 +89,6 @@
     <module>workflow-api</module>
     <module>workflow-java</module>
     <module>starter</module>
+    <module>self-keymaster-starter</module>
   </modules>
 </project>
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnIdBundleManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnIdBundleManager.java
index 1913e68..594568c 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnIdBundleManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnIdBundleManager.java
@@ -43,7 +43,4 @@
     void resetConnManagers();
 
     List<URI> getLocations();
-
-    void setStringLocations(String stringLocations);
-
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
index ca5a33f..f42c689 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
@@ -21,6 +21,8 @@
 import java.util.Optional;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.provisioning.api.Connector;
@@ -106,4 +108,13 @@
                 append(beforeObj, other.beforeObj).
                 build();
     }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this, ToStringStyle.SIMPLE_STYLE).
+                appendSuper(super.toString()).
+                append(externalResource).
+                append(beforeObj).
+                build();
+    }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java
index c9fa698..f9d00be 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java
@@ -53,45 +53,33 @@
 
     private static final Logger LOG = LoggerFactory.getLogger(ConnIdBundleManager.class);
 
-    private String stringLocations;
-
     /**
      * ConnId Locations.
      */
-    private List<URI> locations;
+    private final List<URI> locations;
 
     /**
      * ConnectorInfoManager instances.
      */
     private final Map<URI, ConnectorInfoManager> connInfoManagers = Collections.synchronizedMap(new LinkedHashMap<>());
 
+    public ConnIdBundleManagerImpl(final List<String> stringLocations) {
+        locations = new ArrayList<>();
+        stringLocations.forEach(location -> {
+            try {
+                locations.add(URIUtils.buildForConnId(location));
+                LOG.info("Valid ConnId location: {}", location.trim());
+            } catch (Exception e) {
+                LOG.error("Invalid ConnId location: {}", location.trim(), e);
+            }
+        });
+    }
+
     @Override
     public List<URI> getLocations() {
-        init();
-
         return locations;
     }
 
-    @Override
-    public void setStringLocations(final String stringLocations) {
-        this.stringLocations = stringLocations;
-    }
-
-    private void init() {
-        if (locations == null) {
-            locations = new ArrayList<>();
-            for (String location : StringUtils.isBlank(stringLocations) ? new String[0] : stringLocations.split(",")) {
-                try {
-                    locations.add(URIUtils.buildForConnId(location));
-                    LOG.info("Valid ConnId location: {}", location.trim());
-                } catch (Exception e) {
-                    LOG.error("Invalid ConnId location: {}", location.trim(), e);
-                }
-            }
-            locations = Collections.unmodifiableList(locations);
-        }
-    }
-
     private void initLocal(final URI location) {
         // 1. Find bundles inside local directory
         File bundleDirectory = new File(location);
@@ -191,8 +179,6 @@
 
     @Override
     public Map<URI, ConnectorInfoManager> getConnManagers() {
-        init();
-
         if (connInfoManagers.isEmpty()) {
             locations.forEach(location -> {
                 try {
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 44b70d1..17ad4c1 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
@@ -18,11 +18,11 @@
  */
 package org.apache.syncope.core.provisioning.java;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.lang.reflect.InvocationTargetException;
 import java.nio.charset.StandardCharsets;
-import java.util.Enumeration;
 import java.util.Properties;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -32,10 +32,8 @@
 import javax.mail.Transport;
 import javax.naming.NamingException;
 import javax.sql.DataSource;
-import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.LogOutputStream;
-import org.apache.syncope.common.lib.PropertyUtils;
 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
@@ -51,21 +49,18 @@
 import org.apache.syncope.core.provisioning.java.job.JobManagerImpl;
 import org.apache.syncope.core.provisioning.java.job.SchedulerDBInit;
 import org.apache.syncope.core.provisioning.java.job.SchedulerShutdown;
-import org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.ApplicationContext;
-import org.springframework.context.EnvironmentAware;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.DependsOn;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.context.annotation.Primary;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.env.Environment;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
 import org.springframework.jndi.JndiObjectFactoryBean;
@@ -77,16 +72,11 @@
 import org.springframework.scheduling.quartz.SchedulerFactoryBean;
 import org.springframework.transaction.PlatformTransactionManager;
 
-@PropertySource("classpath:connid.properties")
-@PropertySource("classpath:mail.properties")
-@PropertySource("classpath:provisioning.properties")
-@PropertySource(value = "file:${conf.directory}/connid.properties", ignoreResourceNotFound = true)
-@PropertySource(value = "file:${conf.directory}/mail.properties", ignoreResourceNotFound = true)
-@PropertySource(value = "file:${conf.directory}/provisioning.properties", ignoreResourceNotFound = true)
 @ComponentScan("org.apache.syncope.core.provisioning.java")
 @EnableAsync
+@EnableConfigurationProperties(ProvisioningProperties.class)
 @Configuration
-public class ProvisioningContext implements EnvironmentAware, AsyncConfigurer {
+public class ProvisioningContext implements AsyncConfigurer {
 
     private static final Logger LOG = LoggerFactory.getLogger(ProvisioningContext.class);
 
@@ -97,15 +87,11 @@
     private PlatformTransactionManager masterTransactionManager;
 
     @Autowired
+    private ProvisioningProperties props;
+
+    @Autowired
     private ApplicationContext ctx;
 
-    private Environment env;
-
-    @Override
-    public void setEnvironment(final Environment env) {
-        this.env = env;
-    }
-
     /**
      * Annotated as {@code @Primary} because it will be used by {@code @Async} in {@link AsyncConnectorFacade}.
      *
@@ -115,9 +101,9 @@
     @Primary
     public Executor asyncConnectorFacadeExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
-        executor.setCorePoolSize(env.getProperty("asyncConnectorFacadeExecutor.corePoolSize", Integer.class));
-        executor.setMaxPoolSize(env.getProperty("asyncConnectorFacadeExecutor.maxPoolSize", Integer.class));
-        executor.setQueueCapacity(env.getProperty("asyncConnectorFacadeExecutor.queueCapacity", Integer.class));
+        executor.setCorePoolSize(props.getAsyncConnectorFacadeExecutor().getCorePoolSize());
+        executor.setMaxPoolSize(props.getAsyncConnectorFacadeExecutor().getMaxPoolSize());
+        executor.setQueueCapacity(props.getAsyncConnectorFacadeExecutor().getQueueCapacity());
         executor.setThreadNamePrefix("AsyncConnectorFacadeExecutor-");
         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
         executor.initialize();
@@ -137,9 +123,9 @@
     @Bean
     public Executor propagationTaskExecutorAsyncExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
-        executor.setCorePoolSize(env.getProperty("propagationTaskExecutorAsyncExecutor.corePoolSize", Integer.class));
-        executor.setMaxPoolSize(env.getProperty("propagationTaskExecutorAsyncExecutor.maxPoolSize", Integer.class));
-        executor.setQueueCapacity(env.getProperty("propagationTaskExecutorAsyncExecutor.queueCapacity", Integer.class));
+        executor.setCorePoolSize(props.getPropagationTaskExecutorAsyncExecutor().getCorePoolSize());
+        executor.setMaxPoolSize(props.getPropagationTaskExecutorAsyncExecutor().getMaxPoolSize());
+        executor.setQueueCapacity(props.getPropagationTaskExecutorAsyncExecutor().getQueueCapacity());
         executor.setThreadNamePrefix("PropagationTaskExecutor-");
         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
         executor.initialize();
@@ -147,7 +133,7 @@
     }
 
     @Bean
-    public SchedulerDBInit quartzDataSourceInit() {
+    public SchedulerDBInit quartzDataSourceInit() throws JsonProcessingException {
         SchedulerDBInit init = new SchedulerDBInit();
         init.setDataSource(masterDataSource);
 
@@ -155,7 +141,7 @@
         databasePopulator.setContinueOnError(true);
         databasePopulator.setIgnoreFailedDrops(true);
         databasePopulator.setSqlScriptEncoding(StandardCharsets.UTF_8.name());
-        databasePopulator.setScripts(new ClassPathResource("/quartz/" + env.getProperty("quartz.sql")));
+        databasePopulator.setScripts(new ClassPathResource("/quartz/" + props.getQuartz().getSql()));
         init.setDatabasePopulator(databasePopulator);
 
         return init;
@@ -176,10 +162,14 @@
 
         Properties quartzProperties = new Properties();
         quartzProperties.setProperty(
-                "org.quartz.scheduler.idleWaitTime", env.getProperty("quartz.scheduler.idleWaitTime", "30000"));
+                "org.quartz.scheduler.idleWaitTime",
+                String.valueOf(props.getQuartz().getIdleWaitTime()));
         quartzProperties.setProperty(
-                "org.quartz.jobStore.misfireThreshold", env.getProperty("quartz.misfireThreshold", "60000"));
-        quartzProperties.setProperty("org.quartz.jobStore.driverDelegateClass", env.getProperty("quartz.jobstore"));
+                "org.quartz.jobStore.misfireThreshold",
+                String.valueOf(props.getQuartz().getMisfireThreshold()));
+        quartzProperties.setProperty(
+                "org.quartz.jobStore.driverDelegateClass",
+                props.getQuartz().getDelegate().getName());
         quartzProperties.setProperty("org.quartz.jobStore.isClustered", "true");
         quartzProperties.setProperty("org.quartz.jobStore.clusterCheckinInterval", "20000");
         quartzProperties.setProperty("org.quartz.scheduler.instanceName", "ClusteredScheduler");
@@ -198,7 +188,7 @@
     @Bean
     public JobManager jobManager() {
         JobManagerImpl jobManager = new JobManagerImpl();
-        jobManager.setDisableQuartzInstance(env.getProperty("quartz.disableInstance", Boolean.class, false));
+        jobManager.setDisableQuartzInstance(props.getQuartz().isDisableInstance());
         return jobManager;
     }
 
@@ -219,12 +209,12 @@
                 return super.connectTransport();
             }
         };
-        mailSender.setDefaultEncoding(env.getProperty("smtpEncoding"));
-        mailSender.setHost(env.getProperty("smtpHost"));
-        mailSender.setPort(env.getProperty("smtpPort", Integer.class));
-        mailSender.setUsername(env.getProperty("smtpUsername"));
-        mailSender.setPassword(env.getProperty("smtpPassword"));
-        mailSender.setProtocol(env.getProperty("smtpProtocol"));
+        mailSender.setDefaultEncoding(props.getSmtp().getDefaultEncoding());
+        mailSender.setHost(props.getSmtp().getHost());
+        mailSender.setPort(props.getSmtp().getPort());
+        mailSender.setUsername(props.getSmtp().getUsername());
+        mailSender.setPassword(props.getSmtp().getPassword());
+        mailSender.setProtocol(props.getSmtp().getProtocol());
 
         if (LOG.isDebugEnabled()) {
             LOG.debug("[Mail] host:port = {}:{}", mailSender.getHost(), mailSender.getPort());
@@ -245,13 +235,8 @@
         if (session == null) {
             Properties javaMailProperties = mailSender.getJavaMailProperties();
 
-            Properties props = PropertyUtils.read(ProvisioningContext.class, "mail.properties", "conf.directory");
-            for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) {
-                String prop = (String) e.nextElement();
-                if (prop.startsWith("mail.smtp.")) {
-                    javaMailProperties.setProperty(prop, props.getProperty(prop));
-                }
-            }
+            props.getSmtp().getJavamailProperties().
+                    forEach((key, value) -> javaMailProperties.setProperty(key, value));
 
             if (StringUtils.isNotBlank(mailSender.getUsername())) {
                 javaMailProperties.setProperty("mail.smtp.auth", "true");
@@ -262,8 +247,7 @@
                         forEach((key, value) -> LOG.debug("[Mail] property: {} = {}", key, value));
             }
 
-            String mailDebug = props.getProperty("mail.debug", "false");
-            if (BooleanUtils.toBoolean(mailDebug)) {
+            if (props.getSmtp().isDebug()) {
                 session = mailSender.getSession();
                 session.setDebug(true);
                 try (LogOutputStream los = new LogOutputStream(LOG)) {
@@ -279,16 +263,16 @@
 
     @ConditionalOnMissingBean
     @Bean
-    public PropagationManager propagationManager() {
-        return new PropagationManagerImpl();
+    public PropagationManager propagationManager() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+
+        return props.getPropagationManager().getDeclaredConstructor().newInstance();
     }
 
     @ConditionalOnMissingBean
     @Bean
     public ConnIdBundleManager connIdBundleManager() {
-        ConnIdBundleManagerImpl connIdBundleManager = new ConnIdBundleManagerImpl();
-        connIdBundleManager.setStringLocations(env.getProperty("connid.locations"));
-        return connIdBundleManager;
+        return new ConnIdBundleManagerImpl(props.getConnIdLocation());
     }
 
     @Bean
@@ -297,67 +281,53 @@
     }
 
     @Bean
-    public PropagationTaskExecutor propagationTaskExecutor()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public PropagationTaskExecutor propagationTaskExecutor() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (PropagationTaskExecutor) Class.forName(env.getProperty("propagationTaskExecutor")).
-                getConstructor().newInstance();
+        return props.getPropagationTaskExecutor().getDeclaredConstructor().newInstance();
     }
 
     @Bean
-    public UserProvisioningManager userProvisioningManager()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public UserProvisioningManager userProvisioningManager() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (UserProvisioningManager) Class.forName(env.getProperty("userProvisioningManager")).
-                getConstructor().newInstance();
+        return props.getUserProvisioningManager().getDeclaredConstructor().newInstance();
     }
 
     @Bean
-    public GroupProvisioningManager groupProvisioningManager()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public GroupProvisioningManager groupProvisioningManager() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (GroupProvisioningManager) Class.forName(env.getProperty("groupProvisioningManager")).
-                getConstructor().newInstance();
+        return props.getGroupProvisioningManager().getDeclaredConstructor().newInstance();
     }
 
     @Bean
-    public AnyObjectProvisioningManager anyObjectProvisioningManager()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public AnyObjectProvisioningManager anyObjectProvisioningManager() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (AnyObjectProvisioningManager) Class.forName(env.getProperty("anyObjectProvisioningManager")).
-                getConstructor().newInstance();
+        return props.getAnyObjectProvisioningManager().getDeclaredConstructor().newInstance();
     }
 
     @Bean
-    public VirAttrCache virAttrCache()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public VirAttrCache virAttrCache() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        VirAttrCache virAttrCache = (VirAttrCache) Class.forName(env.getProperty("virAttrCache")).
-                getConstructor().newInstance();
-        virAttrCache.setCacheSpec(env.getProperty("virAttrCacheSpec", "maximumSize=5000,expireAfterAccess=1m"));
+        VirAttrCache virAttrCache = props.getVirAttrCache().getDeclaredConstructor().newInstance();
+        virAttrCache.setCacheSpec(props.getVirAttrCacheSpec());
         return virAttrCache;
     }
 
     @Bean
-    public NotificationManager notificationManager()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public NotificationManager notificationManager() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (NotificationManager) Class.forName(env.getProperty("notificationManager")).
-                getConstructor().newInstance();
+        return props.getNotifcationManager().getDeclaredConstructor().newInstance();
     }
 
     @Bean
-    public AuditManager auditManager()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
-            IllegalArgumentException, InvocationTargetException {
+    public AuditManager auditManager() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (AuditManager) Class.forName(env.getProperty("auditManager")).
-                getConstructor().newInstance();
+        return props.getAuditManager().getDeclaredConstructor().newInstance();
     }
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningProperties.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningProperties.java
new file mode 100644
index 0000000..a86fdab
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningProperties.java
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
+import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
+import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache;
+import org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager;
+import org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationManager;
+import org.quartz.impl.jdbcjobstore.DriverDelegate;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("provisioning")
+public class ProvisioningProperties {
+
+    public static class ExecutorProperties {
+
+        private int corePoolSize = 5;
+
+        private int maxPoolSize = 25;
+
+        private int queueCapacity = 100;
+
+        public int getCorePoolSize() {
+            return corePoolSize;
+        }
+
+        public void setCorePoolSize(final int corePoolSize) {
+            this.corePoolSize = corePoolSize;
+        }
+
+        public int getMaxPoolSize() {
+            return maxPoolSize;
+        }
+
+        public void setMaxPoolSize(final int maxPoolSize) {
+            this.maxPoolSize = maxPoolSize;
+        }
+
+        public int getQueueCapacity() {
+            return queueCapacity;
+        }
+
+        public void setQueueCapacity(final int queueCapacity) {
+            this.queueCapacity = queueCapacity;
+        }
+    }
+
+    public static class QuartzProperties {
+
+        private Class<? extends DriverDelegate> delegate;
+
+        private String sql;
+
+        private boolean disableInstance = false;
+
+        private int idleWaitTime = 30000;
+
+        private int misfireThreshold = 60000;
+
+        public Class<? extends DriverDelegate> getDelegate() {
+            return delegate;
+        }
+
+        public void setDelegate(final Class<? extends DriverDelegate> delegate) {
+            this.delegate = delegate;
+        }
+
+        public String getSql() {
+            return sql;
+        }
+
+        public void setSql(final String sql) {
+            this.sql = sql;
+        }
+
+        public boolean isDisableInstance() {
+            return disableInstance;
+        }
+
+        public void setDisableInstance(final boolean disableInstance) {
+            this.disableInstance = disableInstance;
+        }
+
+        public int getIdleWaitTime() {
+            return idleWaitTime;
+        }
+
+        public void setIdleWaitTime(final int idleWaitTime) {
+            this.idleWaitTime = idleWaitTime;
+        }
+
+        public int getMisfireThreshold() {
+            return misfireThreshold;
+        }
+
+        public void setMisfireThreshold(final int misfireThreshold) {
+            this.misfireThreshold = misfireThreshold;
+        }
+    }
+
+    public static class SMTPProperties {
+
+        private String host;
+
+        private int port = 25;
+
+        private String username;
+
+        private String password;
+
+        private String protocol = "smtp";
+
+        private String defaultEncoding = "UTF-8";
+
+        private boolean debug = false;
+
+        private final Map<String, String> javamailProperties = new HashMap<>();
+
+        public String getHost() {
+            return host;
+        }
+
+        public void setHost(final String host) {
+            this.host = host;
+        }
+
+        public int getPort() {
+            return port;
+        }
+
+        public void setPort(final int port) {
+            this.port = port;
+        }
+
+        public String getUsername() {
+            return username;
+        }
+
+        public void setUsername(final String username) {
+            this.username = username;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+
+        public void setPassword(final String password) {
+            this.password = password;
+        }
+
+        public String getProtocol() {
+            return protocol;
+        }
+
+        public void setProtocol(final String protocol) {
+            this.protocol = protocol;
+        }
+
+        public String getDefaultEncoding() {
+            return defaultEncoding;
+        }
+
+        public void setDefaultEncoding(final String defaultEncoding) {
+            this.defaultEncoding = defaultEncoding;
+        }
+
+        public boolean isDebug() {
+            return debug;
+        }
+
+        public void setDebug(final boolean debug) {
+            this.debug = debug;
+        }
+
+        public Map<String, String> getJavamailProperties() {
+            return javamailProperties;
+        }
+    }
+
+    private final ExecutorProperties asyncConnectorFacadeExecutor = new ExecutorProperties();
+
+    private final ExecutorProperties propagationTaskExecutorAsyncExecutor = new ExecutorProperties();
+
+    private Class<? extends PropagationManager> propagationManager = DefaultPropagationManager.class;
+
+    private Class<? extends PropagationTaskExecutor> propagationTaskExecutor = PriorityPropagationTaskExecutor.class;
+
+    private Class<? extends UserProvisioningManager> userProvisioningManager =
+            DefaultUserProvisioningManager.class;
+
+    private Class<? extends GroupProvisioningManager> groupProvisioningManager =
+            DefaultGroupProvisioningManager.class;
+
+    private Class<? extends AnyObjectProvisioningManager> anyObjectProvisioningManager =
+            DefaultAnyObjectProvisioningManager.class;
+
+    private Class<? extends VirAttrCache> virAttrCache = CaffeineVirAttrCache.class;
+
+    private String virAttrCacheSpec = "maximumSize=5000,expireAfterAccess=1m";
+
+    private Class<? extends NotificationManager> notifcationManager = DefaultNotificationManager.class;
+
+    private Class<? extends AuditManager> auditManager = DefaultAuditManager.class;
+
+    private final List<String> connIdLocation = new ArrayList<>();
+
+    private final QuartzProperties quartz = new QuartzProperties();
+
+    private final SMTPProperties smtp = new SMTPProperties();
+
+    public Class<? extends PropagationManager> getPropagationManager() {
+        return propagationManager;
+    }
+
+    public void setPropagationManager(final Class<? extends PropagationManager> propagationManager) {
+        this.propagationManager = propagationManager;
+    }
+
+    public Class<? extends PropagationTaskExecutor> getPropagationTaskExecutor() {
+        return propagationTaskExecutor;
+    }
+
+    public void setPropagationTaskExecutor(final Class<? extends PropagationTaskExecutor> propagationTaskExecutor) {
+        this.propagationTaskExecutor = propagationTaskExecutor;
+    }
+
+    public Class<? extends UserProvisioningManager> getUserProvisioningManager() {
+        return userProvisioningManager;
+    }
+
+    public void setUserProvisioningManager(final Class<? extends UserProvisioningManager> userProvisioningManager) {
+        this.userProvisioningManager = userProvisioningManager;
+    }
+
+    public Class<? extends GroupProvisioningManager> getGroupProvisioningManager() {
+        return groupProvisioningManager;
+    }
+
+    public void setGroupProvisioningManager(final Class<? extends GroupProvisioningManager> groupProvisioningManager) {
+        this.groupProvisioningManager = groupProvisioningManager;
+    }
+
+    public Class<? extends AnyObjectProvisioningManager> getAnyObjectProvisioningManager() {
+        return anyObjectProvisioningManager;
+    }
+
+    public void setAnyObjectProvisioningManager(
+            final Class<? extends AnyObjectProvisioningManager> anyObjectProvisioningManager) {
+
+        this.anyObjectProvisioningManager = anyObjectProvisioningManager;
+    }
+
+    public Class<? extends VirAttrCache> getVirAttrCache() {
+        return virAttrCache;
+    }
+
+    public void setVirAttrCache(final Class<? extends VirAttrCache> virAttrCache) {
+        this.virAttrCache = virAttrCache;
+    }
+
+    public String getVirAttrCacheSpec() {
+        return virAttrCacheSpec;
+    }
+
+    public void setVirAttrCacheSpec(final String virAttrCacheSpec) {
+        this.virAttrCacheSpec = virAttrCacheSpec;
+    }
+
+    public Class<? extends NotificationManager> getNotifcationManager() {
+        return notifcationManager;
+    }
+
+    public void setNotifcationManager(final Class<? extends NotificationManager> notifcationManager) {
+        this.notifcationManager = notifcationManager;
+    }
+
+    public Class<? extends AuditManager> getAuditManager() {
+        return auditManager;
+    }
+
+    public void setAuditManager(final Class<? extends AuditManager> auditManager) {
+        this.auditManager = auditManager;
+    }
+
+    public ExecutorProperties getAsyncConnectorFacadeExecutor() {
+        return asyncConnectorFacadeExecutor;
+    }
+
+    public ExecutorProperties getPropagationTaskExecutorAsyncExecutor() {
+        return propagationTaskExecutorAsyncExecutor;
+    }
+
+    public List<String> getConnIdLocation() {
+        return connIdLocation;
+    }
+
+    public QuartzProperties getQuartz() {
+        return quartz;
+    }
+
+    public SMTPProperties getSmtp() {
+        return smtp;
+    }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
index cb9ff1f..42bd842 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderImpl.java
@@ -26,7 +26,6 @@
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Map;
-import javax.annotation.Resource;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -39,6 +38,7 @@
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.DefaultCredentialChecker;
 import org.apache.syncope.core.spring.security.SecureRandomUtils;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.apache.syncope.core.spring.security.jws.AccessTokenJWSSigner;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -46,11 +46,8 @@
 @Component
 public class AccessTokenDataBinderImpl implements AccessTokenDataBinder {
 
-    @Resource(name = "adminUser")
-    private String adminUser;
-
-    @Resource(name = "jwtIssuer")
-    private String jwtIssuer;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @Autowired
     private AccessTokenJWSSigner jwsSigner;
@@ -86,7 +83,7 @@
                 jwtID(tokenId).
                 subject(subject).
                 issueTime(currentTime).
-                issuer(jwtIssuer).
+                issuer(securityProperties.getJwtIssuer()).
                 expirationTime(expiration.getTime()).
                 notBeforeTime(currentTime);
         claims.forEach(claimsSet::claim);
@@ -118,7 +115,7 @@
         accessToken.setExpirationTime(generated.getRight());
         accessToken.setOwner(subject);
 
-        if (!adminUser.equals(accessToken.getOwner())) {
+        if (!securityProperties.getAdminUser().equals(accessToken.getOwner())) {
             accessToken.setAuthorities(authorities);
         }
 
@@ -179,7 +176,7 @@
         accessToken.setBody(body);
         accessToken.setExpirationTime(expiration.getTime());
 
-        if (!adminUser.equals(accessToken.getOwner())) {
+        if (!securityProperties.getAdminUser().equals(accessToken.getOwner())) {
             accessToken.setAuthorities(authorities);
         }
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
index 6cf9a19..4f8f109 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
@@ -22,7 +22,6 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.stream.Collectors;
-import javax.annotation.Resource;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.AnyTypeTO;
@@ -42,6 +41,7 @@
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.Encryptor;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,8 +55,8 @@
 
     private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
 
-    @Resource(name = "adminUser")
-    private String adminUser;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @Autowired
     private AnyTypeDAO anyTypeDAO;
@@ -77,7 +77,7 @@
 
         Set<String> added = EntitlementsHolder.getInstance().addFor(anyType.getKey());
 
-        if (!adminUser.equals(AuthContextUtils.getUsername())) {
+        if (!securityProperties.getAdminUser().equals(AuthContextUtils.getUsername())) {
             AccessToken accessToken = accessTokenDAO.findByOwner(AuthContextUtils.getUsername());
             try {
                 Set<SyncopeGrantedAuthority> authorities = new HashSet<>(POJOHelper.deserialize(
@@ -134,7 +134,7 @@
 
         final Set<String> removed = EntitlementsHolder.getInstance().removeFor(deleted.getKey());
 
-        if (!adminUser.equals(AuthContextUtils.getUsername())) {
+        if (!securityProperties.getAdminUser().equals(AuthContextUtils.getUsername())) {
             AccessToken accessToken = accessTokenDAO.findByOwner(AuthContextUtils.getUsername());
             try {
                 Set<SyncopeGrantedAuthority> authorities = new HashSet<>(POJOHelper.deserialize(
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index bec14a0..1525660 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -25,7 +25,6 @@
 import java.util.Set;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
-import javax.annotation.Resource;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
@@ -76,6 +75,7 @@
 import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.user.URelationship;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -105,11 +105,8 @@
     @Autowired
     private ConfParamOps confParamOps;
 
-    @Resource(name = "adminUser")
-    private String adminUser;
-
-    @Resource(name = "anonymousUser")
-    private String anonymousUser;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @Transactional(readOnly = true)
     @Override
@@ -127,14 +124,14 @@
         UserTO authUserTO;
 
         String authUsername = AuthContextUtils.getUsername();
-        if (anonymousUser.equals(authUsername)) {
+        if (securityProperties.getAnonymousUser().equals(authUsername)) {
             authUserTO = new UserTO();
             authUserTO.setKey(null);
-            authUserTO.setUsername(anonymousUser);
-        } else if (adminUser.equals(authUsername)) {
+            authUserTO.setUsername(securityProperties.getAnonymousUser());
+        } else if (securityProperties.getAdminUser().equals(authUsername)) {
             authUserTO = new UserTO();
             authUserTO.setKey(null);
-            authUserTO.setUsername(adminUser);
+            authUserTO.setUsername(securityProperties.getAdminUser());
         } else {
             User authUser = userDAO.findByUsername(authUsername);
             authUserTO = getUserTO(authUser, true);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
index df88571..59f93b8 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java
@@ -21,7 +21,6 @@
 import java.util.Date;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
-import javax.annotation.Resource;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.core.provisioning.api.job.JobManager;
 import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
@@ -34,6 +33,7 @@
 import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
@@ -45,8 +45,8 @@
 
     protected static final Logger LOG = LoggerFactory.getLogger(SchedTaskJobDelegate.class);
 
-    @Resource(name = "adminUser")
-    protected String adminUser;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     /**
      * The actual task to be executed.
@@ -117,7 +117,7 @@
         }
 
         String executor = Optional.ofNullable(context.getMergedJobDataMap().getString(JobManager.EXECUTOR_KEY)).
-                orElse(adminUser);
+                orElse(securityProperties.getAdminUser());
         TaskExec execution = entityFactory.newEntity(TaskExec.class);
         execution.setStart(new Date());
         execution.setTask(task);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java
index 257efae..59cf981 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/JobManagerImpl.java
@@ -29,7 +29,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import javax.annotation.Resource;
 import javax.sql.DataSource;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
@@ -77,6 +76,7 @@
 import org.apache.syncope.core.provisioning.java.job.report.ReportJob;
 import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
 import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 
 public class JobManagerImpl implements JobManager, SyncopeCoreLoader {
 
@@ -100,8 +100,8 @@
     @Autowired
     private ConfParamOps confParamOps;
 
-    @Resource(name = "adminUser")
-    private String adminUser;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     private boolean disableQuartzInstance;
 
@@ -347,7 +347,7 @@
             for (Iterator<SchedTask> it = tasks.iterator(); it.hasNext() && !loadException;) {
                 SchedTask task = it.next();
                 try {
-                    register(task, task.getStartAt(), conf.getRight(), adminUser);
+                    register(task, task.getStartAt(), conf.getRight(), securityProperties.getAdminUser());
                 } catch (Exception e) {
                     LOG.error("While loading job instance for task " + task.getKey(), e);
                     loadException = true;
@@ -361,7 +361,7 @@
                 for (Iterator<Report> it = reportDAO.findAll().iterator(); it.hasNext() && !loadException;) {
                     Report report = it.next();
                     try {
-                        register(report, null, conf.getRight(), adminUser);
+                        register(report, null, conf.getRight(), securityProperties.getAdminUser());
                     } catch (Exception e) {
                         LOG.error("While loading job instance for report " + report.getName(), e);
                         loadException = true;
@@ -387,7 +387,7 @@
 
                 try {
                     NotificationJob job = createSpringBean(NotificationJob.class);
-                    Map<String, Object> jobData = createJobMapForExecutionContext(adminUser);
+                    Map<String, Object> jobData = createJobMapForExecutionContext(securityProperties.getAdminUser());
                     registerJob(
                             NOTIFICATION_JOB.getName(),
                             job,
@@ -403,7 +403,7 @@
             LOG.debug("Registering {}", SystemLoadReporterJob.class);
             try {
                 SystemLoadReporterJob job = createSpringBean(SystemLoadReporterJob.class);
-                Map<String, Object> jobData = createJobMapForExecutionContext(adminUser);
+                Map<String, Object> jobData = createJobMapForExecutionContext(securityProperties.getAdminUser());
                 registerJob(
                         StringUtils.uncapitalize(SystemLoadReporterJob.class.getSimpleName()),
                         job,
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SetUMembershipsJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SetUMembershipsJob.java
index 49370be..a9d9a52 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SetUMembershipsJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SetUMembershipsJob.java
@@ -23,7 +23,6 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import javax.annotation.Resource;
 import org.apache.syncope.common.lib.request.MembershipUR;
 import org.apache.syncope.common.lib.request.UserUR;
 import org.apache.syncope.common.lib.types.PatchOperation;
@@ -31,6 +30,7 @@
 import org.apache.syncope.core.provisioning.api.job.JobManager;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
@@ -51,8 +51,8 @@
 
     public static final String CONTEXT = "context";
 
-    @Resource(name = "adminUser")
-    private String adminUser;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @Autowired
     private UserProvisioningManager userProvisioningManager;
@@ -60,7 +60,7 @@
     @Override
     public void execute(final JobExecutionContext context) throws JobExecutionException {
         String executor = Optional.ofNullable(context.getMergedJobDataMap().getString(JobManager.EXECUTOR_KEY)).
-                orElse(adminUser);
+                orElse(securityProperties.getAdminUser());
 
         try {
             AuthContextUtils.callAsAdmin(context.getMergedJobDataMap().getString(JobManager.DOMAIN_KEY), () -> {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
index cb4eaf5..8e88f9e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java
@@ -19,13 +19,13 @@
 package org.apache.syncope.core.provisioning.java.job.notification;
 
 import java.util.Optional;
-import javax.annotation.Resource;
 import org.apache.syncope.core.persistence.api.DomainHolder;
 import org.apache.syncope.core.provisioning.api.job.JobManager;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.provisioning.api.job.JobDelegate;
 import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
 import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
@@ -48,12 +48,12 @@
 
     }
 
-    public static final String DEFAULT_CRON_EXP = "0 0/5 * * * ?";
-
     private static final Logger LOG = LoggerFactory.getLogger(NotificationJob.class);
 
-    @Resource(name = "adminUser")
-    private String adminUser;
+    public static final String DEFAULT_CRON_EXP = "0 0/5 * * * ?";
+
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @Autowired
     private DomainHolder domainHolder;
@@ -70,7 +70,7 @@
     public void execute(final JobExecutionContext context) throws JobExecutionException {
         LOG.debug("Waking up...");
         String executor = Optional.ofNullable(context.getMergedJobDataMap().getString(JobManager.EXECUTOR_KEY)).
-                orElse(adminUser);
+                orElse(securityProperties.getAdminUser());
         for (String domain : domainHolder.getDomains().keySet()) {
             try {
                 AuthContextUtils.callAsAdmin(domain, () -> {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
index ddebbb4..7be8405 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.provisioning.java.job.report;
 
 import java.util.Optional;
-import javax.annotation.Resource;
 import org.apache.syncope.core.provisioning.api.job.JobDelegate;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.provisioning.java.job.AbstractInterruptableJob;
@@ -28,6 +27,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.apache.syncope.core.provisioning.api.job.JobManager;
 import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,8 +38,8 @@
 
     private static final Logger LOG = LoggerFactory.getLogger(ReportJob.class);
 
-    @Resource(name = "adminUser")
-    private String adminUser;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     /**
      * Key, set by the caller, for identifying the report to be executed.
@@ -68,7 +68,7 @@
         try {
             String domainKey = context.getMergedJobDataMap().getString(JobManager.DOMAIN_KEY);
             String executor = Optional.ofNullable(context.getMergedJobDataMap().getString(JobManager.EXECUTOR_KEY)).
-                    orElse(adminUser);
+                    orElse(securityProperties.getAdminUser());
 
             AuthContextUtils.callAsAdmin(domainKey, () -> {
                 try {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
similarity index 99%
rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
rename to core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
index 2edba41..2a774e6 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
@@ -72,7 +72,7 @@
  * Manage the data propagation to external resources.
  */
 @Transactional(rollbackFor = { Throwable.class })
-public class PropagationManagerImpl implements PropagationManager {
+public class DefaultPropagationManager implements PropagationManager {
 
     protected static final Logger LOG = LoggerFactory.getLogger(PropagationManager.class);
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index 67d245d..ac41367 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -473,7 +473,7 @@
                                 null,
                                 null),
                                 false,
-                                adminUser);
+                                securityProperties.getAdminUser());
 
                         AnyUR anyUR = null;
                         if (matchingRule == MatchingRule.UNASSIGN) {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 70e229c..046ab72 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -119,7 +119,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.of(beforeObj));
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
     }
@@ -143,7 +143,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.of(beforeObj));
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
     }
@@ -167,7 +167,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.empty());
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
     }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
index b22a390..5d9d05f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.provisioning.java.pushpull;
 
-import javax.annotation.Resource;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 import org.apache.syncope.core.provisioning.api.AuditManager;
@@ -29,6 +28,7 @@
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningActions;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopeResultHandler;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -68,8 +68,8 @@
     @Autowired
     protected PropagationTaskExecutor taskExecutor;
 
-    @Resource(name = "adminUser")
-    protected String adminUser;
+    @Autowired
+    protected SecurityProperties securityProperties;
 
     /**
      * Provisioning profile.
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
index 54f9cdf..e72983c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.provisioning.java.pushpull;
 
-import javax.annotation.Resource;
 import org.apache.syncope.common.lib.request.AnyUR;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.core.persistence.api.entity.Any;
@@ -34,6 +33,7 @@
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningActions;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
@@ -85,8 +85,8 @@
     @Autowired
     protected AnyUtilsFactory anyUtilsFactory;
 
-    @Resource(name = "adminUser")
-    protected String adminUser;
+    @Autowired
+    protected SecurityProperties securityProperties;
 
     /**
      * Provisioning profile.
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
index aa1382b..6d26d10 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
@@ -239,7 +239,7 @@
             propByRes.addAll(ResourceOperation.CREATE, realm.getResourceKeys());
             if (unmatchingRule == UnmatchingRule.ASSIGN) {
                 List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
-                taskExecutor.execute(taskInfos, false, adminUser);
+                taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser());
             }
 
             RealmTO actual = binder.getRealmTO(realm, true);
@@ -314,7 +314,7 @@
                     RealmTO updated = binder.getRealmTO(realm, true);
 
                     List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
-                    taskExecutor.execute(taskInfos, false, adminUser);
+                    taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser());
 
                     for (PullActions action : profile.getActions()) {
                         action.after(profile, delta, updated, result);
@@ -393,7 +393,9 @@
 
                     PropagationByResource<String> propByRes = new PropagationByResource<>();
                     propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey());
-                    taskExecutor.execute(propagationManager.createTasks(realm, propByRes, null), false, adminUser);
+                    taskExecutor.execute(
+                            propagationManager.createTasks(realm, propByRes, null),
+                            false, securityProperties.getAdminUser());
 
                     RealmTO realmTO;
                     if (unlink) {
@@ -572,7 +574,7 @@
                         PropagationByResource<String> propByRes = new PropagationByResource<>();
                         propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
                         List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
-                        taskExecutor.execute(taskInfos, false, adminUser);
+                        taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser());
 
                         realmDAO.delete(realm);
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
index b89e630..6195b30 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
@@ -111,7 +111,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.ofNullable(beforeObj));
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
 
@@ -129,7 +129,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.ofNullable(beforeObj));
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
     }
@@ -142,7 +142,8 @@
         propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
 
         PropagationReporter reporter = taskExecutor.execute(
-                propagationManager.createTasks(realm, propByRes, noPropResources), false, adminUser);
+                propagationManager.createTasks(realm, propByRes, noPropResources),
+                false, securityProperties.getAdminUser());
         reportPropagation(result, reporter);
     }
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
index 7a64b72..6827ce0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
@@ -97,7 +97,7 @@
                 before.getVirAttrs(),
                 noPropResources),
                 false,
-                adminUser);
+                securityProperties.getAdminUser());
         reportPropagation(result, reporter);
     }
 
@@ -136,7 +136,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.of(beforeObj));
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
     }
@@ -167,7 +167,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.of(beforeObj));
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
     }
@@ -375,7 +375,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.empty());
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
     }
@@ -402,7 +402,7 @@
         if (!taskInfos.isEmpty()) {
             taskInfos.get(0).setBeforeObj(Optional.empty());
             PropagationReporter reporter = new DefaultPropagationReporter();
-            taskExecutor.execute(taskInfos.get(0), reporter, adminUser);
+            taskExecutor.execute(taskInfos.get(0), reporter, securityProperties.getAdminUser());
             reportPropagation(result, reporter);
         }
     }
diff --git a/core/provisioning-java/src/main/resources/connid.properties b/core/provisioning-java/src/main/resources/connid.properties
deleted file mode 100644
index 24d5c93..0000000
--- a/core/provisioning-java/src/main/resources/connid.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-connid.locations=${connid.location}
diff --git a/core/provisioning-java/src/main/resources/mail.properties b/core/provisioning-java/src/main/resources/mail.properties
deleted file mode 100644
index e0cb1b0..0000000
--- a/core/provisioning-java/src/main/resources/mail.properties
+++ /dev/null
@@ -1,30 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-conf.directory=${conf.directory}
-
-smtpHost=none.syncope.apache.org
-smtpPort=25
-smtpUser=
-smtpPassword=
-smtpProtocol=smtp
-smtpEncoding=UTF-8
-mail.debug=false
-
-# Add more properties starting with mail.smtp.* from
-# https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties
-mail.smtp.connectiontimeout=3000
-mail.smtp.starttls.enable=false
diff --git a/core/provisioning-java/src/main/resources/provisioning.properties b/core/provisioning-java/src/main/resources/provisioning.properties
deleted file mode 100644
index 096c89c..0000000
--- a/core/provisioning-java/src/main/resources/provisioning.properties
+++ /dev/null
@@ -1,36 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
-quartz.sql=tables_postgres.sql
-quartz.disableInstance=false
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyDomainOps.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyDomainOps.java
index d000e20..30320da 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyDomainOps.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyDomainOps.java
@@ -22,11 +22,16 @@
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
 import org.apache.syncope.common.keymaster.client.api.model.Domain;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.core.persistence.api.DomainRegistry;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 @Component
 public class DummyDomainOps implements DomainOps {
 
+    @Autowired
+    private DomainRegistry domainRegistry;
+
     @Override
     public List<Domain> list() {
         return List.of();
@@ -39,7 +44,7 @@
 
     @Override
     public void create(final Domain domain) {
-        // nothing to do
+        domainRegistry.register(domain);
     }
 
     @Override
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java
index 0b5123c..67b76cb 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java
@@ -18,24 +18,15 @@
  */
 package org.apache.syncope.core.provisioning.java;
 
-import java.io.IOException;
 import org.apache.syncope.core.persistence.jpa.PersistenceContext;
 import org.apache.syncope.core.spring.security.SecurityContext;
 import org.apache.syncope.core.workflow.java.WorkflowContext;
-import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
-import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+import org.springframework.context.annotation.PropertySource;
 
+@PropertySource("classpath:core-test.properties")
 @Import({ SecurityContext.class, PersistenceContext.class, ProvisioningContext.class, WorkflowContext.class })
 @Configuration
 public class ProvisioningTestContext {
-
-    @Bean
-    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
-        PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
-        pspc.setIgnoreResourceNotFound(true);
-        pspc.setIgnoreUnresolvablePlaceholders(true);
-        return pspc;
-    }
 }
diff --git a/ext/self-keymaster/persistence-jpa/pom.xml b/core/self-keymaster-starter/pom.xml
similarity index 77%
rename from ext/self-keymaster/persistence-jpa/pom.xml
rename to core/self-keymaster-starter/pom.xml
index 6d56d00..2ea32a4 100644
--- a/ext/self-keymaster/persistence-jpa/pom.xml
+++ b/core/self-keymaster-starter/pom.xml
@@ -22,31 +22,32 @@
   <modelVersion>4.0.0</modelVersion>
 
   <parent>
-    <groupId>org.apache.syncope.ext</groupId>
-    <artifactId>syncope-ext-self-keymaster</artifactId>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-core</artifactId>
     <version>3.0.0-SNAPSHOT</version>
   </parent>
 
-  <name>Apache Syncope Ext: Self Keymaster Persistence JPA</name>
-  <description>Apache Syncope Ext: Self Keymaster Persistence JPA</description>
-  <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-  <artifactId>syncope-ext-self-keymaster-persistence-jpa</artifactId>
+  <name>Apache Syncope Core Spring Boot Starter: Self Keymaster</name>
+  <description>Apache Syncope Core Spring Boot Starter: Self Keymaster</description>
+  <groupId>org.apache.syncope.core</groupId>
+  <artifactId>syncope-core-self-keymaster-starter</artifactId>
   <packaging>jar</packaging>
   
   <properties>
-    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
   </properties>
 
-  <dependencies>    
+  <dependencies>
     <dependency>
       <groupId>org.apache.syncope.core</groupId>
-      <artifactId>syncope-core-persistence-jpa</artifactId>
+      <artifactId>syncope-core-starter</artifactId>
       <version>${project.version}</version>
     </dependency>
+
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-persistence-api</artifactId>
-      <version>${project.version}</version>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-self-rest-api</artifactId>
+      <version>${project.version}</version>      
     </dependency>
   </dependencies>
 
@@ -64,7 +65,7 @@
           </dependency>
         </dependencies>
         <configuration>
-          <persistenceXmlFile>${rootpom.basedir}/core/persistence-jpa/src/main/resources/persistence-enhance.xml</persistenceXmlFile> 
+          <persistenceXmlFile>${rootpom.basedir}/core/persistence-jpa/src/main/resources/persistence-enhance.xml</persistenceXmlFile>
           <includes>org/apache/syncope/core/persistence/jpa/entity/**/*.class</includes>
           <connectionDriverName>org.springframework.jdbc.datasource.DriverManagerDataSource</connectionDriverName>
           <connectionProperties>
@@ -84,32 +85,32 @@
           </execution>
         </executions>
       </plugin>
-      
+
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
       </plugin>
     </plugins>
 
-    <testResources>
-      <testResource>
-        <directory>${rootpom.basedir}/core/persistence-jpa/src/main/resources</directory>
-        <filtering>true</filtering>        
-      </testResource>
-    </testResources>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
   </build>
-
+  
   <profiles>
     <profile>
       <id>sqlgen</id>
-      
+
       <properties>
         <skipTests>true</skipTests>
       </properties>
-      
+
       <build>
         <defaultGoal>clean verify</defaultGoal>
-        
+
         <plugins>
           <plugin>
             <groupId>org.apache.openjpa</groupId>
@@ -124,11 +125,10 @@
                 </goals>
               </execution>
             </executions>
-          </plugin>          
+          </plugin>
         </plugins>
       </build>
-        
-    </profile>
-  </profiles>
 
+    </profile>
+  </profiles>  
 </project>
diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalConfParamOps.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOps.java
similarity index 86%
rename from ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalConfParamOps.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOps.java
index 3d46cae..e3667e8 100644
--- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalConfParamOps.java
+++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOps.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.cxf.client;
+package org.apache.syncope.core.keymaster.internal;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -24,12 +24,12 @@
 import java.util.List;
 import java.util.Map;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
+import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
 import org.apache.syncope.core.logic.ConfParamLogic;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 
 public class SelfKeymasterInternalConfParamOps implements ConfParamOps {
 
@@ -40,24 +40,23 @@
     @Autowired
     private ConfParamLogic logic;
 
-    @Value("${keymaster.username}")
-    private String keymasterUser;
+    @Autowired
+    private KeymasterProperties props;
 
     @Override
     public Map<String, Object> list(final String domain) {
         return AuthContextUtils.callAs(
                 domain,
-                keymasterUser,
+                props.getUsername(),
                 List.of(),
                 () -> logic.list());
     }
 
     @Override
     public <T> T get(final String domain, final String key, final T defaultValue, final Class<T> reference) {
-
         JsonNode valueNode = AuthContextUtils.callAs(
                 domain,
-                keymasterUser,
+                props.getUsername(),
                 List.of(),
                 () -> logic.get(key));
         if (valueNode == null) {
@@ -79,7 +78,7 @@
         } else {
             JsonNode valueNode = MAPPER.valueToTree(value);
 
-            AuthContextUtils.callAs(domain, keymasterUser, List.of(), () -> {
+            AuthContextUtils.callAs(domain, props.getUsername(), List.of(), () -> {
                 logic.set(key, valueNode);
                 return null;
             });
@@ -88,7 +87,7 @@
 
     @Override
     public void remove(final String domain, final String key) {
-        AuthContextUtils.callAs(domain, keymasterUser, List.of(), () -> {
+        AuthContextUtils.callAs(domain, props.getUsername(), List.of(), () -> {
             logic.remove(key);
             return null;
         });
diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalDomainOps.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalDomainOps.java
similarity index 74%
rename from ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalDomainOps.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalDomainOps.java
index 8605cf9..dff60fc 100644
--- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalDomainOps.java
+++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalDomainOps.java
@@ -16,39 +16,47 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.cxf.client;
+package org.apache.syncope.core.keymaster.internal;
 
 import java.util.List;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
+import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
 import org.apache.syncope.common.keymaster.client.api.model.Domain;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.logic.DomainLogic;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 
 public class SelfKeymasterInternalDomainOps implements DomainOps {
 
     @Autowired
     private DomainLogic logic;
 
-    @Value("${keymaster.username}")
-    private String keymasterUser;
+    @Autowired
+    private KeymasterProperties props;
 
     @Override
     public List<Domain> list() {
-        return AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, keymasterUser, List.of(), () -> logic.list());
+        return AuthContextUtils.callAs(
+                SyncopeConstants.MASTER_DOMAIN,
+                props.getUsername(),
+                List.of(),
+                () -> logic.list());
     }
 
     @Override
     public Domain read(final String key) {
-        return AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, keymasterUser, List.of(), () -> logic.read(key));
+        return AuthContextUtils.callAs(
+                SyncopeConstants.MASTER_DOMAIN,
+                props.getUsername(),
+                List.of(),
+                () -> logic.read(key));
     }
 
     @Override
     public void create(final Domain domain) {
-        AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, keymasterUser, List.of(), () -> {
+        AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, props.getUsername(), List.of(), () -> {
             logic.create(domain);
             return null;
         });
@@ -56,7 +64,7 @@
 
     @Override
     public void changeAdminPassword(final String key, final String password, final CipherAlgorithm cipherAlgorithm) {
-        AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, keymasterUser, List.of(), () -> {
+        AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, props.getUsername(), List.of(), () -> {
             logic.changeAdminPassword(key, password, cipherAlgorithm);
             return null;
         });
@@ -64,15 +72,15 @@
 
     @Override
     public void adjustPoolSize(final String key, final int maxPoolSize, final int minIdle) {
-        AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, keymasterUser, List.of(), () -> {
-            logic.adjustPoolSize(keymasterUser, maxPoolSize, minIdle);
+        AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, props.getUsername(), List.of(), () -> {
+            logic.adjustPoolSize(props.getUsername(), maxPoolSize, minIdle);
             return null;
         });
     }
 
     @Override
     public void delete(final String key) {
-        AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, keymasterUser, List.of(), () -> {
+        AuthContextUtils.callAs(SyncopeConstants.MASTER_DOMAIN, props.getUsername(), List.of(), () -> {
             logic.delete(key);
             return null;
         });
diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalServiceOps.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOps.java
similarity index 88%
rename from ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalServiceOps.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOps.java
index f283bf6..96edfb9 100644
--- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/client/SelfKeymasterInternalServiceOps.java
+++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOps.java
@@ -16,31 +16,31 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.cxf.client;
+package org.apache.syncope.core.keymaster.internal;
 
 import java.util.List;
 import org.apache.syncope.common.keymaster.client.api.KeymasterException;
+import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.logic.NetworkServiceLogic;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 
 public class SelfKeymasterInternalServiceOps implements ServiceOps {
 
     @Autowired
     private NetworkServiceLogic logic;
 
-    @Value("${keymaster.username}")
-    private String keymasterUser;
+    @Autowired
+    private KeymasterProperties props;
 
     @Override
     public List<NetworkService> list(final NetworkService.Type serviceType) {
         return AuthContextUtils.callAs(
                 SyncopeConstants.MASTER_DOMAIN,
-                keymasterUser,
+                props.getUsername(),
                 List.of(),
                 () -> logic.list(serviceType));
     }
@@ -50,7 +50,7 @@
         try {
             return AuthContextUtils.callAs(
                     SyncopeConstants.MASTER_DOMAIN,
-                    keymasterUser,
+                    props.getUsername(),
                     List.of(),
                     () -> logic.get(serviceType));
         } catch (Exception e) {
@@ -62,7 +62,7 @@
     public void register(final NetworkService service) {
         AuthContextUtils.callAs(
                 SyncopeConstants.MASTER_DOMAIN,
-                keymasterUser,
+                props.getUsername(),
                 List.of(),
                 () -> {
                     logic.register(service);
@@ -74,7 +74,7 @@
     public void unregister(final NetworkService service) {
         AuthContextUtils.callAs(
                 SyncopeConstants.MASTER_DOMAIN,
-                keymasterUser,
+                props.getUsername(),
                 List.of(),
                 () -> {
                     logic.unregister(service);
diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/ConfParamServiceImpl.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImpl.java
similarity index 93%
rename from ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/ConfParamServiceImpl.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImpl.java
index 8295291..d468a1e 100644
--- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/ConfParamServiceImpl.java
+++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImpl.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.cxf.service;
+package org.apache.syncope.core.keymaster.rest.cxf.service;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -24,8 +24,8 @@
 import java.io.InputStream;
 import java.util.Map;
 import javax.ws.rs.core.Response;
+import org.apache.syncope.common.keymaster.rest.api.service.ConfParamService;
 import org.apache.syncope.core.logic.ConfParamLogic;
-import org.apache.syncope.ext.self.keymaster.api.service.ConfParamService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/DomainServiceImpl.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/DomainServiceImpl.java
similarity index 94%
rename from ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/DomainServiceImpl.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/DomainServiceImpl.java
index 2440922..6a7463d 100644
--- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/DomainServiceImpl.java
+++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/DomainServiceImpl.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.cxf.service;
+package org.apache.syncope.core.keymaster.rest.cxf.service;
 
 import java.net.URI;
 import java.util.List;
@@ -24,10 +24,10 @@
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.apache.syncope.common.keymaster.client.api.model.Domain;
+import org.apache.syncope.common.keymaster.rest.api.service.DomainService;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.core.logic.DomainLogic;
-import org.apache.syncope.ext.self.keymaster.api.service.DomainService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/NetworkServiceServiceImpl.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImpl.java
similarity index 92%
rename from ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/NetworkServiceServiceImpl.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImpl.java
index a026acc..27649ee 100644
--- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/NetworkServiceServiceImpl.java
+++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImpl.java
@@ -16,13 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.cxf.service;
+package org.apache.syncope.core.keymaster.rest.cxf.service;
 
 import java.util.List;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.apache.syncope.common.keymaster.rest.api.service.NetworkServiceService;
 import org.apache.syncope.core.logic.NetworkServiceLogic;
-import org.apache.syncope.ext.self.keymaster.api.service.NetworkServiceService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
diff --git a/ext/self-keymaster/logic/src/main/java/org/apache/syncope/core/logic/ConfParamLogic.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/ConfParamLogic.java
similarity index 100%
rename from ext/self-keymaster/logic/src/main/java/org/apache/syncope/core/logic/ConfParamLogic.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/ConfParamLogic.java
diff --git a/ext/self-keymaster/logic/src/main/java/org/apache/syncope/core/logic/DomainLogic.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/DomainLogic.java
similarity index 100%
rename from ext/self-keymaster/logic/src/main/java/org/apache/syncope/core/logic/DomainLogic.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/DomainLogic.java
diff --git a/ext/self-keymaster/logic/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java
similarity index 100%
rename from ext/self-keymaster/logic/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java
diff --git a/ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConfParamDAO.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/ConfParamDAO.java
similarity index 100%
rename from ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ConfParamDAO.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/ConfParamDAO.java
diff --git a/ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DomainDAO.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/DomainDAO.java
similarity index 100%
rename from ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DomainDAO.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/DomainDAO.java
diff --git a/ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/NetworkServiceDAO.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/NetworkServiceDAO.java
similarity index 100%
rename from ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/NetworkServiceDAO.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/NetworkServiceDAO.java
diff --git a/ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConfParam.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/ConfParam.java
similarity index 100%
rename from ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConfParam.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/ConfParam.java
diff --git a/ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DomainEntity.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/DomainEntity.java
similarity index 100%
rename from ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/DomainEntity.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/DomainEntity.java
diff --git a/ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/NetworkServiceEntity.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/NetworkServiceEntity.java
similarity index 100%
rename from ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/NetworkServiceEntity.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/NetworkServiceEntity.java
diff --git a/ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/SelfKeymasterEntityFactory.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/SelfKeymasterEntityFactory.java
similarity index 100%
rename from ext/self-keymaster/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/SelfKeymasterEntityFactory.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/SelfKeymasterEntityFactory.java
diff --git a/ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfParamDAO.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfParamDAO.java
similarity index 100%
rename from ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfParamDAO.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAConfParamDAO.java
diff --git a/ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADomainDAO.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADomainDAO.java
similarity index 100%
rename from ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADomainDAO.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADomainDAO.java
diff --git a/ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANetworkServiceDAO.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANetworkServiceDAO.java
similarity index 100%
rename from ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANetworkServiceDAO.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANetworkServiceDAO.java
diff --git a/ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConfParam.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConfParam.java
similarity index 100%
rename from ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConfParam.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConfParam.java
diff --git a/ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADomain.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADomain.java
similarity index 100%
rename from ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADomain.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADomain.java
diff --git a/ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANetworkService.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANetworkService.java
similarity index 100%
rename from ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANetworkService.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANetworkService.java
diff --git a/ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASelfKeymasterEntityFactory.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASelfKeymasterEntityFactory.java
similarity index 100%
rename from ext/self-keymaster/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASelfKeymasterEntityFactory.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASelfKeymasterEntityFactory.java
diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/security/SelfKeymasterUsernamePasswordAuthenticationProvider.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/rest/security/SelfKeymasterUsernamePasswordAuthenticationProvider.java
similarity index 80%
rename from ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/security/SelfKeymasterUsernamePasswordAuthenticationProvider.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/rest/security/SelfKeymasterUsernamePasswordAuthenticationProvider.java
index 68b63db..4c6ae94 100644
--- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/security/SelfKeymasterUsernamePasswordAuthenticationProvider.java
+++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/rest/security/SelfKeymasterUsernamePasswordAuthenticationProvider.java
@@ -16,30 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.cxf.security;
+package org.apache.syncope.core.rest.security;
 
+import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
 import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails;
 import org.apache.syncope.core.spring.security.UsernamePasswordAuthenticationProvider;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Configurable;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.core.Authentication;
 
 @Configurable
 public class SelfKeymasterUsernamePasswordAuthenticationProvider extends UsernamePasswordAuthenticationProvider {
 
-    @Value("${keymaster.username}")
-    private String keymasterUsername;
-
-    @Value("${keymaster.password}")
-    private String keymasterPassword;
+    @Autowired
+    private KeymasterProperties props;
 
     @Override
     public Authentication authenticate(final Authentication authentication) {
-        if (keymasterUsername.equals(authentication.getName())) {
+        if (props.getUsername().equals(authentication.getName())) {
             return finalizeAuthentication(
-                    authentication.getCredentials().toString().equals(keymasterPassword),
+                    authentication.getCredentials().toString().equals(props.getPassword()),
                     SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain(),
-                    keymasterUsername,
+                    props.getUsername(),
                     null,
                     authentication);
         }
diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/SelfKeymasterContext.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java
similarity index 66%
rename from ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/SelfKeymasterContext.java
rename to core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java
index 9fea5ec..073eb35 100644
--- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/SelfKeymasterContext.java
+++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java
@@ -16,11 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.ext.self.keymaster.cxf;
+package org.apache.syncope.core.starter;
 
 import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Pattern;
 import org.apache.cxf.Bus;
 import org.apache.cxf.endpoint.Server;
 import org.apache.cxf.jaxrs.spring.JAXRSServerFactoryBeanDefinitionParser.SpringJAXRSServerFactoryBean;
@@ -29,37 +30,54 @@
 import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
+import org.apache.syncope.common.keymaster.client.api.KeymasterProperties;
 import org.apache.syncope.common.keymaster.client.api.ServiceOps;
+import org.apache.syncope.core.keymaster.internal.SelfKeymasterInternalConfParamOps;
+import org.apache.syncope.core.keymaster.internal.SelfKeymasterInternalDomainOps;
+import org.apache.syncope.core.keymaster.internal.SelfKeymasterInternalServiceOps;
 import org.apache.syncope.core.rest.cxf.RestServiceExceptionMapper;
+import org.apache.syncope.core.rest.security.SelfKeymasterUsernamePasswordAuthenticationProvider;
 import org.apache.syncope.core.spring.security.UsernamePasswordAuthenticationProvider;
 import org.apache.syncope.core.spring.security.WebSecurityContext;
-import org.apache.syncope.ext.self.keymaster.cxf.client.SelfKeymasterInternalConfParamOps;
-import org.apache.syncope.ext.self.keymaster.cxf.client.SelfKeymasterInternalDomainOps;
-import org.apache.syncope.ext.self.keymaster.cxf.client.SelfKeymasterInternalServiceOps;
-import org.apache.syncope.ext.self.keymaster.cxf.security.SelfKeymasterUsernamePasswordAuthenticationProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
+import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.ConditionContext;
+import org.springframework.context.annotation.Conditional;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.type.AnnotatedTypeMetadata;
 
-@PropertySource("classpath:keymaster.properties")
-@PropertySource(value = "file:${conf.directory}/keymaster.properties", ignoreResourceNotFound = true)
-@ComponentScan("org.apache.syncope.ext.self.keymaster.cxf.service")
+@EnableConfigurationProperties(KeymasterProperties.class)
+@ComponentScan("org.apache.syncope.core.keymaster.rest.cxf.service")
 @Configuration
 @AutoConfigureBefore(WebSecurityContext.class)
-@ConditionalOnExpression("'${keymaster.address}' matches '^http.+'")
 public class SelfKeymasterContext {
 
+    private static final Pattern HTTP = Pattern.compile("^http.+");
+
+    static class SelfKeymasterCondition extends SpringBootCondition {
+
+        @Override
+        public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
+            String keymasterAddress = context.getEnvironment().getProperty("keymaster.address");
+            return new ConditionOutcome(
+                    keymasterAddress != null && HTTP.matcher(keymasterAddress).matches(),
+                    "Keymaster address not set for Self: " + keymasterAddress);
+        }
+    }
+
     @Autowired
     private Bus bus;
 
     @Autowired
     private ApplicationContext ctx;
 
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     public Server selfKeymasterContainer(final JacksonJsonProvider jsonProvider) {
         SpringJAXRSServerFactoryBean selfKeymasterContainer = new SpringJAXRSServerFactoryBean();
@@ -67,8 +85,8 @@
         selfKeymasterContainer.setAddress("/keymaster");
         selfKeymasterContainer.setStaticSubresourceResolution(true);
         selfKeymasterContainer.setBasePackages(List.of(
-                "org.apache.syncope.ext.self.keymaster.api.service",
-                "org.apache.syncope.ext.self.keymaster.cxf.service"));
+                "org.apache.syncope.common.keymaster.rest.api.service",
+                "org.apache.syncope.core.keymaster.rest.cxf.service"));
         selfKeymasterContainer.setProperties(Map.of("convert.wadl.resources.to.dom", "false"));
 
         selfKeymasterContainer.setInInterceptors(List.of(
@@ -86,21 +104,25 @@
         return selfKeymasterContainer.create();
     }
 
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     public UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider() {
         return new SelfKeymasterUsernamePasswordAuthenticationProvider();
     }
 
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     public ConfParamOps internalConfParamOps() {
         return new SelfKeymasterInternalConfParamOps();
     }
 
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     public ServiceOps internalServiceOps() {
         return new SelfKeymasterInternalServiceOps();
     }
 
+    @Conditional(SelfKeymasterCondition.class)
     @Bean
     public DomainOps domainOps() {
         return new SelfKeymasterInternalDomainOps();
diff --git a/ext/self-keymaster/rest-cxf/src/main/resources/META-INF/spring.factories b/core/self-keymaster-starter/src/main/resources/META-INF/spring.factories
similarity index 92%
rename from ext/self-keymaster/rest-cxf/src/main/resources/META-INF/spring.factories
rename to core/self-keymaster-starter/src/main/resources/META-INF/spring.factories
index 5f98d3e..af8390b 100644
--- a/ext/self-keymaster/rest-cxf/src/main/resources/META-INF/spring.factories
+++ b/core/self-keymaster-starter/src/main/resources/META-INF/spring.factories
@@ -16,4 +16,4 @@
 # under the License.
 
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-  org.apache.syncope.ext.self.keymaster.cxf.SelfKeymasterContext
+  org.apache.syncope.core.starter.SelfKeymasterContext
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/ApplicationContextProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/ApplicationContextProvider.java
index c82adbe..c4c550c 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/ApplicationContextProvider.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/ApplicationContextProvider.java
@@ -40,8 +40,10 @@
     }
 
     public static DefaultListableBeanFactory getBeanFactory() {
-        return Optional.ofNullable(BEAN_FACTORY).orElseGet(()
-            -> Optional.ofNullable(CTX).map(ctx -> (DefaultListableBeanFactory) ctx.getBeanFactory()).orElse(null));
+        return Optional.ofNullable(BEAN_FACTORY).
+                orElseGet(() -> Optional.ofNullable(CTX).
+                map(ctx -> (DefaultListableBeanFactory) ctx.getBeanFactory()).
+                orElse(null));
     }
 
     public static void setBeanFactory(final DefaultListableBeanFactory beanFactory) {
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/ResourceWithFallbackLoader.java b/core/spring/src/main/java/org/apache/syncope/core/spring/ResourceWithFallbackLoader.java
deleted file mode 100644
index 846b575..0000000
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/ResourceWithFallbackLoader.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.spring;
-
-import java.io.IOException;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.ArrayUtils;
-import org.springframework.context.ResourceLoaderAware;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.core.io.support.ResourcePatternResolver;
-
-public class ResourceWithFallbackLoader implements ResourceLoaderAware, ResourcePatternResolver {
-
-    private ResourcePatternResolver resolver;
-
-    private String primary;
-
-    private String fallback;
-
-    @Override
-    public void setResourceLoader(final ResourceLoader resourceLoader) {
-        this.resolver = (ResourcePatternResolver) resourceLoader;
-    }
-
-    public void setPrimary(final String primary) {
-        this.primary = primary;
-    }
-
-    public void setFallback(final String fallback) {
-        this.fallback = fallback;
-    }
-
-    @Override
-    public Resource getResource(final String location) {
-        Resource resource = resolver.getResource(primary + location);
-        if (!resource.exists()) {
-            resource = resolver.getResource(fallback + location);
-        }
-
-        return resource;
-    }
-
-    public Resource getResource() {
-        return getResource(StringUtils.EMPTY);
-    }
-
-    @Override
-    public Resource[] getResources(final String locationPattern) throws IOException {
-        Resource[] resources = resolver.getResources(primary + locationPattern);
-        if (ArrayUtils.isEmpty(resources)) {
-            resources = resolver.getResources(fallback + locationPattern);
-        }
-
-        return resources;
-    }
-
-    public Resource[] getResources() throws IOException {
-        return getResources(StringUtils.EMPTY);
-    }
-
-    @Override
-    public ClassLoader getClassLoader() {
-        return resolver.getClassLoader();
-    }
-}
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
index d7bc51e..e8e3046 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java
@@ -27,7 +27,6 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
-import javax.annotation.Resource;
 import javax.security.auth.login.AccountNotFoundException;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.BooleanUtils;
@@ -96,11 +95,8 @@
     protected static final Set<SyncopeGrantedAuthority> MUST_CHANGE_PASSWORD_AUTHORITIES =
             Set.of(new SyncopeGrantedAuthority(IdRepoEntitlement.MUST_CHANGE_PASSWORD));
 
-    @Resource(name = "adminUser")
-    protected String adminUser;
-
-    @Resource(name = "anonymousUser")
-    protected String anonymousUser;
+    @Autowired
+    protected SecurityProperties securityProperties;
 
     @Autowired
     protected RealmDAO realmDAO;
@@ -404,9 +400,9 @@
     public Set<SyncopeGrantedAuthority> getAuthorities(final String username, final String delegationKey) {
         Set<SyncopeGrantedAuthority> authorities;
 
-        if (anonymousUser.equals(username)) {
+        if (securityProperties.getAnonymousUser().equals(username)) {
             authorities = ANONYMOUS_AUTHORITIES;
-        } else if (adminUser.equals(username)) {
+        } else if (securityProperties.getAdminUser().equals(username)) {
             authorities = getAdminAuthorities();
         } else if (delegationKey != null) {
             Delegation delegation = Optional.ofNullable(delegationDAO.find(delegationKey)).
@@ -432,14 +428,14 @@
         String username;
         Set<SyncopeGrantedAuthority> authorities;
 
-        if (adminUser.equals(authentication.getClaims().getSubject())) {
+        if (securityProperties.getAdminUser().equals(authentication.getClaims().getSubject())) {
             AccessToken accessToken = accessTokenDAO.find(authentication.getClaims().getJWTID());
             if (accessToken == null) {
                 throw new AuthenticationCredentialsNotFoundException(
                         "Could not find an Access Token for JWT " + authentication.getClaims().getJWTID());
             }
 
-            username = adminUser;
+            username = securityProperties.getAdminUser();
             authorities = getAdminAuthorities();
         } else {
             JWTSSOProvider jwtSSOProvider = getJWTSSOProvider(authentication.getClaims().getIssuer());
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java
index cfc0044..52db5fb 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java
@@ -24,7 +24,6 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.Base64;
 import java.util.Map;
-import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -33,8 +32,8 @@
 import javax.crypto.spec.SecretKeySpec;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.PropertyUtils;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.jasypt.commons.CommonUtils;
 import org.jasypt.digest.StandardStringDigester;
 import org.slf4j.Logger;
@@ -49,85 +48,8 @@
 
     private static final String DEFAULT_SECRET_KEY = "1abcdefghilmnopqrstuvz2!";
 
-    /**
-     * Default value for salted {@link StandardStringDigester#setIterations(int)}.
-     */
-    private static final int DEFAULT_SALT_ITERATIONS = 1;
-
-    /**
-     * Default value for {@link StandardStringDigester#setSaltSizeBytes(int)}.
-     */
-    private static final int DEFAULT_SALT_SIZE_BYTES = 8;
-
-    /**
-     * Default value for {@link StandardStringDigester#setInvertPositionOfPlainSaltInEncryptionResults(boolean)}.
-     */
-    private static final boolean DEFAULT_IPOPSIER = true;
-
-    /**
-     * Default value for salted {@link StandardStringDigester#setInvertPositionOfSaltInMessageBeforeDigesting(boolean)}.
-     */
-    private static final boolean DEFAULT_IPOSIMBD = true;
-
-    /**
-     * Default value for salted {@link StandardStringDigester#setUseLenientSaltSizeCheck(boolean)}.
-     */
-    private static final boolean DEFAULT_ULSSC = true;
-
-    private static String SECRET_KEY;
-
-    private static Integer SALT_ITERATIONS;
-
-    private static Integer SALT_SIZE_BYTES;
-
-    private static Boolean IPOPSIER;
-
-    private static Boolean IPOSIMBD;
-
-    private static Boolean ULSSC;
-
-    static {
-        try {
-            Properties props = PropertyUtils.read(Encryptor.class, "security.properties", "conf.directory");
-
-            SECRET_KEY = props.getProperty("secretKey");
-            SALT_ITERATIONS = Integer.valueOf(props.getProperty("digester.saltIterations"));
-            SALT_SIZE_BYTES = Integer.valueOf(props.getProperty("digester.saltSizeBytes"));
-            IPOPSIER = Boolean.valueOf(props.getProperty("digester.invertPositionOfPlainSaltInEncryptionResults"));
-            IPOSIMBD = Boolean.valueOf(props.getProperty("digester.invertPositionOfSaltInMessageBeforeDigesting"));
-            ULSSC = Boolean.valueOf(props.getProperty("digester.useLenientSaltSizeCheck"));
-        } catch (Exception e) {
-            LOG.error("Could not read security parameters", e);
-        }
-
-        if (SECRET_KEY == null) {
-            SECRET_KEY = DEFAULT_SECRET_KEY;
-            LOG.debug("secretKey not found, reverting to default");
-        }
-        if (SALT_ITERATIONS == null) {
-            SALT_ITERATIONS = DEFAULT_SALT_ITERATIONS;
-            LOG.debug("digester.saltIterations not found, reverting to default");
-        }
-        if (SALT_SIZE_BYTES == null) {
-            SALT_SIZE_BYTES = DEFAULT_SALT_SIZE_BYTES;
-            LOG.debug("digester.saltSizeBytes not found, reverting to default");
-        }
-        if (IPOPSIER == null) {
-            IPOPSIER = DEFAULT_IPOPSIER;
-            LOG.debug("digester.invertPositionOfPlainSaltInEncryptionResults not found, reverting to default");
-        }
-        if (IPOSIMBD == null) {
-            IPOSIMBD = DEFAULT_IPOSIMBD;
-            LOG.debug("digester.invertPositionOfSaltInMessageBeforeDigesting not found, reverting to default");
-        }
-        if (ULSSC == null) {
-            ULSSC = DEFAULT_ULSSC;
-            LOG.debug("digester.useLenientSaltSizeCheck not found, reverting to default");
-        }
-    }
-
     public static Encryptor getInstance() {
-        return getInstance(SECRET_KEY);
+        return getInstance(null);
     }
 
     public static Encryptor getInstance(final String secretKey) {
@@ -233,13 +155,19 @@
             digester = new StandardStringDigester();
 
             if (cipherAlgorithm.getAlgorithm().startsWith("S-")) {
+                SecurityProperties securityProperties =
+                        ApplicationContextProvider.getApplicationContext().getBean(SecurityProperties.class);
+
                 // Salted ...
                 digester.setAlgorithm(cipherAlgorithm.getAlgorithm().replaceFirst("S\\-", ""));
-                digester.setIterations(SALT_ITERATIONS);
-                digester.setSaltSizeBytes(SALT_SIZE_BYTES);
-                digester.setInvertPositionOfPlainSaltInEncryptionResults(IPOPSIER);
-                digester.setInvertPositionOfSaltInMessageBeforeDigesting(IPOSIMBD);
-                digester.setUseLenientSaltSizeCheck(ULSSC);
+                digester.setIterations(securityProperties.getDigester().getSaltIterations());
+                digester.setSaltSizeBytes(securityProperties.getDigester().getSaltSizeBytes());
+                digester.setInvertPositionOfPlainSaltInEncryptionResults(
+                        securityProperties.getDigester().isInvertPositionOfPlainSaltInEncryptionResults());
+                digester.setInvertPositionOfSaltInMessageBeforeDigesting(
+                        securityProperties.getDigester().isInvertPositionOfSaltInMessageBeforeDigesting());
+                digester.setUseLenientSaltSizeCheck(
+                        securityProperties.getDigester().isUseLenientSaltSizeCheck());
             } else {
                 // Not salted ...
                 digester.setAlgorithm(cipherAlgorithm.getAlgorithm());
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
index 1c71c05..3fa043c 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
@@ -21,64 +21,89 @@
 import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.KeyLengthException;
+import java.lang.reflect.InvocationTargetException;
 import java.security.NoSuchAlgorithmException;
 import java.security.spec.InvalidKeySpecException;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.spring.security.jws.AccessTokenJWSSigner;
 import org.apache.syncope.core.spring.security.jws.AccessTokenJWSVerifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.EnvironmentAware;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.env.Environment;
 import org.springframework.security.config.core.GrantedAuthorityDefaults;
 
-@PropertySource("classpath:security.properties")
-@PropertySource(value = "file:${conf.directory}/security.properties", ignoreResourceNotFound = true)
+@EnableConfigurationProperties(SecurityProperties.class)
 @Configuration
-public class SecurityContext implements EnvironmentAware {
+public class SecurityContext {
 
-    private Environment env;
+    private static final Logger LOG = LoggerFactory.getLogger(SecurityContext.class);
 
-    @Override
-    public void setEnvironment(final Environment env) {
-        this.env = env;
-    }
+    @Autowired
+    private SecurityProperties props;
 
     @Bean
     public String adminUser() {
-        return env.getProperty("adminUser");
+        return props.getAdminUser();
     }
 
     @Bean
     public String adminPassword() {
-        return env.getProperty("adminPassword");
+        return props.getAdminPassword();
     }
 
     @Bean
-    public String adminPasswordAlgorithm() {
-        return env.getProperty("adminPasswordAlgorithm");
+    public CipherAlgorithm adminPasswordAlgorithm() {
+        return props.getAdminPasswordAlgorithm();
     }
 
     @Bean
     public String anonymousUser() {
-        return env.getProperty("anonymousUser");
+        return props.getAnonymousUser();
     }
 
     @Bean
     public String anonymousKey() {
-        return env.getProperty("anonymousKey");
+        return props.getAnonymousKey();
     }
 
     @Bean
     public String jwtIssuer() {
-        return env.getProperty("jwtIssuer");
+        return props.getJwtIssuer();
+    }
+
+    @Bean
+    public JWSAlgorithm jwsAlgorithm() {
+        return JWSAlgorithm.parse(props.getJwsAlgorithm().toUpperCase());
     }
 
     @Bean
     public String jwsKey() {
-        return env.getProperty("jwsKey");
+        String jwsKey = props.getJwsKey();
+        if (jwsKey == null) {
+            throw new IllegalArgumentException("No JWS key provided");
+        }
+
+        JWSAlgorithm jwsAlgorithm = jwsAlgorithm();
+        if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {
+            int minLength = jwsAlgorithm.equals(JWSAlgorithm.HS256)
+                    ? 256 / 8
+                    : jwsAlgorithm.equals(JWSAlgorithm.HS384)
+                    ? 384 / 8
+                    : 512 / 8;
+            if (jwsKey.length() < minLength) {
+                jwsKey = SecureRandomUtils.generateRandomPassword(minLength);
+                props.setJwsKey(jwsKey);
+                LOG.warn("The configured key for {} must be at least {} bits, generating random: {}",
+                        jwsAlgorithm, minLength * 8, jwsKey);
+            }
+        }
+
+        return jwsKey;
     }
 
     @ConditionalOnMissingBean
@@ -92,9 +117,7 @@
     public AccessTokenJWSVerifier accessTokenJWSVerifier()
             throws JOSEException, NoSuchAlgorithmException, InvalidKeySpecException {
 
-        return new AccessTokenJWSVerifier(
-                JWSAlgorithm.parse(env.getProperty("jwsAlgorithm")),
-                jwsKey());
+        return new AccessTokenJWSVerifier(jwsAlgorithm(), jwsKey());
     }
 
     @ConditionalOnMissingBean
@@ -102,15 +125,14 @@
     public AccessTokenJWSSigner accessTokenJWSSigner()
             throws KeyLengthException, NoSuchAlgorithmException, InvalidKeySpecException {
 
-        return new AccessTokenJWSSigner(
-                JWSAlgorithm.parse(env.getProperty("jwsAlgorithm")),
-                jwsKey());
+        return new AccessTokenJWSSigner(jwsAlgorithm(), jwsKey());
     }
 
-    @ConditionalOnMissingBean
     @Bean
-    public PasswordGenerator passwordGenerator() {
-        return new DefaultPasswordGenerator();
+    public PasswordGenerator passwordGenerator() throws NoSuchMethodException,
+            InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+
+        return props.getPasswordGenerator().getDeclaredConstructor().newInstance();
     }
 
     @Bean
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java
new file mode 100644
index 0000000..7276e78
--- /dev/null
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java
@@ -0,0 +1,190 @@
+/*
+ * 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.spring.security;
+
+import com.nimbusds.jose.JWSAlgorithm;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("security")
+public class SecurityProperties {
+
+    public static class DigesterProperties {
+
+        private int saltIterations = 1;
+
+        private int saltSizeBytes = 8;
+
+        private boolean invertPositionOfPlainSaltInEncryptionResults = true;
+
+        private boolean invertPositionOfSaltInMessageBeforeDigesting = true;
+
+        private boolean useLenientSaltSizeCheck = true;
+
+        public int getSaltIterations() {
+            return saltIterations;
+        }
+
+        public void setSaltIterations(final int saltIterations) {
+            this.saltIterations = saltIterations;
+        }
+
+        public int getSaltSizeBytes() {
+            return saltSizeBytes;
+        }
+
+        public void setSaltSizeBytes(final int saltSizeBytes) {
+            this.saltSizeBytes = saltSizeBytes;
+        }
+
+        public boolean isInvertPositionOfPlainSaltInEncryptionResults() {
+            return invertPositionOfPlainSaltInEncryptionResults;
+        }
+
+        public void setInvertPositionOfPlainSaltInEncryptionResults(
+                final boolean invertPositionOfPlainSaltInEncryptionResults) {
+
+            this.invertPositionOfPlainSaltInEncryptionResults = invertPositionOfPlainSaltInEncryptionResults;
+        }
+
+        public boolean isInvertPositionOfSaltInMessageBeforeDigesting() {
+            return invertPositionOfSaltInMessageBeforeDigesting;
+        }
+
+        public void setInvertPositionOfSaltInMessageBeforeDigesting(
+                final boolean invertPositionOfSaltInMessageBeforeDigesting) {
+
+            this.invertPositionOfSaltInMessageBeforeDigesting = invertPositionOfSaltInMessageBeforeDigesting;
+        }
+
+        public boolean isUseLenientSaltSizeCheck() {
+            return useLenientSaltSizeCheck;
+        }
+
+        public void setUseLenientSaltSizeCheck(final boolean useLenientSaltSizeCheck) {
+            this.useLenientSaltSizeCheck = useLenientSaltSizeCheck;
+        }
+    }
+
+    private String adminUser;
+
+    private String adminPassword;
+
+    private CipherAlgorithm adminPasswordAlgorithm;
+
+    private String anonymousUser;
+
+    private String anonymousKey;
+
+    private String jwtIssuer = "ApacheSyncope";
+
+    private String jwsKey;
+
+    private String jwsAlgorithm = JWSAlgorithm.HS512.getName();
+
+    private String secretKey;
+
+    private Class<? extends PasswordGenerator> passwordGenerator = DefaultPasswordGenerator.class;
+
+    private final DigesterProperties digester = new DigesterProperties();
+
+    public String getAdminUser() {
+        return adminUser;
+    }
+
+    public void setAdminUser(final String adminUser) {
+        this.adminUser = adminUser;
+    }
+
+    public String getAdminPassword() {
+        return adminPassword;
+    }
+
+    public void setAdminPassword(final String adminPassword) {
+        this.adminPassword = adminPassword;
+    }
+
+    public CipherAlgorithm getAdminPasswordAlgorithm() {
+        return adminPasswordAlgorithm;
+    }
+
+    public void setAdminPasswordAlgorithm(final CipherAlgorithm adminPasswordAlgorithm) {
+        this.adminPasswordAlgorithm = adminPasswordAlgorithm;
+    }
+
+    public String getAnonymousUser() {
+        return anonymousUser;
+    }
+
+    public void setAnonymousUser(final String anonymousUser) {
+        this.anonymousUser = anonymousUser;
+    }
+
+    public String getAnonymousKey() {
+        return anonymousKey;
+    }
+
+    public void setAnonymousKey(final String anonymousKey) {
+        this.anonymousKey = anonymousKey;
+    }
+
+    public String getJwtIssuer() {
+        return jwtIssuer;
+    }
+
+    public void setJwtIssuer(final String jwtIssuer) {
+        this.jwtIssuer = jwtIssuer;
+    }
+
+    public String getJwsKey() {
+        return jwsKey;
+    }
+
+    public void setJwsKey(final String jwsKey) {
+        this.jwsKey = jwsKey;
+    }
+
+    public String getJwsAlgorithm() {
+        return jwsAlgorithm;
+    }
+
+    public void setJwsAlgorithm(final String jwsAlgorithm) {
+        this.jwsAlgorithm = jwsAlgorithm;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(final String secretKey) {
+        this.secretKey = secretKey;
+    }
+
+    public Class<? extends PasswordGenerator> getPasswordGenerator() {
+        return passwordGenerator;
+    }
+
+    public void setPasswordGenerator(final Class<? extends PasswordGenerator> passwordGenerator) {
+        this.passwordGenerator = passwordGenerator;
+    }
+
+    public DigesterProperties getDigester() {
+        return digester;
+    }
+}
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
index 5e27fa1..ebf5d81 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProvider.java
@@ -26,7 +26,6 @@
 import com.nimbusds.jose.util.Base64URL;
 import com.nimbusds.jwt.JWTClaimsSet;
 import java.util.Set;
-import javax.annotation.Resource;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
@@ -49,8 +48,8 @@
 
     private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
 
-    @Resource(name = "jwtIssuer")
-    private String jwtIssuer;
+    @Autowired
+    private SecurityProperties securityProperties;
 
     @Autowired
     private AccessTokenJWSVerifier delegate;
@@ -63,7 +62,7 @@
 
     @Override
     public String getIssuer() {
-        return jwtIssuer;
+        return securityProperties.getJwtIssuer();
     }
 
     @Override
@@ -96,8 +95,8 @@
                 try {
                     authorities = POJOHelper.deserialize(
                             ENCRYPTOR.decode(new String(accessToken.getAuthorities()), CipherAlgorithm.AES),
-                        new TypeReference<>() {
-                        });
+                            new TypeReference<>() {
+                    });
                 } catch (Throwable t) {
                     LOG.error("Could not read stored authorities", t);
                 }
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java
index a8728fd..5875e2b 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.spring.security;
 
 import java.util.concurrent.atomic.AtomicReference;
-import javax.annotation.Resource;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
@@ -27,7 +26,6 @@
 import org.apache.syncope.common.keymaster.client.api.model.Domain;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
@@ -59,41 +57,8 @@
     @Autowired
     protected DefaultCredentialChecker credentialChecker;
 
-    @Resource(name = "adminUser")
-    protected String adminUser;
-
-    @Resource(name = "adminPassword")
-    protected String adminPassword;
-
-    @Resource(name = "adminPasswordAlgorithm")
-    protected String adminPasswordAlgorithm;
-
-    @Resource(name = "anonymousUser")
-    protected String anonymousUser;
-
-    @Resource(name = "anonymousKey")
-    protected String anonymousKey;
-
-    /**
-     * @param adminPassword the adminPassword to set
-     */
-    public void setAdminPassword(final String adminPassword) {
-        this.adminPassword = adminPassword;
-    }
-
-    /**
-     * @param adminPasswordAlgorithm the adminPasswordAlgorithm to set
-     */
-    public void setAdminPasswordAlgorithm(final String adminPasswordAlgorithm) {
-        this.adminPasswordAlgorithm = adminPasswordAlgorithm;
-    }
-
-    /**
-     * @param anonymousKey the anonymousKey to set
-     */
-    public void setAnonymousKey(final String anonymousKey) {
-        this.anonymousKey = anonymousKey;
-    }
+    @Autowired
+    protected SecurityProperties securityProperties;
 
     @Override
     public Authentication authenticate(final Authentication authentication) {
@@ -116,18 +81,18 @@
         Boolean authenticated;
         AtomicReference<String> delegationKey = new AtomicReference<>();
 
-        if (anonymousUser.equals(authentication.getName())) {
-            username.set(anonymousUser);
+        if (securityProperties.getAnonymousUser().equals(authentication.getName())) {
+            username.set(securityProperties.getAnonymousUser());
             credentialChecker.checkIsDefaultAnonymousKeyInUse();
-            authenticated = authentication.getCredentials().toString().equals(anonymousKey);
-        } else if (adminUser.equals(authentication.getName())) {
-            username.set(adminUser);
+            authenticated = authentication.getCredentials().toString().equals(securityProperties.getAnonymousKey());
+        } else if (securityProperties.getAdminUser().equals(authentication.getName())) {
+            username.set(securityProperties.getAdminUser());
             if (SyncopeConstants.MASTER_DOMAIN.equals(domain.getKey())) {
                 credentialChecker.checkIsDefaultAdminPasswordInUse();
                 authenticated = ENCRYPTOR.verify(
                         authentication.getCredentials().toString(),
-                        CipherAlgorithm.valueOf(adminPasswordAlgorithm),
-                        adminPassword);
+                        securityProperties.getAdminPasswordAlgorithm(),
+                        securityProperties.getAdminPassword());
             } else {
                 authenticated = ENCRYPTOR.verify(
                         authentication.getCredentials().toString(),
@@ -144,7 +109,9 @@
                 if (!authenticated) {
                     AuthContextUtils.callAsAdmin(domain.getKey(), () -> {
                         provisioningManager.internalSuspend(
-                                authResult.getLeft().getKey(), adminUser, "Failed authentication");
+                                authResult.getLeft().getKey(),
+                                securityProperties.getAdminUser(),
+                                "Failed authentication");
                         return null;
                     });
                 }
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java
index f421dd6..0de9625 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.spring.security;
 
-import javax.annotation.Resource;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.ApplicationContext;
@@ -42,9 +41,9 @@
 @EnableGlobalMethodSecurity(prePostEnabled = true)
 public class WebSecurityContext extends WebSecurityConfigurerAdapter {
 
-    @Resource(name = "anonymousUser")
-    private String anonymousUser;
-
+    @Autowired
+    private SecurityProperties securityProperties;
+    
     @Autowired
     private ApplicationContext ctx;
 
@@ -106,7 +105,7 @@
                 antMatchers("/**").permitAll().and().
                 sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
                 securityContext().securityContextRepository(new NullSecurityContextRepository()).and().
-                anonymous().principal(anonymousUser).and().
+                anonymous().principal(securityProperties.getAnonymousUser()).and().
                 httpBasic().authenticationEntryPoint(basicAuthenticationEntryPoint).
                 authenticationDetailsSource(authenticationDetailsSource).and().
                 exceptionHandling().accessDeniedHandler(accessDeniedHandler()).and().
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSigner.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSigner.java
index b6f7da7..029e555 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSigner.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSigner.java
@@ -44,13 +44,10 @@
     public AccessTokenJWSSigner(final JWSAlgorithm jwsAlgorithm, final String jwsKey)
             throws KeyLengthException, NoSuchAlgorithmException, InvalidKeySpecException {
 
-        if (jwsAlgorithm == null) {
-            throw new IllegalArgumentException("An instance of " + JWSAlgorithm.class + " is required");
-        }
         this.jwsAlgorithm = jwsAlgorithm;
 
         if (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm)) {
-            if (jwsKey == null || jwsKey.indexOf(':') == -1) {
+            if (jwsKey.indexOf(':') == -1) {
                 throw new IllegalArgumentException("A key pair is required, in the 'private:public' format");
             }
 
@@ -59,10 +56,6 @@
                     Base64.getDecoder().decode(StringUtils.substringBefore(jwsKey, ":").getBytes()));
             delegate = new RSASSASigner(kf.generatePrivate(keySpecPKCS8));
         } else if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {
-            if (jwsKey == null) {
-                throw new IllegalArgumentException("A shared key is required");
-            }
-
             delegate = new MACSigner(jwsKey);
         } else {
             throw new IllegalArgumentException("Unsupported JWS algorithm: " + jwsAlgorithm.getName());
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifier.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifier.java
index 6fa2275..1613245 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifier.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifier.java
@@ -42,12 +42,8 @@
     public AccessTokenJWSVerifier(final JWSAlgorithm jwsAlgorithm, final String jwsKey)
             throws JOSEException, NoSuchAlgorithmException, InvalidKeySpecException {
 
-        if (jwsAlgorithm == null) {
-            throw new IllegalArgumentException("An instance of " + JWSAlgorithm.class + " is required");
-        }
-
         if (JWSAlgorithm.Family.RSA.contains(jwsAlgorithm)) {
-            if (jwsKey == null || jwsKey.indexOf(':') == -1) {
+            if (jwsKey.indexOf(':') == -1) {
                 throw new IllegalArgumentException("A key pair is required, in the 'private:public' format");
             }
 
@@ -56,10 +52,6 @@
                     Base64.getDecoder().decode(StringUtils.substringAfter(jwsKey, ":").getBytes()));
             delegate = new RSASSAVerifier((RSAPublicKey) kf.generatePublic(keySpecX509));
         } else if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {
-            if (jwsKey == null) {
-                throw new IllegalArgumentException("A shared key is required");
-            }
-
             delegate = new MACVerifier(jwsKey);
         } else {
             throw new IllegalArgumentException("Unsupported JWS algorithm: " + jwsAlgorithm.getName());
diff --git a/core/spring/src/main/resources/security.properties b/core/spring/src/main/resources/security.properties
deleted file mode 100644
index a68eed1..0000000
--- a/core/spring/src/main/resources/security.properties
+++ /dev/null
@@ -1,39 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-conf.directory=${conf.directory}
-
-adminUser=${adminUser}
-adminPassword=${adminPassword}
-adminPasswordAlgorithm=SSHA256
-
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-
-secretKey=${secretKey}
-
-jwtIssuer=ApacheSyncope
-jwsAlgorithm=HS512
-jwsKey=${jwsKey}
-
-# default for LDAP / RFC2307 SSHA
-digester.saltIterations=1
-digester.saltSizeBytes=8
-digester.invertPositionOfPlainSaltInEncryptionResults=true
-digester.invertPositionOfSaltInMessageBeforeDigesting=true
-digester.useLenientSaltSizeCheck=true
-
-passwordGenerator=org.apache.syncope.core.spring.security.DefaultPasswordGenerator
diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java
index 62d43ce..4e32e2f 100644
--- a/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java
+++ b/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java
@@ -34,7 +34,7 @@
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
 
-@SpringJUnitConfig(locations = { "classpath:springTest.xml" })
+@SpringJUnitConfig(classes = { SpringTestConfiguration.class })
 public class ImplementationManagerTest {
 
     private static DefaultPasswordRuleConf createBaseDefaultPasswordRuleConf() {
@@ -98,7 +98,7 @@
 
             assertTrue(
                     errorMessages.isEmpty(),
-                () -> errorMessages.stream().collect(Collectors.joining(System.lineSeparator())));
+                    () -> errorMessages.stream().collect(Collectors.joining(System.lineSeparator())));
         });
     }
 }
diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/SpringTestConfiguration.java b/core/spring/src/test/java/org/apache/syncope/core/spring/SpringTestConfiguration.java
new file mode 100644
index 0000000..6947097
--- /dev/null
+++ b/core/spring/src/test/java/org/apache/syncope/core/spring/SpringTestConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * 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.spring;
+
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.spring.security.DummyImplementationLookup;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+@Configuration
+public class SpringTestConfiguration {
+
+    @Bean
+    public ApplicationContextProvider applicationContextProvider() {
+        return new ApplicationContextProvider();
+    }
+
+    @Primary
+    @Bean
+    public ImplementationLookup implementationLookup() {
+        return new DummyImplementationLookup();
+    }
+}
diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/EncryptorTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/EncryptorTest.java
index e7540d5..8f4cf5b 100644
--- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/EncryptorTest.java
+++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/EncryptorTest.java
@@ -24,14 +24,22 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 public class EncryptorTest {
 
-    private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
-
     private static final String PASSWORD_VALUE = "password";
 
+    private static Encryptor ENCRYPTOR;
+
+    @BeforeAll
+    public static void setUp() {
+        ApplicationContextProvider.getBeanFactory().registerSingleton("securityProperties", new SecurityProperties());
+        ENCRYPTOR = Encryptor.getInstance();
+    }
+
     @Test
     public void encoder() throws Exception {
         for (CipherAlgorithm cipherAlgorithm : CipherAlgorithm.values()) {
diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java
index f2eb68a..0cf68d9 100644
--- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java
+++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java
@@ -29,12 +29,13 @@
 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.spring.SpringTestConfiguration;
 import org.apache.syncope.core.spring.policy.InvalidPasswordRuleConf;
 import org.apache.syncope.core.spring.policy.PolicyPattern;
 import org.junit.jupiter.api.Test;
 import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
 
-@SpringJUnitConfig(locations = { "classpath:springTest.xml" })
+@SpringJUnitConfig(classes = { SpringTestConfiguration.class })
 public class PasswordGeneratorTest {
 
     private final DefaultPasswordGenerator passwordGenerator = new DefaultPasswordGenerator();
diff --git a/core/spring/src/test/resources/springTest.xml b/core/spring/src/test/resources/springTest.xml
deleted file mode 100644
index 76ae511..0000000
--- a/core/spring/src/test/resources/springTest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-                           http://www.springframework.org/schema/beans/spring-beans.xsd
-                           http://www.springframework.org/schema/context
-                           http://www.springframework.org/schema/context/spring-context.xsd">
-
-  <bean class="org.apache.syncope.core.spring.ApplicationContextProvider"/>
-  <bean class="org.apache.syncope.core.spring.security.DummyImplementationLookup"/>
-
-</beans>
diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java
index 83dcb4b..c5ec551 100644
--- a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java
+++ b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.core.starter;
 
-import java.io.IOException;
+import java.util.Map;
 import org.apache.cxf.spring.boot.autoconfigure.openapi.OpenApiAutoConfiguration;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop;
@@ -26,7 +26,6 @@
 import org.apache.syncope.core.starter.actuate.ExternalResourcesHealthIndicator;
 import org.apache.syncope.core.starter.actuate.SyncopeCoreInfoContributor;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.SpringApplication;
 import org.springframework.boot.actuate.mail.MailHealthIndicator;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -37,9 +36,9 @@
 import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
 import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
 import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
 import org.springframework.mail.javamail.JavaMailSender;
 import org.springframework.mail.javamail.JavaMailSenderImpl;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
@@ -56,21 +55,19 @@
 public class SyncopeCoreApplication extends SpringBootServletInitializer {
 
     public static void main(final String[] args) {
-        SpringApplication.run(SyncopeCoreApplication.class, args);
-    }
-
-    @ConditionalOnMissingBean(name = "propertySourcesPlaceholderConfigurer")
-    @Bean
-    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
-        PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
-        pspc.setIgnoreResourceNotFound(true);
-        pspc.setIgnoreUnresolvablePlaceholders(true);
-        return pspc;
+        new SpringApplicationBuilder(SyncopeCoreApplication.class).
+                properties("spring.config.name:core").
+                build().run(args);
     }
 
     @Autowired
     protected JavaMailSender mailSender;
 
+    @Override
+    protected SpringApplicationBuilder configure(final SpringApplicationBuilder builder) {
+        return builder.properties(Map.of("spring.config.name", "core")).sources(SyncopeCoreApplication.class);
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public SyncopeCoreInfoContributor syncopeCoreInfoContributor() {
diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/SyncopeCoreInfoContributor.java b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/SyncopeCoreInfoContributor.java
index 467c302..d77e26c 100644
--- a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/SyncopeCoreInfoContributor.java
+++ b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/SyncopeCoreInfoContributor.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.core.starter.actuate;
 
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import java.lang.management.ManagementFactory;
 import java.lang.management.OperatingSystemMXBean;
 import java.lang.management.RuntimeMXBean;
@@ -42,6 +40,7 @@
 import org.apache.syncope.common.lib.types.EntitlementsHolder;
 import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
 import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.core.logic.LogicProperties;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
@@ -64,6 +63,7 @@
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
+import org.apache.syncope.core.persistence.jpa.PersistenceProperties;
 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
@@ -72,11 +72,14 @@
 import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.provisioning.java.ProvisioningProperties;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.PasswordGenerator;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
+import org.apache.syncope.core.workflow.java.WorkflowProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.aop.support.AopUtils;
@@ -93,8 +96,6 @@
 
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeCoreInfoContributor.class);
 
-    protected static final ObjectMapper MAPPER = new ObjectMapper().findAndRegisterModules();
-
     protected static final Object MONITOR = new Object();
 
     protected static PlatformInfo PLATFORM_INFO;
@@ -166,6 +167,21 @@
     }
 
     @Autowired
+    protected SecurityProperties securityProperties;
+
+    @Autowired
+    protected PersistenceProperties persistenceProperties;
+
+    @Autowired
+    protected ProvisioningProperties provisioningProperties;
+
+    @Autowired
+    protected LogicProperties logicProperties;
+
+    @Autowired
+    protected WorkflowProperties workflowProperties;
+
+    @Autowired
     protected AnyTypeDAO anyTypeDAO;
 
     @Autowired
@@ -286,10 +302,8 @@
                 PLATFORM_INFO.setKeymasterConfParamOps(AopUtils.getTargetClass(confParamOps).getName());
                 PLATFORM_INFO.setKeymasterServiceOps(AopUtils.getTargetClass(serviceOps).getName());
 
-                if (bundleManager.getLocations() != null) {
-                    PLATFORM_INFO.getConnIdLocations().addAll(bundleManager.getLocations().stream().
-                            map(URI::toASCIIString).collect(Collectors.toList()));
-                }
+                PLATFORM_INFO.getConnIdLocations().addAll(bundleManager.getLocations().stream().
+                        map(URI::toASCIIString).collect(Collectors.toList()));
 
                 PLATFORM_INFO.getWorkflowInfo().
                         setAnyObjectWorkflowAdapter(AopUtils.getTargetClass(awfAdapter).getName());
@@ -446,20 +460,17 @@
     @Override
     public void contribute(final Info.Builder builder) {
         buildPlatform();
-        builder.withDetail(
-                "platform",
-                MAPPER.convertValue(PLATFORM_INFO, new TypeReference<Map<String, Object>>() {
-                }));
+        builder.withDetail("platform", PLATFORM_INFO);
 
-        builder.withDetail(
-                "numbers",
-                MAPPER.convertValue(buildNumbers(), new TypeReference<Map<String, Object>>() {
-                }));
+        builder.withDetail("numbers", buildNumbers());
 
         buildSystem();
-        builder.withDetail(
-                "system",
-                MAPPER.convertValue(SYSTEM_INFO, new TypeReference<Map<String, Object>>() {
-                }));
+        builder.withDetail("system", SYSTEM_INFO);
+
+        builder.withDetail("securityProperties", securityProperties);
+        builder.withDetail("persistenceProperties", persistenceProperties);
+        builder.withDetail("provisioningProperties", provisioningProperties);
+        builder.withDetail("logicProperties", logicProperties);
+        builder.withDetail("workflowProperties", workflowProperties);
     }
 }
diff --git a/core/starter/src/main/resources/application.properties b/core/starter/src/main/resources/application.properties
deleted file mode 100644
index 0363a8d..0000000
--- a/core/starter/src/main/resources/application.properties
+++ /dev/null
@@ -1,33 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} Core
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.servlet.encoding.charset=UTF-8
-server.servlet.encoding.enabled=true
-server.servlet.encoding.force=true
-
-conf.directory=${conf.directory}
-
-server.servlet.contextPath=/syncope
-cxf.path=/rest
-
-management.endpoints.web.exposure.include=health,info,loggers
-management.endpoint.health.show-details=ALWAYS
-
-service.discovery.address=http://localhost:8080/syncope/rest/
diff --git a/core/starter/src/main/resources/core.properties b/core/starter/src/main/resources/core.properties
new file mode 100644
index 0000000..27308ae
--- /dev/null
+++ b/core/starter/src/main/resources/core.properties
@@ -0,0 +1,143 @@
+# 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.
+spring.application.name=Apache Syncope ${syncope.version} Core
+spring.groovy.template.check-template-location=false
+spring.main.banner-mode=log
+
+version=${syncope.version}
+
+server.servlet.encoding.charset=UTF-8
+server.servlet.encoding.enabled=true
+server.servlet.encoding.force=true
+
+conf.directory=${conf.directory}
+
+server.servlet.contextPath=/syncope
+cxf.path=/rest
+
+management.endpoints.web.exposure.include=health,info,loggers
+management.endpoint.health.show-details=ALWAYS
+
+service.discovery.address=http://localhost:8080/syncope/rest/
+
+###############
+# Persistence #
+###############
+
+persistence.entityFactory=org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory
+persistence.plainSchemaDao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainSchemaDAO
+persistence.plainAttrDao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrDAO
+persistence.plainAttrValueDao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO
+persistence.searchCondVisitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
+persistence.userDao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
+persistence.groupDao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
+persistence.anyObjectDao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
+persistence.auditConfDao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditConfDAO
+persistence.remoteCommitProvider=sjvm
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.postgresql.Driver
+persistence.domain[0].jdbcURL=jdbc:postgresql://localhost:5432/syncope
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
+persistence.domain[0].auditSql=audit.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+################
+# Provisioning #
+################
+
+provisioning.asyncConnectorFacadeExecutor.corePoolSize=5
+provisioning.asyncConnectorFacadeExecutor.maxPoolSize=25
+provisioning.asyncConnectorFacadeExecutor.queueCapacity=100
+
+provisioning.propagationTaskExecutorAsyncExecutor.corePoolSize=5
+provisioning.propagationTaskExecutorAsyncExecutor.maxPoolSize=25
+provisioning.propagationTaskExecutorAsyncExecutor.queueCapacity=100
+
+provisioning.propagationManager=org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationManager
+provisioning.propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
+provisioning.userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
+provisioning.groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
+provisioning.anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
+provisioning.virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
+provisioning.virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
+provisioning.notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
+provisioning.auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
+
+provisioning.connIdLocation=${connid.location}
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
+provisioning.quartz.sql=tables_postgres.sql
+provisioning.quartz.disableInstance=false
+
+provisioning.smtp.host=none.syncope.apache.org
+provisioning.smtp.port=25
+provisioning.smtp.username=
+provisioning.smtp.password=
+provisioning.smtp.protocol=smtp
+provisioning.smtp.defaultEncoding=UTF-8
+provisioning.smtp.debug=false
+# Add more properties starting with mail.smtp.* from
+# https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties
+provisioning.smtp.javamailProperties[mail.smtp.connectiontimeout]=3000
+provisioning.smtp.javamailProperties[mail.smtp.starttls.enable]=false
+
+############
+# Security #
+############
+
+security.adminUser=${adminUser}
+security.adminPassword=${adminPassword}
+security.adminPasswordAlgorithm=SSHA256
+
+security.anonymousUser=${anonymousUser}
+security.anonymousKey=${anonymousKey}
+
+security.jwtIssuer=ApacheSyncope
+security.jwsAlgorithm=HS512
+security.jwsKey=${jwsKey}
+
+security.secretKey=${secretKey}
+
+# default for LDAP / RFC2307 SSHA
+security.digester.saltIterations=1
+security.digester.saltSizeBytes=8
+security.digester.invertPositionOfPlainSaltInEncryptionResults=true
+security.digester.invertPositionOfSaltInMessageBeforeDigesting=true
+security.digester.useLenientSaltSizeCheck=true
+
+security.passwordGenerator=org.apache.syncope.core.spring.security.DefaultPasswordGenerator
+
+############
+# Workflow #
+############
+
+workflow.uwfAdapter=org.apache.syncope.core.workflow.java.DefaultUserWorkflowAdapter
+workflow.gwfAdapter=org.apache.syncope.core.workflow.java.DefaultGroupWorkflowAdapter
+workflow.awfAdapter=org.apache.syncope.core.workflow.java.DefaultAnyObjectWorkflowAdapter
+
+#########
+# Logic #
+#########
+
+logic.invocationHandler=org.apache.syncope.core.logic.LogicInvocationHandler
+logic.implementationLookup=org.apache.syncope.core.logic.init.ClassPathScanImplementationLookup
+logic.enableJDBCAuditAppender=true
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/WorkflowContext.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/WorkflowContext.java
index d215a18..4003615 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/WorkflowContext.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/WorkflowContext.java
@@ -22,48 +22,36 @@
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
-import org.springframework.context.EnvironmentAware;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
-import org.springframework.core.env.Environment;
 
-@PropertySource("classpath:workflow.properties")
-@PropertySource(value = "file:${conf.directory}/workflow.properties", ignoreResourceNotFound = true)
+@EnableConfigurationProperties(WorkflowProperties.class)
 @Configuration
-public class WorkflowContext implements EnvironmentAware {
+public class WorkflowContext {
 
-    private Environment env;
+    @Autowired
+    private WorkflowProperties props;
 
-    @Override
-    public void setEnvironment(final Environment env) {
-        this.env = env;
+    @Bean
+    public UserWorkflowAdapter uwfAdapter() throws NoSuchMethodException, InstantiationException,
+            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+
+        return props.getUwfAdapter().getDeclaredConstructor().newInstance();
     }
 
     @Bean
-    public UserWorkflowAdapter uwfAdapter()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
-            NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
+    public GroupWorkflowAdapter gwfAdapter() throws NoSuchMethodException, InstantiationException,
+            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (UserWorkflowAdapter) Class.forName(env.getProperty("uwfAdapter")).
-                getDeclaredConstructor().newInstance();
+        return props.getGwfAdapter().getDeclaredConstructor().newInstance();
     }
 
     @Bean
-    public GroupWorkflowAdapter gwfAdapter()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
-            NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
+    public AnyObjectWorkflowAdapter awfAdapter() throws NoSuchMethodException, InstantiationException,
+            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
-        return (GroupWorkflowAdapter) Class.forName(env.getProperty("gwfAdapter")).
-                getDeclaredConstructor().newInstance();
-    }
-
-    @Bean
-    public AnyObjectWorkflowAdapter awfAdapter()
-            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
-            NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
-
-        return (AnyObjectWorkflowAdapter) Class.forName(env.getProperty("awfAdapter")).
-                getDeclaredConstructor().newInstance();
+        return props.getAwfAdapter().getDeclaredConstructor().newInstance();
     }
 }
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/WorkflowProperties.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/WorkflowProperties.java
new file mode 100644
index 0000000..c74b84a
--- /dev/null
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/WorkflowProperties.java
@@ -0,0 +1,58 @@
+/*
+ * 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.workflow.java;
+
+import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
+import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
+import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("workflow")
+public class WorkflowProperties {
+
+    private Class<? extends UserWorkflowAdapter> uwfAdapter = DefaultUserWorkflowAdapter.class;
+
+    private Class<? extends GroupWorkflowAdapter> gwfAdapter = DefaultGroupWorkflowAdapter.class;
+
+    private Class<? extends AnyObjectWorkflowAdapter> awfAdapter = DefaultAnyObjectWorkflowAdapter.class;
+
+    public Class<? extends UserWorkflowAdapter> getUwfAdapter() {
+        return uwfAdapter;
+    }
+
+    public void setUwfAdapter(final Class<? extends UserWorkflowAdapter> uwfAdapter) {
+        this.uwfAdapter = uwfAdapter;
+    }
+
+    public Class<? extends GroupWorkflowAdapter> getGwfAdapter() {
+        return gwfAdapter;
+    }
+
+    public void setGwfAdapter(final Class<? extends GroupWorkflowAdapter> gwfAdapter) {
+        this.gwfAdapter = gwfAdapter;
+    }
+
+    public Class<? extends AnyObjectWorkflowAdapter> getAwfAdapter() {
+        return awfAdapter;
+    }
+
+    public void setAwfAdapter(final Class<? extends AnyObjectWorkflowAdapter> awfAdapter) {
+        this.awfAdapter = awfAdapter;
+    }
+}
diff --git a/core/workflow-java/src/main/resources/workflow.properties b/core/workflow-java/src/main/resources/workflow.properties
deleted file mode 100644
index 8e5f662..0000000
--- a/core/workflow-java/src/main/resources/workflow.properties
+++ /dev/null
@@ -1,20 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-wf.directory=${conf.directory}
-uwfAdapter=org.apache.syncope.core.workflow.java.DefaultUserWorkflowAdapter
-gwfAdapter=org.apache.syncope.core.workflow.java.DefaultGroupWorkflowAdapter
-awfAdapter=org.apache.syncope.core.workflow.java.DefaultAnyObjectWorkflowAdapter
diff --git a/docker/console/pom.xml b/docker/console/pom.xml
index 40d8da8..a8786d7 100644
--- a/docker/console/pom.xml
+++ b/docker/console/pom.xml
@@ -55,8 +55,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
diff --git a/docker/console/src/main/resources/Dockerfile b/docker/console/src/main/resources/Dockerfile
index 2cb5219..154b4f2 100644
--- a/docker/console/src/main/resources/Dockerfile
+++ b/docker/console/src/main/resources/Dockerfile
@@ -31,6 +31,9 @@
 
 COPY syncope-docker-console-*war /opt/syncope/lib/syncope-console.war
 
+ENV SPRING_PROFILES_ACTIVE=docker
+ENV LOADER_PATH="/opt/syncope/conf,/opt/syncope/lib"
+
 COPY startup.sh /opt/syncope/bin
 RUN chmod 755 /opt/syncope/bin/startup.sh
 CMD ["/opt/syncope/bin/startup.sh"]
diff --git a/docker/console/src/main/resources/application.properties b/docker/console/src/main/resources/application.properties
deleted file mode 100644
index f50ca0c..0000000
--- a/docker/console/src/main/resources/application.properties
+++ /dev/null
@@ -1,32 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} Console
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.servlet.encoding.charset=UTF-8
-server.servlet.encoding.enabled=true
-server.servlet.encoding.force=true
-
-server.servlet.contextPath=/syncope-console
-
-management.endpoints.web.exposure.include=info,health,loggers
-management.endpoint.health.show-details=ALWAYS
-
-service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
-
-wicket.core.csrf.enabled=false
diff --git a/docker/console/src/main/resources/keymaster.properties b/docker/console/src/main/resources/console-docker.properties
similarity index 81%
rename from docker/console/src/main/resources/keymaster.properties
rename to docker/console/src/main/resources/console-docker.properties
index 14e8ca6..6891bea 100644
--- a/docker/console/src/main/resources/keymaster.properties
+++ b/docker/console/src/main/resources/console-docker.properties
@@ -17,3 +17,10 @@
 keymaster.address=${KEYMASTER_ADDRESS}
 keymaster.username=${KEYMASTER_USERNAME}
 keymaster.password=${KEYMASTER_PASSWORD}
+
+service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
+
+console.anonymousUser=${ANONYMOUS_USER:anonymous}
+console.anonymousKey=${ANONYMOUS_KEY:anonymousKey}
+
+logging.config=file:///opt/syncope/conf/log4j2.xml
diff --git a/docker/console/src/main/resources/startup.sh b/docker/console/src/main/resources/startup.sh
index 22d2163..b12192c 100755
--- a/docker/console/src/main/resources/startup.sh
+++ b/docker/console/src/main/resources/startup.sh
@@ -17,6 +17,5 @@
 # specific language governing permissions and limitations
 # under the License.
 
-export LOADER_PATH="/opt/syncope/conf,/opt/syncope/lib"
 java -Dfile.encoding=UTF-8 -server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m \
  -XX:+DisableExplicitGC -Djava.security.egd=file:/dev/./urandom -jar /opt/syncope/lib/syncope-console.war
diff --git a/docker/core/pom.xml b/docker/core/pom.xml
index 7de23c5..b6ed6ec 100644
--- a/docker/core/pom.xml
+++ b/docker/core/pom.xml
@@ -60,15 +60,11 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-rest-cxf</artifactId>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-self-keymaster-starter</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-persistence-jpa</artifactId>
-      <version>${project.version}</version>
-    </dependency>
+
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
       <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
@@ -161,6 +157,12 @@
       <version>${jdbc.mssql.version}11</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.oracle.database.jdbc</groupId>
+      <artifactId>ojdbc11</artifactId>
+      <version>${jdbc.oracle.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -197,6 +199,7 @@
                 <copy file="${settings.localRepository}/mysql/mysql-connector-java/${jdbc.mysql.version}/mysql-connector-java-${jdbc.mysql.version}.jar" todir="${project.build.outputDirectory}/lib" overwrite="true"/>
                 <copy file="${settings.localRepository}/org/mariadb/jdbc/mariadb-java-client/${jdbc.mariadb.version}/mariadb-java-client-${jdbc.mariadb.version}.jar" todir="${project.build.outputDirectory}/lib" overwrite="true"/>
                 <copy file="${settings.localRepository}/com/microsoft/sqlserver/mssql-jdbc/${jdbc.mssql.version}11/mssql-jdbc-${jdbc.mssql.version}11.jar" todir="${project.build.outputDirectory}/lib" overwrite="true"/>
+                <copy file="${settings.localRepository}/com/oracle/database/jdbc/ojdbc11/${jdbc.oracle.version}/ojdbc11-${jdbc.oracle.version}.jar" todir="${project.build.outputDirectory}/lib" overwrite="true"/>
 
                 <copy file="${basedir}/../../core/persistence-jpa-json/target/syncope-core-persistence-jpa-json-${project.version}.jar" todir="${project.build.outputDirectory}/jpa-json" overwrite="true"/>
               </target>
@@ -287,43 +290,6 @@
       </resource>
 
       <resource>
-        <directory>${basedir}/../../core/spring/src/main/resources</directory>
-        <includes>
-          <include>security.properties</include>
-        </includes>
-        <filtering>true</filtering>
-      </resource>
-      <resource>
-        <directory>${basedir}/../../ext/flowable/flowable-bpmn/src/main/resources</directory>
-        <includes>
-          <include>workflow.properties</include>
-          <include>userWorkflow.bpmn20.xml</include>
-        </includes>
-        <filtering>true</filtering>
-      </resource>
-      <resource>
-        <directory>${basedir}/../../core/provisioning-java/src/main/resources</directory>
-        <includes>
-          <include>mail.properties</include>
-          <include>connid.properties</include>
-        </includes>
-        <filtering>true</filtering>
-      </resource>
-      <resource>
-        <directory>${basedir}/../../core/idrepo/logic/src/main/resources</directory>
-        <includes>
-          <include>logic.properties</include>
-        </includes>
-        <filtering>true</filtering>
-      </resource>
-      <resource>
-        <directory>${project.basedir}/../../ext/saml2sp4ui/logic/src/main/resources</directory>
-        <includes>
-          <include>saml2sp4ui-logic.properties</include>
-        </includes>
-        <filtering>true</filtering>
-      </resource>
-      <resource>
         <directory>${basedir}/../../fit/core-reference/src/test/resources</directory>
         <includes>
           <include>saml.keystore.jks</include>
diff --git a/docker/core/src/main/resources/Dockerfile b/docker/core/src/main/resources/Dockerfile
index a1eeda8..9d13cd0 100644
--- a/docker/core/src/main/resources/Dockerfile
+++ b/docker/core/src/main/resources/Dockerfile
@@ -24,15 +24,13 @@
 RUN mkdir /opt/syncope/bin
 RUN mkdir /opt/syncope/bundles
 RUN mkdir /opt/syncope/conf
-RUN mkdir /opt/syncope/conf/domains
 RUN mkdir /opt/syncope/lib
 RUN mkdir /opt/syncope/jpa-json
 RUN mkdir /opt/syncope/log
 
-COPY *.properties* /opt/syncope/conf/
-COPY *.xml* /opt/syncope/conf/
+COPY *.properties /opt/syncope/conf/
+COPY *.xml /opt/syncope/conf/
 COPY saml.keystore.jks /opt/syncope/conf/
-COPY domains/* /opt/syncope/conf/domains/
 
 COPY bundles/*.jar /opt/syncope/bundles/
 COPY lib/*.jar /opt/syncope/lib/
@@ -40,6 +38,9 @@
 
 COPY lib/syncope-docker-core-*war /opt/syncope/lib/syncope.war
 
+ENV SPRING_PROFILES_ACTIVE=docker
+ENV LOADER_PATH="/opt/syncope/conf,/opt/syncope/lib"
+
 COPY startup.sh /opt/syncope/bin
 RUN chmod 755 /opt/syncope/bin/startup.sh
 CMD ["/opt/syncope/bin/startup.sh"]
diff --git a/docker/core/src/main/resources/application.properties b/docker/core/src/main/resources/application.properties
deleted file mode 100644
index 6cb0197..0000000
--- a/docker/core/src/main/resources/application.properties
+++ /dev/null
@@ -1,33 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} Core
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.servlet.encoding.charset=UTF-8
-server.servlet.encoding.enabled=true
-server.servlet.encoding.force=true
-
-conf.directory=${conf.directory}
-
-server.servlet.contextPath=/syncope
-cxf.path=/rest
-
-management.endpoints.web.exposure.include=health,info,loggers
-management.endpoint.health.show-details=ALWAYS
-
-service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
diff --git a/docker/core/src/main/resources/keymaster.properties b/docker/core/src/main/resources/core-docker.properties
similarity index 74%
rename from docker/core/src/main/resources/keymaster.properties
rename to docker/core/src/main/resources/core-docker.properties
index 14e8ca6..44fe8dc 100644
--- a/docker/core/src/main/resources/keymaster.properties
+++ b/docker/core/src/main/resources/core-docker.properties
@@ -17,3 +17,14 @@
 keymaster.address=${KEYMASTER_ADDRESS}
 keymaster.username=${KEYMASTER_USERNAME}
 keymaster.password=${KEYMASTER_PASSWORD}
+
+service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
+
+security.anonymousUser=${ANONYMOUS_USER:anonymous}
+security.anonymousKey=${ANONYMOUS_KEY:anonymousKey}
+
+persistence.remoteCommitProvider=${OPENJPA_REMOTE_COMMIT}
+
+provisioning.connIdLocation=${connid.location}
+
+logging.config=file:///opt/syncope/conf/log4j2.xml
diff --git a/docker/core/src/main/resources/core-mariadb.properties b/docker/core/src/main/resources/core-mariadb.properties
new file mode 100644
index 0000000..a1412f4
--- /dev/null
+++ b/docker/core/src/main/resources/core-mariadb.properties
@@ -0,0 +1,28 @@
+# 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.
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
+persistence.domain[0].jdbcURL=${DB_URL}
+persistence.domain[0].dbUsername=${DB_USER}
+persistence.domain[0].dbPassword=${DB_PASSWORD}
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].auditSql=audit_mariadb.sql
+persistence.domain[0].poolMaxActive=${DB_POOL_MAX}
+persistence.domain[0].poolMinIdle=${DB_POOL_MIN}
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mariadb.sql
diff --git a/docker/core/src/main/resources/core-myjson.properties b/docker/core/src/main/resources/core-myjson.properties
new file mode 100644
index 0000000..62d0454
--- /dev/null
+++ b/docker/core/src/main/resources/core-myjson.properties
@@ -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.
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mysql_innodb.sql
+
+persistence.entityFactory=org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory
+persistence.plainSchemaDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONPlainSchemaDAO
+persistence.plainAttrDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
+persistence.plainAttrValueDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO
+persistence.searchCondVisitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
+persistence.userDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
+persistence.groupDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
+persistence.anyObjectDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
+persistence.auditConfDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditConfDAO
+
+persistence.indexesXML=classpath:myjson/indexes.xml
+persistence.viewsXML=classpath:myjson/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].content=classpath:domains/jpa-json/MasterContent.xml
+persistence.domain[0].jdbcDriver=com.mysql.cj.jdbc.Driver
+persistence.domain[0].jdbcURL=${DB_URL}
+persistence.domain[0].dbUsername=${DB_USER}
+persistence.domain[0].dbPassword=${DB_PASSWORD}
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].orm=META-INF/spring-orm-myjson.xml
+persistence.domain[0].auditSql=audit_myjson.sql
+persistence.domain[0].poolMaxActive=${DB_POOL_MAX}
+persistence.domain[0].poolMinIdle=${DB_POOL_MIN}
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mysql_innodb.sql
diff --git a/docker/core/src/main/resources/core-mysql.properties b/docker/core/src/main/resources/core-mysql.properties
new file mode 100644
index 0000000..12b2f29
--- /dev/null
+++ b/docker/core/src/main/resources/core-mysql.properties
@@ -0,0 +1,28 @@
+# 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.
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=com.mysql.cj.jdbc.Driver
+persistence.domain[0].jdbcURL=${DB_URL}
+persistence.domain[0].dbUsername=${DB_USER}
+persistence.domain[0].dbPassword=${DB_PASSWORD}
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].auditSql=audit_mysql_innodb.sql
+persistence.domain[0].poolMaxActive=${DB_POOL_MAX}
+persistence.domain[0].poolMinIdle=${DB_POOL_MIN}
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mysql_innodb.sql
diff --git a/docker/core/src/main/resources/core-oracle.properties b/docker/core/src/main/resources/core-oracle.properties
new file mode 100644
index 0000000..88a8df9
--- /dev/null
+++ b/docker/core/src/main/resources/core-oracle.properties
@@ -0,0 +1,31 @@
+# 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.
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=oracle.jdbc.OracleDriver
+persistence.domain[0].jdbcURL=${DB_URL}
+persistence.domain[0].schema=${DB_SCHEMA}
+persistence.domain[0].dbUsername=${DB_USER}
+persistence.domain[0].dbPassword=${DB_PASSWORD}
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.OracleDictionary
+persistence.domain[0].orm=META-INF/spring-orm-oracle.xml
+persistence.domain[0].auditSql=audit_oracle.sql
+persistence.domain[0].poolMaxActive=${DB_POOL_MAX}
+persistence.domain[0].poolMinIdle=${DB_POOL_MIN}
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
+provisioning.quartz.sql=tables_oracle.sql
diff --git a/docker/core/src/main/resources/core-pgjsonb.properties b/docker/core/src/main/resources/core-pgjsonb.properties
new file mode 100644
index 0000000..030d7ec
--- /dev/null
+++ b/docker/core/src/main/resources/core-pgjsonb.properties
@@ -0,0 +1,45 @@
+# 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.
+
+persistence.entityFactory=org.apache.syncope.core.persistence.jpa.entity.PGJPAJSONEntityFactory
+persistence.plainSchemaDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONPlainSchemaDAO
+persistence.plainAttrDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
+persistence.plainAttrValueDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO
+persistence.searchCondVisitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
+persistence.userDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
+persistence.groupDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
+persistence.anyObjectDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
+persistence.auditConfDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditConfDAO
+
+persistence.indexesXML=classpath:pgjsonb/indexes.xml
+persistence.viewsXML=classpath:pgjsonb/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].content=classpath:domains/jpa-json/MasterContent.xml
+persistence.domain[0].jdbcDriver=org.postgresql.Driver
+persistence.domain[0].jdbcURL=${DB_URL}
+persistence.domain[0].dbUsername=${DB_USER}
+persistence.domain[0].dbPassword=${DB_PASSWORD}
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
+persistence.domain[0].orm=META-INF/spring-orm-pgjsonb.xml
+persistence.domain[0].auditSql=audit_pgjsonb.sql
+persistence.domain[0].poolMaxActive=${DB_POOL_MAX}
+persistence.domain[0].poolMinIdle=${DB_POOL_MIN}
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
+provisioning.quartz.sql=tables_postgres.sql
diff --git a/docker/core/src/main/resources/core-postgresql.properties b/docker/core/src/main/resources/core-postgresql.properties
new file mode 100644
index 0000000..d35e330
--- /dev/null
+++ b/docker/core/src/main/resources/core-postgresql.properties
@@ -0,0 +1,28 @@
+# 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.
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.postgresql.Driver
+persistence.domain[0].jdbcURL=${DB_URL}
+persistence.domain[0].dbUsername=${DB_USER}
+persistence.domain[0].dbPassword=${DB_PASSWORD}
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
+persistence.domain[0].auditSql=audit.sql
+persistence.domain[0].poolMaxActive=${DB_POOL_MAX}
+persistence.domain[0].poolMinIdle=${DB_POOL_MIN}
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
+provisioning.quartz.sql=tables_postgres.sql
diff --git a/docker/core/src/main/resources/core-sqlserver.properties b/docker/core/src/main/resources/core-sqlserver.properties
new file mode 100644
index 0000000..91d2909
--- /dev/null
+++ b/docker/core/src/main/resources/core-sqlserver.properties
@@ -0,0 +1,33 @@
+# 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.
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=com.microsoft.sqlserver.jdbc.SQLServerDriver
+persistence.domain[0].jdbcURL=${DB_URL}
+persistence.domain[0].schema=${DB_SCHEMA}
+persistence.domain[0].dbUsername=${DB_USER}
+persistence.domain[0].dbPassword=${DB_PASSWORD}
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.SQLServerDictionary
+persistence.domain[0].orm=META-INF/spring-orm-sqlserver.xml
+persistence.domain[0].auditSql=audit_sqlserver.sql
+persistence.domain[0].poolMaxActive=${DB_POOL_MAX}
+persistence.domain[0].poolMinIdle=${DB_POOL_MIN}
+
+persistence.viewsXML=classpath:sqlserver_views.xml
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.MSSQLDelegate
+provisioning.quartz.sql=tables_sqlServer.sql
diff --git a/docker/core/src/main/resources/domains/Master.properties.mariadb b/docker/core/src/main/resources/domains/Master.properties.mariadb
deleted file mode 100644
index f5aeab8..0000000
--- a/docker/core/src/main/resources/domains/Master.properties.mariadb
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.mariadb.jdbc.Driver
-Master.url=${DB_URL}
-Master.schema=
-Master.username=${DB_USER}
-Master.password=${DB_PASSWORD}
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=${DB_POOL_MAX}
-Master.pool.minIdle=${DB_POOL_MIN}
-
-Master.audit.sql=audit.sql
diff --git a/docker/core/src/main/resources/domains/Master.properties.mssql b/docker/core/src/main/resources/domains/Master.properties.mssql
deleted file mode 100644
index 108e1fe..0000000
--- a/docker/core/src/main/resources/domains/Master.properties.mssql
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
-Master.url=${DB_URL}
-Master.schema=${DB_SCHEMA}
-Master.username=${DB_USER}
-Master.password=${DB_PASSWORD}
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.SQLServerDictionary
-Master.orm=META-INF/spring-orm-sqlserver.xml
-
-Master.pool.maxActive=${DB_POOL_MAX}
-Master.pool.minIdle=${DB_POOL_MIN}
-
-Master.audit.sql=audit_sqlserver.sql
diff --git a/docker/core/src/main/resources/domains/Master.properties.myjson b/docker/core/src/main/resources/domains/Master.properties.myjson
deleted file mode 100644
index 958fd2e..0000000
--- a/docker/core/src/main/resources/domains/Master.properties.myjson
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=com.mysql.cj.jdbc.Driver
-Master.url=${DB_URL}
-Master.schema=
-Master.username=${DB_USER}
-Master.password=${DB_PASSWORD}
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
-Master.orm=META-INF/spring-orm-myjson.xml
-
-Master.pool.maxActive=${DB_POOL_MAX}
-Master.pool.minIdle=${DB_POOL_MIN}
-
-Master.audit.sql=audit_myjson.sql
diff --git a/docker/core/src/main/resources/domains/Master.properties.mysql b/docker/core/src/main/resources/domains/Master.properties.mysql
deleted file mode 100644
index 86e634a..0000000
--- a/docker/core/src/main/resources/domains/Master.properties.mysql
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=com.mysql.cj.jdbc.Driver
-Master.url=${DB_URL}
-Master.schema=
-Master.username=${DB_USER}
-Master.password=${DB_PASSWORD}
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=${DB_POOL_MAX}
-Master.pool.minIdle=${DB_POOL_MIN}
-
-Master.audit.sql=audit_mysql_innodb.sql
diff --git a/docker/core/src/main/resources/domains/Master.properties.pgjsonb b/docker/core/src/main/resources/domains/Master.properties.pgjsonb
deleted file mode 100644
index 8cb007d..0000000
--- a/docker/core/src/main/resources/domains/Master.properties.pgjsonb
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.postgresql.Driver
-Master.url=${DB_URL}
-Master.schema=
-Master.username=${DB_USER}
-Master.password=${DB_PASSWORD}
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
-Master.orm=META-INF/spring-orm-pgjsonb.xml
-
-Master.pool.maxActive=${DB_POOL_MAX}
-Master.pool.minIdle=${DB_POOL_MIN}
-
-Master.audit.sql=audit_pgjsonb.sql
diff --git a/docker/core/src/main/resources/domains/Master.properties.postgresql b/docker/core/src/main/resources/domains/Master.properties.postgresql
deleted file mode 100644
index 74b3471..0000000
--- a/docker/core/src/main/resources/domains/Master.properties.postgresql
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.postgresql.Driver
-Master.url=${DB_URL}
-Master.schema=
-Master.username=${DB_USER}
-Master.password=${DB_PASSWORD}
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=${DB_POOL_MAX}
-Master.pool.minIdle=${DB_POOL_MIN}
-
-Master.audit.sql=audit.sql
diff --git a/docker/core/src/main/resources/domains/MasterContent.xml.all b/docker/core/src/main/resources/domains/MasterContent.xml.all
deleted file mode 100644
index 932d2f4..0000000
--- a/docker/core/src/main/resources/domains/MasterContent.xml.all
+++ /dev/null
@@ -1,130 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<dataset>
-  <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/"/>
-
-  <AnyType id="USER" kind="USER"/>
-  <AnyTypeClass id="BaseUser"/>
-  <AnyType_AnyTypeClass anyType_id="USER" anyTypeClass_id="BaseUser"/>
-
-  <AnyType id="GROUP" kind="GROUP"/>
-  <AnyTypeClass id="BaseGroup"/>
-  <AnyType_AnyTypeClass anyType_id="GROUP" anyTypeClass_id="BaseGroup"/>
-        
-  <!-- Actual plain schemas -->
-  <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
-                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
-  <SyncopeSchema id="email"/>
-  <PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
-               mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
-               validator_id="EmailAddressValidator"/>
-  
-  <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
-                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.BinaryValidator"/>
-
-  <Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
-  <Implementation id="PushJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate"/>
-
-
-  <Implementation id="ExpiredAccessTokenCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/>
-  <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
-  <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/>
-  <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
-
-  <!-- Password reset notifications -->
-  <MailTemplate id="requestPasswordReset"
-                textTemplate="Hi,
-a password reset was requested for ${user.getUsername()}.
-
-In order to complete this request, you need to visit this link:
-
-http://localhost:9080/syncope-enduser/confirmpasswordreset?token=${input.get(0).replaceAll(' ', '%20')}
-
-If you did not request this reset, just ignore the present e-mail.
-
-Best regards."
-                htmlTemplate="&lt;html&gt;
-&lt;body&gt;
-&lt;p&gt;Hi,
-a password reset was requested for ${user.getUsername()}.&lt;/p&gt;
-
-&lt;p&gt;In order to complete this request, you need to visit this 
-&lt;a href=&quot;http://localhost:9080/syncope-enduser/confirmpasswordreset?token=${input.get(0).replaceAll(' ', '%20')}&quot;&gt;link&lt;/a&gt;&lt;/p&gt;.
-
-&lt;p&gt;If you did not request this reset, just ignore the present e-mail.&lt;/p&gt;
-
-&lt;p&gt;Best regards.&lt;/p&gt;
-&lt;/body&gt;
-&lt;/html&gt;"/>
-  <MailTemplate id="confirmPasswordReset"
-                textTemplate="Hi,
-we are happy to inform you that the password request was successfully executed for your account.
-
-Best regards."
-                htmlTemplate="&lt;html&gt;
-&lt;body&gt;
-&lt;p&gt;Hi,&lt;/br&gt;
-we are happy to inform you that the password request was successfully executed for your account.&lt;/p&gt;
-
-&lt;p&gt;Best regards.&lt;/p&gt;
-&lt;/body&gt;
-&lt;/html&gt;"/>
-
-  <Notification id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" active="1" recipientAttrName="email" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset request" template_id="requestPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <AnyAbout id="a328f2e6-25e9-4cc1-badf-7425d7be4b39" anyType_id="USER" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" filter="token!=$null"/>
-  <Notification_events notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
-  
-  <Notification id="bef0c250-e8a7-4848-bb63-2564fc409ce2" active="1" recipientAttrName="email" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset successful" template_id="confirmPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <Notification_events notification_id="bef0c250-e8a7-4848-bb63-2564fc409ce2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
-
-  <ReportTemplate id="empty"/>  
-
-  <Report id="c3520ad9-179f-49e7-b315-d684d216dd97" name="reconciliation" active="1" template_id="empty"/>
-  <Implementation id="ReconciliationReportletConf" type="REPORTLET" engine="JAVA"
-                  body='{"@class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
-  <ReportReportlet report_id="c3520ad9-179f-49e7-b315-d684d216dd97" implementation_id="ReconciliationReportletConf"/>
-
-  <SyncopeRole id="GROUP_OWNER"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_CREATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_DELETE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="RELATIONSHIPTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="REALM_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_DELETE"/>
-</dataset>
diff --git a/docker/core/src/main/resources/domains/MasterContent.xml.pgjsonb b/docker/core/src/main/resources/domains/MasterContent.xml.pgjsonb
deleted file mode 100644
index 0e35f39..0000000
--- a/docker/core/src/main/resources/domains/MasterContent.xml.pgjsonb
+++ /dev/null
@@ -1,130 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<dataset>
-  <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/"/>  
-
-  <AnyType id="USER" kind="USER"/>
-  <AnyTypeClass id="BaseUser"/>
-  <AnyType_AnyTypeClass anyType_id="USER" anyTypeClass_id="BaseUser"/>
-
-  <AnyType id="GROUP" kind="GROUP"/>
-  <AnyTypeClass id="BaseGroup"/>
-  <AnyType_AnyTypeClass anyType_id="GROUP" anyTypeClass_id="BaseGroup"/>
-        
-  <!-- Actual plain schemas -->
-  <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
-                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
-  <SyncopeSchema id="email"/>
-  <PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
-               mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
-               validator_id="EmailAddressValidator"/>
-  
-  <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
-                  body="org.apache.syncope.core.persistence.jpa.attrvalue.validation.BinaryValidator"/>
-
-  <Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
-  <Implementation id="PushJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate"/>
-
-
-  <Implementation id="ExpiredAccessTokenCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup"/>
-  <Task DTYPE="SchedTask" id="89de5014-e3f5-4462-84d8-d97575740baf" name="Access Token Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredAccessTokenCleanup" cronExpression="0 0/5 * * * ?"/>
-  <Implementation id="ExpiredBatchCleanup" type="TASKJOB_DELEGATE" engine="JAVA"
-                  body="org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup"/>
-  <Task DTYPE="SchedTask" id="8ea0ea51-ce08-4fe3-a0c8-c281b31b5893" name="Expired Batch Operations Cleanup Task"  active="1"
-        jobDelegate_id="ExpiredBatchCleanup" cronExpression="0 0/5 * * * ?"/>
-
-  <!-- Password reset notifications -->
-  <MailTemplate id="requestPasswordReset"
-                textTemplate="Hi,
-a password reset was requested for ${user.getUsername()}.
-
-In order to complete this request, you need to visit this link:
-
-http://localhost:9080/syncope-enduser/confirmpasswordreset?token=${input.get(0).replaceAll(' ', '%20')}
-
-If you did not request this reset, just ignore the present e-mail.
-
-Best regards."
-                htmlTemplate="&lt;html&gt;
-&lt;body&gt;
-&lt;p&gt;Hi,
-a password reset was requested for ${user.getUsername()}.&lt;/p&gt;
-
-&lt;p&gt;In order to complete this request, you need to visit this 
-&lt;a href=&quot;http://localhost:9080/syncope-enduser/confirmpasswordreset?token=${input.get(0).replaceAll(' ', '%20')}&quot;&gt;link&lt;/a&gt;&lt;/p&gt;.
-
-&lt;p&gt;If you did not request this reset, just ignore the present e-mail.&lt;/p&gt;
-
-&lt;p&gt;Best regards.&lt;/p&gt;
-&lt;/body&gt;
-&lt;/html&gt;"/>
-  <MailTemplate id="confirmPasswordReset"
-                textTemplate="Hi,
-we are happy to inform you that the password request was successfully executed for your account.
-
-Best regards."
-                htmlTemplate="&lt;html&gt;
-&lt;body&gt;
-&lt;p&gt;Hi,&lt;/br&gt;
-we are happy to inform you that the password request was successfully executed for your account.&lt;/p&gt;
-
-&lt;p&gt;Best regards.&lt;/p&gt;
-&lt;/body&gt;
-&lt;/html&gt;"/>
-
-  <Notification id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" active="1" recipientAttrName="email" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset request" template_id="requestPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <AnyAbout id="a328f2e6-25e9-4cc1-badf-7425d7be4b39" anyType_id="USER" notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" filter="token!=$null"/>
-  <Notification_events notification_id="e00945b5-1184-4d43-8e45-4318a8dcdfd4" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
-  
-  <Notification id="bef0c250-e8a7-4848-bb63-2564fc409ce2" active="1" recipientAttrName="email" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset successful" template_id="confirmPasswordReset" 
-                traceLevel="FAILURES"/> 
-  <Notification_events notification_id="bef0c250-e8a7-4848-bb63-2564fc409ce2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
-
-  <ReportTemplate id="empty"/>  
-
-  <Report id="c3520ad9-179f-49e7-b315-d684d216dd97" name="reconciliation" active="1" template_id="empty"/>
-  <Implementation id="ReconciliationReportletConf" type="REPORTLET" engine="JAVA"
-                  body='{"@class":"org.apache.syncope.common.lib.report.ReconciliationReportletConf","name":"dashboardReconciliationReportlet","userMatchingCond":null,"groupMatchingCond":null,"anyObjectMatchingCond":null,"features":["key","username","groupName"]}'/>
-  <ReportReportlet report_id="c3520ad9-179f-49e7-b315-d684d216dd97" implementation_id="ReconciliationReportletConf"/>
-
-  <SyncopeRole id="GROUP_OWNER"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_CREATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="USER_DELETE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPECLASS_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="RELATIONSHIPTYPE_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="ANYTYPE_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="REALM_LIST"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_SEARCH"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_READ"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_UPDATE"/>
-  <SyncopeRole_entitlements role_id="GROUP_OWNER" entitlement="GROUP_DELETE"/>
-</dataset>
diff --git a/docker/core/src/main/resources/index.xml.myjson b/docker/core/src/main/resources/index.xml.myjson
deleted file mode 100644
index 08340e6..0000000
--- a/docker/core/src/main/resources/index.xml.myjson
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  <comment>Additional indexes (in respect to JPA's)</comment>
-
-  <entry key="UDynGroupMembers_any_id">CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id)</entry>
-  <entry key="UDynGroupMembers_group_id">CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id)</entry>
-  <entry key="ADynGroupMembers_any_id">CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id)</entry>
-  <entry key="ADynGroupMembers_group_id">CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id)</entry>
-
-  <entry key="DynRoleMembers_any_id">CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id)</entry>
-  <entry key="DynRoleMembers_role_id">CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id)</entry>
-
-  <entry key="DynRealmMembers_any_id">CREATE INDEX DynRealmMembers_any_id ON DynRealmMembers(any_id)</entry>
-  <entry key="DynRealmMembers_realm_id">CREATE INDEX DynRealmMembers_dynRealm_id ON DynRealmMembers(dynRealm_id)</entry>
-
-  <entry key="UMembership_GroupIndex">CREATE INDEX UMembership_GroupIndex ON UMembership(group_id)</entry>
-  <entry key="UMembership_UserIndex">CREATE INDEX UMembership_UserIndex ON UMembership(user_id)</entry>
-  <entry key="AMembership_GroupIndex">CREATE INDEX AMembership_GroupIndex ON AMembership(group_id)</entry>
-  <entry key="AMembership_AnyObjectIndex">CREATE INDEX AMembership_AnyObjectIndex ON AMembership(anyObject_id)</entry>
-
-  <entry key="URelationship_RightIndex">CREATE INDEX URelationship_RightIndex ON URelationship(anyObject_id)</entry>
-  <entry key="URelationship_LeftIndex">CREATE INDEX URelationship_LeftIndex ON URelationship(user_id)</entry>
-  <entry key="ARelationship_RightIndex">CREATE INDEX ARelationship_RightIndex ON ARelationship(right_anyObject_id)</entry>
-  <entry key="ARelationship_AnyObjectIndex">CREATE INDEX ARelationship_AnyObjectIndex ON ARelationship(left_anyObject_id)</entry>
-
-  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
-</properties>
diff --git a/docker/core/src/main/resources/indexes.xml.pgjsonb b/docker/core/src/main/resources/indexes.xml.pgjsonb
deleted file mode 100644
index 9de817a..0000000
--- a/docker/core/src/main/resources/indexes.xml.pgjsonb
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  <comment>Additional indexes (in respect to JPA's)</comment>
-
-  <entry key="SyncopeUser_plainAttrs_idx">CREATE INDEX SyncopeUser_plainAttrs_idx ON SyncopeUser USING gin ((plainAttrs) jsonb_path_ops)</entry>
-  <entry key="SyncopeGroup_plainAttrs_idx">CREATE INDEX SyncopeGroup_plainAttrs_idx ON SyncopeGroup USING gin ((plainAttrs) jsonb_path_ops)</entry>
-  <entry key="AnyObject_plainAttrs_idx">CREATE INDEX AnyObject_plainAttrs_idx ON AnyObject USING gin ((plainAttrs) jsonb_path_ops)</entry>
-
-  <entry key="UDynGroupMembers_any_id">CREATE INDEX UDynGroupMembers_any_id ON UDynGroupMembers(any_id)</entry>
-  <entry key="UDynGroupMembers_group_id">CREATE INDEX UDynGroupMembers_group_id ON UDynGroupMembers(group_id)</entry>
-  <entry key="ADynGroupMembers_any_id">CREATE INDEX ADynGroupMembers_any_id ON ADynGroupMembers(any_id)</entry>
-  <entry key="ADynGroupMembers_group_id">CREATE INDEX ADynGroupMembers_group_id ON ADynGroupMembers(group_id)</entry>
-
-  <entry key="DynRoleMembers_any_id">CREATE INDEX DynRoleMembers_any_id ON DynRoleMembers(any_id)</entry>
-  <entry key="DynRoleMembers_role_id">CREATE INDEX DynRoleMembers_role_id ON DynRoleMembers(role_id)</entry>
-
-  <entry key="DynRealmMembers_any_id">CREATE INDEX DynRealmMembers_any_id ON DynRealmMembers(any_id)</entry>
-  <entry key="DynRealmMembers_realm_id">CREATE INDEX DynRealmMembers_dynRealm_id ON DynRealmMembers(dynRealm_id)</entry>
-
-  <entry key="UMembership_GroupIndex">CREATE INDEX UMembership_GroupIndex ON UMembership(group_id)</entry>
-  <entry key="UMembership_UserIndex">CREATE INDEX UMembership_UserIndex ON UMembership(user_id)</entry>
-  <entry key="AMembership_GroupIndex">CREATE INDEX AMembership_GroupIndex ON AMembership(group_id)</entry>
-  <entry key="AMembership_AnyObjectIndex">CREATE INDEX AMembership_AnyObjectIndex ON AMembership(anyObject_id)</entry>
-
-  <entry key="URelationship_RightIndex">CREATE INDEX URelationship_RightIndex ON URelationship(anyObject_id)</entry>
-  <entry key="URelationship_LeftIndex">CREATE INDEX URelationship_LeftIndex ON URelationship(user_id)</entry>
-  <entry key="ARelationship_RightIndex">CREATE INDEX ARelationship_RightIndex ON ARelationship(right_anyObject_id)</entry>
-  <entry key="ARelationship_AnyObjectIndex">CREATE INDEX ARelationship_AnyObjectIndex ON ARelationship(left_anyObject_id)</entry>
-
-  <entry key="Task_executedIndex">CREATE INDEX Task_executedIndex ON Task(executed)</entry>
-</properties>
diff --git a/docker/core/src/main/resources/persistence.properties.all b/docker/core/src/main/resources/persistence.properties.all
deleted file mode 100644
index 34293b8..0000000
--- a/docker/core/src/main/resources/persistence.properties.all
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO
-any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditConfDAO
-openjpa.RemoteCommitProvider=${OPENJPA_REMOTE_COMMIT}
diff --git a/docker/core/src/main/resources/persistence.properties.myjson b/docker/core/src/main/resources/persistence.properties.myjson
deleted file mode 100644
index f32258e..0000000
--- a/docker/core/src/main/resources/persistence.properties.myjson
+++ /dev/null
@@ -1,29 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO
-any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONConfDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditConfDAO
-openjpa.RemoteCommitProvider=sjvm
diff --git a/docker/core/src/main/resources/persistence.properties.pgjsonb b/docker/core/src/main/resources/persistence.properties.pgjsonb
deleted file mode 100644
index ab20e0d..0000000
--- a/docker/core/src/main/resources/persistence.properties.pgjsonb
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.PGJPAJSONEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO
-any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditConfDAO
-openjpa.RemoteCommitProvider=${OPENJPA_REMOTE_COMMIT}
diff --git a/docker/core/src/main/resources/provisioning.properties.mariadb b/docker/core/src/main/resources/provisioning.properties.mariadb
deleted file mode 100644
index 8ed216e..0000000
--- a/docker/core/src/main/resources/provisioning.properties.mariadb
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_mariadb.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/docker/core/src/main/resources/provisioning.properties.mssql b/docker/core/src/main/resources/provisioning.properties.mssql
deleted file mode 100644
index 32fead9..0000000
--- a/docker/core/src/main/resources/provisioning.properties.mssql
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.MSSQLDelegate
-quartz.sql=tables_sqlServer.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/docker/core/src/main/resources/provisioning.properties.myjson b/docker/core/src/main/resources/provisioning.properties.myjson
deleted file mode 100644
index 6dfcbef..0000000
--- a/docker/core/src/main/resources/provisioning.properties.myjson
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_mysql_innodb.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/docker/core/src/main/resources/provisioning.properties.mysql b/docker/core/src/main/resources/provisioning.properties.mysql
deleted file mode 100644
index 6dfcbef..0000000
--- a/docker/core/src/main/resources/provisioning.properties.mysql
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_mysql_innodb.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/docker/core/src/main/resources/provisioning.properties.pgjsonb b/docker/core/src/main/resources/provisioning.properties.pgjsonb
deleted file mode 100644
index 8ff6643..0000000
--- a/docker/core/src/main/resources/provisioning.properties.pgjsonb
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
-quartz.sql=tables_postgres.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/docker/core/src/main/resources/provisioning.properties.postgresql b/docker/core/src/main/resources/provisioning.properties.postgresql
deleted file mode 100644
index 096c89c..0000000
--- a/docker/core/src/main/resources/provisioning.properties.postgresql
+++ /dev/null
@@ -1,36 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
-quartz.sql=tables_postgres.sql
-quartz.disableInstance=false
diff --git a/docker/core/src/main/resources/startup.sh b/docker/core/src/main/resources/startup.sh
index ecab0f7..f0007cf 100755
--- a/docker/core/src/main/resources/startup.sh
+++ b/docker/core/src/main/resources/startup.sh
@@ -17,51 +17,5 @@
 # specific language governing permissions and limitations
 # under the License.
 
-cd /opt/syncope/conf
-
-rm -f provisioning.properties
-ln -s provisioning.properties.$DBMS provisioning.properties
-
-rm -f persistence.properties
-if [ $DBMS = "pgjsonb" ]; then
-  ln -s persistence.properties.pgjsonb persistence.properties
-elif [ $DBMS = "myjson" ]; then
-  ln -s persistence.properties.myjson persistence.properties
-else
-  ln -s persistence.properties.all persistence.properties
-fi
-
-rm -f views.xml
-ln -s views.xml.$DBMS views.xml
-
-if [ $DBMS = "pgjsonb" ]; then
-  ln -s indexes.xml.pgjsonb indexes.xml
-elif [ $DBMS = "myjson" ]; then
-  ln -s indexes.xml.myjson indexes.xml
-else
-  rm -f indexes.xml
-fi
-
-cd domains
-
-if [ $DBMS = "pgjsonb" ]; then
-  mv MasterContent.xml MasterContent.xml.all
-  ln -s MasterContent.xml.pgjsonb MasterContent.xml
-elif [ $DBMS = "myjson" ]; then
-  mv MasterContent.xml MasterContent.xml.all
-  ln -s MasterContent.xml.myjson MasterContent.xml
-else
-  rm -f MasterContent.xml
-  mv MasterContent.xml.all MasterContent.xml
-fi
-
-rm -f Master.properties
-ln -s Master.properties.$DBMS Master.properties
-
-if [ $DBMS = "pgjsonb" ] || [ $DBMS = "myjson" ] ; then
-export LOADER_PATH="/opt/syncope/conf,/opt/syncope/lib,/opt/syncope/jpa-json"
-else
-export LOADER_PATH="/opt/syncope/conf,/opt/syncope/lib"
-fi
 java -Dfile.encoding=UTF-8 -server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m \
  -XX:+DisableExplicitGC -Djava.security.egd=file:/dev/./urandom -jar /opt/syncope/lib/syncope.war
diff --git a/docker/core/src/main/resources/views.xml.mariadb b/docker/core/src/main/resources/views.xml.mariadb
deleted file mode 100644
index e8e9a21..0000000
--- a/docker/core/src/main/resources/views.xml.mariadb
+++ /dev/null
@@ -1,268 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  
-  <entry key="UDynGroupMembers">
-    CREATE TABLE UDynGroupMembers(
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(any_id, group_id))
-  </entry>
-  <entry key="ADynGroupMembers">
-    CREATE TABLE ADynGroupMembers(
-    anyType_id VARCHAR(255),
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(anyType_id, any_id, group_id))
-  </entry>
-  <entry key="DynRoleMembers">
-    CREATE TABLE DynRoleMembers(
-    any_id CHAR(36),
-    role_id VARCHAR(255),
-    UNIQUE(any_id, role_id))
-  </entry>
-  <entry key="DynRealmMembers">
-    CREATE TABLE DynRealmMembers(
-    any_id CHAR(36),
-    dynRealm_id VARCHAR(255),
-    UNIQUE(any_id, dynRealm_id))
-  </entry>
-
-  <!-- user -->
-  <entry key="user_search">
-    CREATE VIEW user_search AS
- 
-    SELECT u.id as any_id, u.* FROM SyncopeUser u
-  </entry>
-  <entry key="user_search_unique_attr">
-    CREATE VIEW user_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrUniqueValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_attr">
-    CREATE VIEW user_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_null_attr">
-    CREATE VIEW user_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    NULL AS datevalue,
-    NULL AS doublevalue,
-    NULL AS longvalue,
-    NULL AS stringvalue
-    FROM SyncopeUser u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN UPlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="user_search_urelationship">
-    CREATE VIEW user_search_urelationship AS
-
-    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
-    FROM URelationship m
-  </entry>
-  <entry key="user_search_umembership">
-    CREATE VIEW user_search_umembership AS
-
-    SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM UMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="user_search_role">
-    CREATE VIEW user_search_role AS
-
-    SELECT ss.user_id AS any_id, ss.role_id AS role_id
-    FROM SyncopeUser_SyncopeRole ss
-  </entry>
-  <entry key="user_search_priv">
-    CREATE VIEW user_search_priv AS
-
-    SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
-    FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
-    WHERE ss.role_id = sp.role_id
-  </entry>
-  <entry key="user_search_dynpriv">
-    CREATE VIEW user_search_dynpriv AS
-
-    SELECT any_id, privilege_id
-    FROM DynRoleMembers drm, SyncopeRole_Privilege rp
-    WHERE drm.role_id = rp.role_id
-  </entry>
-  <entry key="user_search_resource">
-    CREATE VIEW user_search_resource AS
-
-    SELECT st.user_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeUser_ExternalResource st
-  </entry>
-  <entry key="user_search_group_res">
-    CREATE VIEW user_search_group_res AS
-
-    SELECT m.user_id AS any_id, st.resource_id AS resource_id
-    FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- anyObject -->
-  <entry key="anyObject_search">
-    CREATE VIEW anyObject_search AS
- 
-    SELECT a.id as any_id, a.* FROM AnyObject a
-  </entry>
-  <entry key="anyObject_search_unique_attr">
-    CREATE VIEW anyObject_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrUniqueValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_attr">
-    CREATE VIEW anyObject_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_null_attr">
-    CREATE VIEW anyObject_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    NULL AS datevalue,
-    NULL AS doublevalue,
-    NULL AS longvalue,
-    NULL AS stringvalue
-    FROM AnyObject u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN APlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="anyObject_search_arelationship">
-    CREATE VIEW anyObject_search_arelationship AS
-
-    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
-    FROM ARelationship m
-  </entry>
-  <entry key="anyObject_search_amembership">
-    CREATE VIEW anyObject_search_amembership AS
-
-    SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM AMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="anyObject_search_resource">
-    CREATE VIEW anyObject_search_resource AS
-
-    SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AnyObject_ExternalResource st
-  </entry>
-  <entry key="anyObject_search_group_res">
-    CREATE VIEW anyObject_search_group_res AS
-
-    SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- group -->
-  <entry key="group_search">
-    CREATE VIEW group_search AS
- 
-    SELECT r.id as any_id, r.* FROM SyncopeGroup r
-  </entry>
-  <entry key="group_search_unique_attr">
-    CREATE VIEW group_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrUniqueValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_attr">
-    CREATE VIEW group_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_null_attr">
-    CREATE VIEW group_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    NULL AS datevalue,
-    NULL AS doublevalue,
-    NULL AS longvalue,
-    NULL AS stringvalue
-    FROM SyncopeGroup u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN GPlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="group_search_resource">
-    CREATE VIEW group_search_resource AS
-
-    SELECT st.group_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeGroup_ExternalResource st
-  </entry>
-
-</properties>
\ No newline at end of file
diff --git a/docker/core/src/main/resources/views.xml.mssql b/docker/core/src/main/resources/views.xml.mssql
deleted file mode 100644
index ab6be98..0000000
--- a/docker/core/src/main/resources/views.xml.mssql
+++ /dev/null
@@ -1,268 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  
-  <entry key="UDynGroupMembers">
-    CREATE TABLE UDynGroupMembers(
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(any_id, group_id))
-  </entry>
-  <entry key="ADynGroupMembers">
-    CREATE TABLE ADynGroupMembers(
-    anyType_id VARCHAR(255),
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(anyType_id, any_id, group_id))
-  </entry>
-  <entry key="DynRoleMembers">
-    CREATE TABLE DynRoleMembers(
-    any_id CHAR(36),
-    role_id VARCHAR(255),
-    UNIQUE(any_id, role_id))
-  </entry>
-  <entry key="DynRealmMembers">
-    CREATE TABLE DynRealmMembers(
-    any_id CHAR(36),
-    dynRealm_id VARCHAR(255),
-    UNIQUE(any_id, dynRealm_id))
-  </entry>
-
-  <!-- user -->
-  <entry key="user_search">
-    CREATE VIEW user_search AS
- 
-    SELECT u.id as any_id, u.* FROM SyncopeUser u
-  </entry>
-  <entry key="user_search_unique_attr">
-    CREATE VIEW user_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrUniqueValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_attr">
-    CREATE VIEW user_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_null_attr">
-    CREATE VIEW user_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    CAST (NULL AS DATETIME2) datevalue,
-    CAST (NULL AS FLOAT) doublevalue,
-    CAST (NULL AS BIGINT) longvalue,
-    CAST (NULL AS VARCHAR(255)) AS stringvalue
-    FROM SyncopeUser u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN UPlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="user_search_urelationship">
-    CREATE VIEW user_search_urelationship AS
-
-    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
-    FROM URelationship m
-  </entry>
-  <entry key="user_search_umembership">
-    CREATE VIEW user_search_umembership AS
-
-    SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM UMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="user_search_role">
-    CREATE VIEW user_search_role AS
-
-    SELECT ss.user_id AS any_id, ss.role_id AS role_id
-    FROM SyncopeUser_SyncopeRole ss
-  </entry>
-  <entry key="user_search_priv">
-    CREATE VIEW user_search_priv AS
-
-    SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
-    FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
-    WHERE ss.role_id = sp.role_id
-  </entry>
-  <entry key="user_search_dynpriv">
-    CREATE VIEW user_search_dynpriv AS
-
-    SELECT any_id, privilege_id
-    FROM DynRoleMembers drm, SyncopeRole_Privilege rp
-    WHERE drm.role_id = rp.role_id
-  </entry>
-  <entry key="user_search_resource">
-    CREATE VIEW user_search_resource AS
-
-    SELECT st.user_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeUser_ExternalResource st
-  </entry>
-  <entry key="user_search_group_res">
-    CREATE VIEW user_search_group_res AS
-
-    SELECT m.user_id AS any_id, st.resource_id AS resource_id
-    FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- anyObject -->
-  <entry key="anyObject_search">
-    CREATE VIEW anyObject_search AS
- 
-    SELECT a.id as any_id, a.* FROM AnyObject a
-  </entry>
-  <entry key="anyObject_search_unique_attr">
-    CREATE VIEW anyObject_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrUniqueValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_attr">
-    CREATE VIEW anyObject_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_null_attr">
-    CREATE VIEW anyObject_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    CAST (NULL AS DATETIME2) datevalue,
-    CAST (NULL AS FLOAT) doublevalue,
-    CAST (NULL AS BIGINT) longvalue,
-    CAST (NULL AS VARCHAR(255)) AS stringvalue
-    FROM AnyObject u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN APlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="anyObject_search_arelationship">
-    CREATE VIEW anyObject_search_arelationship AS
-
-    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
-    FROM ARelationship m
-  </entry>
-  <entry key="anyObject_search_amembership">
-    CREATE VIEW anyObject_search_amembership AS
-
-    SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM AMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="anyObject_search_resource">
-    CREATE VIEW anyObject_search_resource AS
-
-    SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AnyObject_ExternalResource st
-  </entry>
-  <entry key="anyObject_search_group_res">
-    CREATE VIEW anyObject_search_group_res AS
-
-    SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- group -->
-  <entry key="group_search">
-    CREATE VIEW group_search AS
- 
-    SELECT r.id as any_id, r.* FROM SyncopeGroup r
-  </entry>
-  <entry key="group_search_unique_attr">
-    CREATE VIEW group_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrUniqueValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_attr">
-    CREATE VIEW group_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_null_attr">
-    CREATE VIEW group_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    CAST (NULL AS DATETIME2) datevalue,
-    CAST (NULL AS FLOAT) doublevalue,
-    CAST (NULL AS BIGINT) longvalue,
-    CAST (NULL AS VARCHAR(255)) AS stringvalue
-    FROM SyncopeGroup u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN GPlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="group_search_resource">
-    CREATE VIEW group_search_resource AS
-
-    SELECT st.group_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeGroup_ExternalResource st
-  </entry>
-
-</properties>
diff --git a/docker/core/src/main/resources/views.xml.myjson b/docker/core/src/main/resources/views.xml.myjson
deleted file mode 100644
index aa0e3c0..0000000
--- a/docker/core/src/main/resources/views.xml.myjson
+++ /dev/null
@@ -1,181 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  
-  <entry key="UDynGroupMembers">
-    CREATE TABLE UDynGroupMembers(
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(any_id, group_id))
-  </entry>
-  <entry key="ADynGroupMembers">
-    CREATE TABLE ADynGroupMembers(
-    anyType_id VARCHAR(255),
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(anyType_id, any_id, group_id))
-  </entry>
-  <entry key="DynRoleMembers">
-    CREATE TABLE DynRoleMembers(
-    any_id CHAR(36),
-    role_id VARCHAR(255),
-    UNIQUE(any_id, role_id))
-  </entry>
-  <entry key="DynRealmMembers">
-    CREATE TABLE DynRealmMembers(
-    any_id CHAR(36),
-    dynRealm_id VARCHAR(255),
-    UNIQUE(any_id, dynRealm_id))
-  </entry>
-
-  <!-- user -->
-  <entry key="user_search">
-    CREATE VIEW user_search AS
-
-    SELECT u.id as any_id, u.*, attrs.*
-    FROM SyncopeUser u, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
-    plainSchema VARCHAR(255) PATH '$.schema',
-    NESTED PATH '$.values[*]' COLUMNS (
-    binaryValue LONGBLOB PATH '$.binaryValue',
-    booleanValue INT PATH '$.booleanValue',
-    dateValue BIGINT(20) PATH '$.dateValue',
-    doubleValue DOUBLE PATH '$.doubleValue',
-    longValue BIGINT(20) PATH '$.longValue',
-    stringValue VARCHAR(255) PATH '$.stringValue'),
-    attrUniqueValue JSON PATH '$.uniqueValue')
-    ) AS attrs
-  </entry>
-  <entry key="user_search_urelationship">
-    CREATE VIEW user_search_urelationship AS
-
-    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
-    FROM URelationship m
-  </entry>
-  <entry key="user_search_umembership">
-    CREATE VIEW user_search_umembership AS
-
-    SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM UMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="user_search_role">
-    CREATE VIEW user_search_role AS
-
-    SELECT ss.user_id AS any_id, ss.role_id AS role_id
-    FROM SyncopeUser_SyncopeRole ss
-  </entry>
-  <entry key="user_search_priv">
-    CREATE VIEW user_search_priv AS
-
-    SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
-    FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
-    WHERE ss.role_id = sp.role_id
-  </entry>
-  <entry key="user_search_dynpriv">
-    CREATE VIEW user_search_dynpriv AS
-
-    SELECT any_id, privilege_id
-    FROM DynRoleMembers drm, SyncopeRole_Privilege rp
-    WHERE drm.role_id = rp.role_id
-  </entry>
-  <entry key="user_search_resource">
-    CREATE VIEW user_search_resource AS
-
-    SELECT st.user_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeUser_ExternalResource st
-  </entry>
-  <entry key="user_search_group_res">
-    CREATE VIEW user_search_group_res AS
-
-    SELECT m.user_id AS any_id, st.resource_id AS resource_id
-    FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- anyObject -->
-  <entry key="anyObject_search">
-    CREATE VIEW anyObject_search AS
- 
-    SELECT a.id as any_id, a.*, attrs.*
-    FROM AnyObject a, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
-    plainSchema VARCHAR(255) PATH '$.schema',
-    NESTED PATH '$.values[*]' COLUMNS (
-    binaryValue LONGBLOB PATH '$.binaryValue',
-    booleanValue INT PATH '$.booleanValue',
-    dateValue BIGINT(20) PATH '$.dateValue',
-    doubleValue DOUBLE PATH '$.doubleValue',
-    longValue BIGINT(20) PATH '$.longValue',
-    stringValue VARCHAR(255) PATH '$.stringValue'),
-    attrUniqueValue JSON PATH '$.uniqueValue')
-    ) AS attrs
-  </entry>
-  <entry key="anyObject_search_arelationship">
-    CREATE VIEW anyObject_search_arelationship AS
-
-    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
-    FROM ARelationship m
-  </entry>
-  <entry key="anyObject_search_amembership">
-    CREATE VIEW anyObject_search_amembership AS
-
-    SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM AMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="anyObject_search_resource">
-    CREATE VIEW anyObject_search_resource AS
-
-    SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AnyObject_ExternalResource st
-  </entry>
-  <entry key="anyObject_search_group_res">
-    CREATE VIEW anyObject_search_group_res AS
-
-    SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- group -->
-  <entry key="group_search">
-    CREATE VIEW group_search AS
- 
-    SELECT g.id as any_id, g.*, attrs.*
-    FROM SyncopeGroup g, JSON_TABLE(COALESCE(plainAttrs, '[{}]'), '$[*]' COLUMNS (
-    plainSchema VARCHAR(255) PATH '$.schema',
-    NESTED PATH '$.values[*]' COLUMNS (
-    binaryValue LONGBLOB PATH '$.binaryValue',
-    booleanValue INT PATH '$.booleanValue',
-    dateValue BIGINT(20) PATH '$.dateValue',
-    doubleValue DOUBLE PATH '$.doubleValue',
-    longValue BIGINT(20) PATH '$.longValue',
-    stringValue VARCHAR(255) PATH '$.stringValue'),
-    attrUniqueValue JSON PATH '$.uniqueValue')
-    ) AS attrs
-  </entry>
-  <entry key="group_search_resource">
-    CREATE VIEW group_search_resource AS
-
-    SELECT st.group_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeGroup_ExternalResource st
-  </entry>
-
-</properties>
diff --git a/docker/core/src/main/resources/views.xml.mysql b/docker/core/src/main/resources/views.xml.mysql
deleted file mode 100644
index e8e9a21..0000000
--- a/docker/core/src/main/resources/views.xml.mysql
+++ /dev/null
@@ -1,268 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  
-  <entry key="UDynGroupMembers">
-    CREATE TABLE UDynGroupMembers(
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(any_id, group_id))
-  </entry>
-  <entry key="ADynGroupMembers">
-    CREATE TABLE ADynGroupMembers(
-    anyType_id VARCHAR(255),
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(anyType_id, any_id, group_id))
-  </entry>
-  <entry key="DynRoleMembers">
-    CREATE TABLE DynRoleMembers(
-    any_id CHAR(36),
-    role_id VARCHAR(255),
-    UNIQUE(any_id, role_id))
-  </entry>
-  <entry key="DynRealmMembers">
-    CREATE TABLE DynRealmMembers(
-    any_id CHAR(36),
-    dynRealm_id VARCHAR(255),
-    UNIQUE(any_id, dynRealm_id))
-  </entry>
-
-  <!-- user -->
-  <entry key="user_search">
-    CREATE VIEW user_search AS
- 
-    SELECT u.id as any_id, u.* FROM SyncopeUser u
-  </entry>
-  <entry key="user_search_unique_attr">
-    CREATE VIEW user_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrUniqueValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_attr">
-    CREATE VIEW user_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_null_attr">
-    CREATE VIEW user_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    NULL AS datevalue,
-    NULL AS doublevalue,
-    NULL AS longvalue,
-    NULL AS stringvalue
-    FROM SyncopeUser u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN UPlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="user_search_urelationship">
-    CREATE VIEW user_search_urelationship AS
-
-    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
-    FROM URelationship m
-  </entry>
-  <entry key="user_search_umembership">
-    CREATE VIEW user_search_umembership AS
-
-    SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM UMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="user_search_role">
-    CREATE VIEW user_search_role AS
-
-    SELECT ss.user_id AS any_id, ss.role_id AS role_id
-    FROM SyncopeUser_SyncopeRole ss
-  </entry>
-  <entry key="user_search_priv">
-    CREATE VIEW user_search_priv AS
-
-    SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
-    FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
-    WHERE ss.role_id = sp.role_id
-  </entry>
-  <entry key="user_search_dynpriv">
-    CREATE VIEW user_search_dynpriv AS
-
-    SELECT any_id, privilege_id
-    FROM DynRoleMembers drm, SyncopeRole_Privilege rp
-    WHERE drm.role_id = rp.role_id
-  </entry>
-  <entry key="user_search_resource">
-    CREATE VIEW user_search_resource AS
-
-    SELECT st.user_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeUser_ExternalResource st
-  </entry>
-  <entry key="user_search_group_res">
-    CREATE VIEW user_search_group_res AS
-
-    SELECT m.user_id AS any_id, st.resource_id AS resource_id
-    FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- anyObject -->
-  <entry key="anyObject_search">
-    CREATE VIEW anyObject_search AS
- 
-    SELECT a.id as any_id, a.* FROM AnyObject a
-  </entry>
-  <entry key="anyObject_search_unique_attr">
-    CREATE VIEW anyObject_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrUniqueValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_attr">
-    CREATE VIEW anyObject_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_null_attr">
-    CREATE VIEW anyObject_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    NULL AS datevalue,
-    NULL AS doublevalue,
-    NULL AS longvalue,
-    NULL AS stringvalue
-    FROM AnyObject u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN APlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="anyObject_search_arelationship">
-    CREATE VIEW anyObject_search_arelationship AS
-
-    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
-    FROM ARelationship m
-  </entry>
-  <entry key="anyObject_search_amembership">
-    CREATE VIEW anyObject_search_amembership AS
-
-    SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM AMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="anyObject_search_resource">
-    CREATE VIEW anyObject_search_resource AS
-
-    SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AnyObject_ExternalResource st
-  </entry>
-  <entry key="anyObject_search_group_res">
-    CREATE VIEW anyObject_search_group_res AS
-
-    SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- group -->
-  <entry key="group_search">
-    CREATE VIEW group_search AS
- 
-    SELECT r.id as any_id, r.* FROM SyncopeGroup r
-  </entry>
-  <entry key="group_search_unique_attr">
-    CREATE VIEW group_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrUniqueValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_attr">
-    CREATE VIEW group_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_null_attr">
-    CREATE VIEW group_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL AS booleanvalue,
-    NULL AS datevalue,
-    NULL AS doublevalue,
-    NULL AS longvalue,
-    NULL AS stringvalue
-    FROM SyncopeGroup u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN GPlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="group_search_resource">
-    CREATE VIEW group_search_resource AS
-
-    SELECT st.group_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeGroup_ExternalResource st
-  </entry>
-
-</properties>
\ No newline at end of file
diff --git a/docker/core/src/main/resources/views.xml.pgjsonb b/docker/core/src/main/resources/views.xml.pgjsonb
deleted file mode 100644
index eb450be..0000000
--- a/docker/core/src/main/resources/views.xml.pgjsonb
+++ /dev/null
@@ -1,154 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  
-  <entry key="UDynGroupMembers">
-    CREATE TABLE UDynGroupMembers(
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(any_id, group_id))
-  </entry>
-  <entry key="ADynGroupMembers">
-    CREATE TABLE ADynGroupMembers(
-    anyType_id VARCHAR(255),
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(anyType_id, any_id, group_id))
-  </entry>
-  <entry key="DynRoleMembers">
-    CREATE TABLE DynRoleMembers(
-    any_id CHAR(36),
-    role_id VARCHAR(255),
-    UNIQUE(any_id, role_id))
-  </entry>
-  <entry key="DynRealmMembers">
-    CREATE TABLE DynRealmMembers(
-    any_id CHAR(36),
-    dynRealm_id VARCHAR(255),
-    UNIQUE(any_id, dynRealm_id))
-  </entry>
-
-  <!-- user -->
-  <entry key="user_search">
-    CREATE VIEW user_search AS
-
-    SELECT u.id as any_id, u.*,attrs,attrValues
-    FROM SyncopeUser u, jsonb_array_elements(COALESCE(u.plainAttrs, '[{}]'::jsonb)) attrs,
-    jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues
-  </entry>
-  <entry key="user_search_urelationship">
-    CREATE VIEW user_search_urelationship AS
-
-    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
-    FROM URelationship m
-  </entry>
-  <entry key="user_search_umembership">
-    CREATE VIEW user_search_umembership AS
-
-    SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM UMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="user_search_role">
-    CREATE VIEW user_search_role AS
-
-    SELECT ss.user_id AS any_id, ss.role_id AS role_id
-    FROM SyncopeUser_SyncopeRole ss
-  </entry>
-  <entry key="user_search_priv">
-    CREATE VIEW user_search_priv AS
-
-    SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
-    FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
-    WHERE ss.role_id = sp.role_id
-  </entry>
-  <entry key="user_search_dynpriv">
-    CREATE VIEW user_search_dynpriv AS
-
-    SELECT any_id, privilege_id
-    FROM DynRoleMembers drm, SyncopeRole_Privilege rp
-    WHERE drm.role_id = rp.role_id
-  </entry>
-  <entry key="user_search_resource">
-    CREATE VIEW user_search_resource AS
-
-    SELECT st.user_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeUser_ExternalResource st
-  </entry>
-  <entry key="user_search_group_res">
-    CREATE VIEW user_search_group_res AS
-
-    SELECT m.user_id AS any_id, st.resource_id AS resource_id
-    FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- anyObject -->
-  <entry key="anyObject_search">
-    CREATE VIEW anyObject_search AS
-
-    SELECT a.id as any_id, a.*,attrs,attrValues
-    FROM AnyObject a, jsonb_array_elements(COALESCE(a.plainAttrs, '[{}]'::jsonb)) attrs,
-    jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues
-  </entry>
-  <entry key="anyObject_search_arelationship">
-    CREATE VIEW anyObject_search_arelationship AS
-
-    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
-    FROM ARelationship m
-  </entry>
-  <entry key="anyObject_search_amembership">
-    CREATE VIEW anyObject_search_amembership AS
-
-    SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM AMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="anyObject_search_resource">
-    CREATE VIEW anyObject_search_resource AS
-
-    SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AnyObject_ExternalResource st
-  </entry>
-  <entry key="anyObject_search_group_res">
-    CREATE VIEW anyObject_search_group_res AS
-
-    SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- group -->
-  <entry key="group_search">
-    CREATE VIEW group_search AS
-
-    SELECT g.id as any_id, g.*,attrs,attrValues
-    FROM SyncopeGroup g, jsonb_array_elements(COALESCE(g.plainAttrs, '[{}]'::jsonb)) attrs,
-    jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues
-  </entry>
-  <entry key="group_search_resource">
-    CREATE VIEW group_search_resource AS
-
-    SELECT st.group_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeGroup_ExternalResource st
-  </entry>
-
-</properties>
diff --git a/docker/core/src/main/resources/views.xml.postgresql b/docker/core/src/main/resources/views.xml.postgresql
deleted file mode 100644
index b6664c0..0000000
--- a/docker/core/src/main/resources/views.xml.postgresql
+++ /dev/null
@@ -1,268 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  
-  <entry key="UDynGroupMembers">
-    CREATE TABLE UDynGroupMembers(
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(any_id, group_id))
-  </entry>
-  <entry key="ADynGroupMembers">
-    CREATE TABLE ADynGroupMembers(
-    anyType_id VARCHAR(255),
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(anyType_id, any_id, group_id))
-  </entry>
-  <entry key="DynRoleMembers">
-    CREATE TABLE DynRoleMembers(
-    any_id CHAR(36),
-    role_id VARCHAR(255),
-    UNIQUE(any_id, role_id))
-  </entry>
-  <entry key="DynRealmMembers">
-    CREATE TABLE DynRealmMembers(
-    any_id CHAR(36),
-    dynRealm_id VARCHAR(255),
-    UNIQUE(any_id, dynRealm_id))
-  </entry>
-
-  <!-- user -->
-  <entry key="user_search">
-    CREATE VIEW user_search AS
- 
-    SELECT u.id as any_id, u.* FROM SyncopeUser u
-  </entry>
-  <entry key="user_search_unique_attr">
-    CREATE VIEW user_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrUniqueValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_attr">
-    CREATE VIEW user_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_null_attr">
-    CREATE VIEW user_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL::int4 AS booleanvalue,
-    NULL::timestamp AS datevalue,
-    NULL::float8 AS doublevalue,
-    NULL::int8 AS longvalue,
-    NULL AS stringvalue
-    FROM SyncopeUser u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN UPlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="user_search_urelationship">
-    CREATE VIEW user_search_urelationship AS
-
-    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
-    FROM URelationship m
-  </entry>
-  <entry key="user_search_umembership">
-    CREATE VIEW user_search_umembership AS
-
-    SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM UMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="user_search_role">
-    CREATE VIEW user_search_role AS
-
-    SELECT ss.user_id AS any_id, ss.role_id AS role_id
-    FROM SyncopeUser_SyncopeRole ss
-  </entry>
-  <entry key="user_search_priv">
-    CREATE VIEW user_search_priv AS
-
-    SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
-    FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
-    WHERE ss.role_id = sp.role_id
-  </entry>
-  <entry key="user_search_dynpriv">
-    CREATE VIEW user_search_dynpriv AS
-
-    SELECT any_id, privilege_id
-    FROM DynRoleMembers drm, SyncopeRole_Privilege rp
-    WHERE drm.role_id = rp.role_id
-  </entry>
-  <entry key="user_search_resource">
-    CREATE VIEW user_search_resource AS
-
-    SELECT st.user_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeUser_ExternalResource st
-  </entry>
-  <entry key="user_search_group_res">
-    CREATE VIEW user_search_group_res AS
-
-    SELECT m.user_id AS any_id, st.resource_id AS resource_id
-    FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- anyObject -->
-  <entry key="anyObject_search">
-    CREATE VIEW anyObject_search AS
- 
-    SELECT a.id as any_id, a.* FROM AnyObject a
-  </entry>
-  <entry key="anyObject_search_unique_attr">
-    CREATE VIEW anyObject_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrUniqueValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_attr">
-    CREATE VIEW anyObject_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_null_attr">
-    CREATE VIEW anyObject_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL::int4 AS booleanvalue,
-    NULL::timestamp AS datevalue,
-    NULL::float8 AS doublevalue,
-    NULL::int8 AS longvalue,
-    NULL AS stringvalue
-    FROM AnyObject u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN APlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="anyObject_search_arelationship">
-    CREATE VIEW anyObject_search_arelationship AS
-
-    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
-    FROM ARelationship m
-  </entry>
-  <entry key="anyObject_search_amembership">
-    CREATE VIEW anyObject_search_amembership AS
-
-    SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM AMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="anyObject_search_resource">
-    CREATE VIEW anyObject_search_resource AS
-
-    SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AnyObject_ExternalResource st
-  </entry>
-  <entry key="anyObject_search_group_res">
-    CREATE VIEW anyObject_search_group_res AS
-
-    SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- group -->
-  <entry key="group_search">
-    CREATE VIEW group_search AS
- 
-    SELECT r.id as any_id, r.* FROM SyncopeGroup r
-  </entry>
-  <entry key="group_search_unique_attr">
-    CREATE VIEW group_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrUniqueValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_attr">
-    CREATE VIEW group_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_null_attr">
-    CREATE VIEW group_search_null_attr AS
-
-    SELECT u.id AS any_id,
-    PlainSchema.id AS schema_id,
-    NULL::int4 AS booleanvalue,
-    NULL::timestamp AS datevalue,
-    NULL::float8 AS doublevalue,
-    NULL::int8 AS longvalue,
-    NULL AS stringvalue
-    FROM SyncopeGroup u CROSS JOIN PlainSchema
-    LEFT OUTER JOIN GPlainAttr ua ON (PlainSchema.id = ua.schema_id AND ua.owner_id = u.id)
-    WHERE ua.id IS NULL
-  </entry>
-  <entry key="group_search_resource">
-    CREATE VIEW group_search_resource AS
-
-    SELECT st.group_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeGroup_ExternalResource st
-  </entry>
-
-</properties>
diff --git a/docker/enduser/pom.xml b/docker/enduser/pom.xml
index 5d0b231..d94a702 100644
--- a/docker/enduser/pom.xml
+++ b/docker/enduser/pom.xml
@@ -50,8 +50,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
diff --git a/docker/enduser/src/main/resources/Dockerfile b/docker/enduser/src/main/resources/Dockerfile
index d0c0792..4ce3a01 100644
--- a/docker/enduser/src/main/resources/Dockerfile
+++ b/docker/enduser/src/main/resources/Dockerfile
@@ -33,6 +33,9 @@
 
 COPY syncope-docker-enduser-*war /opt/syncope/lib/syncope-enduser.war
 
+ENV SPRING_PROFILES_ACTIVE=docker
+ENV LOADER_PATH="/opt/syncope/conf,/opt/syncope/lib"
+
 COPY startup.sh /opt/syncope/bin
 RUN chmod 755 /opt/syncope/bin/startup.sh
 CMD ["/opt/syncope/bin/startup.sh"]
diff --git a/docker/enduser/src/main/resources/application.properties b/docker/enduser/src/main/resources/application.properties
deleted file mode 100644
index 2b3c7b2..0000000
--- a/docker/enduser/src/main/resources/application.properties
+++ /dev/null
@@ -1,32 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} Enduser
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.servlet.encoding.charset=UTF-8
-server.servlet.encoding.enabled=true
-server.servlet.encoding.force=true
-
-server.servlet.contextPath=/syncope-enduser
-
-management.endpoints.web.exposure.include=info,health,loggers
-management.endpoint.health.show-details=ALWAYS
-
-service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
-
-wicket.core.csrf.enabled=false
diff --git a/docker/enduser/src/main/resources/keymaster.properties b/docker/enduser/src/main/resources/enduser-docker.properties
similarity index 81%
rename from docker/enduser/src/main/resources/keymaster.properties
rename to docker/enduser/src/main/resources/enduser-docker.properties
index 14e8ca6..ef5b6d2 100644
--- a/docker/enduser/src/main/resources/keymaster.properties
+++ b/docker/enduser/src/main/resources/enduser-docker.properties
@@ -17,3 +17,10 @@
 keymaster.address=${KEYMASTER_ADDRESS}
 keymaster.username=${KEYMASTER_USERNAME}
 keymaster.password=${KEYMASTER_PASSWORD}
+
+service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
+
+enduser.anonymousUser=${ANONYMOUS_USER:anonymous}
+enduser.anonymousKey=${ANONYMOUS_KEY:anonymousKey}
+
+logging.config=file:///opt/syncope/conf/log4j2.xml
diff --git a/docker/enduser/src/main/resources/startup.sh b/docker/enduser/src/main/resources/startup.sh
index 2fd53ff..42656c6 100755
--- a/docker/enduser/src/main/resources/startup.sh
+++ b/docker/enduser/src/main/resources/startup.sh
@@ -17,6 +17,5 @@
 # specific language governing permissions and limitations
 # under the License.
 
-export LOADER_PATH="/opt/syncope/conf,/opt/syncope/lib"
 java -Dfile.encoding=UTF-8 -server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m \
  -XX:+DisableExplicitGC -Djava.security.egd=file:/dev/./urandom -jar /opt/syncope/lib/syncope-enduser.war
diff --git a/docker/sra/pom.xml b/docker/sra/pom.xml
index 6a80959..e8c1bd6 100644
--- a/docker/sra/pom.xml
+++ b/docker/sra/pom.xml
@@ -45,8 +45,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
diff --git a/docker/sra/src/main/resources/Dockerfile b/docker/sra/src/main/resources/Dockerfile
index e9aab29..f95554f 100644
--- a/docker/sra/src/main/resources/Dockerfile
+++ b/docker/sra/src/main/resources/Dockerfile
@@ -31,6 +31,8 @@
 
 COPY syncope-docker-sra-*jar /opt/syncope/lib/syncope-sra.jar
 
+ENV SPRING_PROFILES_ACTIVE=docker
+
 COPY startup.sh /opt/syncope/bin
 RUN chmod 755 /opt/syncope/bin/startup.sh
 CMD ["/opt/syncope/bin/startup.sh"]
diff --git a/docker/sra/src/main/resources/application.properties b/docker/sra/src/main/resources/application.properties
deleted file mode 100644
index 2e2da44..0000000
--- a/docker/sra/src/main/resources/application.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} SRA
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.port=8080
-
-management.endpoint.gateway.enabled=true
-management.endpoints.web.exposure.include=info,health,loggers,metrics,gateway,sraSessions
-management.endpoint.health.show-details=ALWAYS
-spring.cloud.discovery.client.health-indicator.enabled=false
-
-service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
diff --git a/docker/sra/src/main/resources/keymaster.properties b/docker/sra/src/main/resources/keymaster.properties
deleted file mode 100644
index 14e8ca6..0000000
--- a/docker/sra/src/main/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=${KEYMASTER_ADDRESS}
-keymaster.username=${KEYMASTER_USERNAME}
-keymaster.password=${KEYMASTER_PASSWORD}
diff --git a/docker/console/src/main/resources/keymaster.properties b/docker/sra/src/main/resources/sra-docker.properties
similarity index 81%
copy from docker/console/src/main/resources/keymaster.properties
copy to docker/sra/src/main/resources/sra-docker.properties
index 14e8ca6..8f9e096 100644
--- a/docker/console/src/main/resources/keymaster.properties
+++ b/docker/sra/src/main/resources/sra-docker.properties
@@ -17,3 +17,10 @@
 keymaster.address=${KEYMASTER_ADDRESS}
 keymaster.username=${KEYMASTER_USERNAME}
 keymaster.password=${KEYMASTER_PASSWORD}
+
+service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
+
+sra.anonymousUser=${ANONYMOUS_USER:anonymous}
+sra.anonymousKey=${ANONYMOUS_KEY:anonymousKey}
+
+logging.config=file:///opt/syncope/conf/log4j2.xml
diff --git a/docker/sra/src/main/resources/sra.properties b/docker/sra/src/main/resources/sra.properties
deleted file mode 100644
index 77cb93e..0000000
--- a/docker/sra/src/main/resources/sra.properties
+++ /dev/null
@@ -1,20 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-
-useGZIPCompression=true
diff --git a/docker/src/main/resources/docker-compose/docker-compose-all.yml b/docker/src/main/resources/docker-compose/docker-compose-all.yml
index 78bb996..65b7247 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-all.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-all.yml
@@ -24,7 +24,7 @@
 
 services:
    keymaster:
-     image: zookeeper:3.5.6
+     image: zookeeper:3.7.0
      restart: always
 
    db:
@@ -44,7 +44,7 @@
        - "18080:8080"
      restart: always
      environment:
-       DBMS: postgresql
+       SPRING_PROFILES_ACTIVE: docker,postgresql
        DB_URL: jdbc:postgresql://db:5432/syncope
        DB_USER: syncope
        DB_PASSWORD: syncope
@@ -65,6 +65,7 @@
        - "28080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: keymaster:2181
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
@@ -79,6 +80,7 @@
        - "38080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: keymaster:2181
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
@@ -93,10 +95,12 @@
        - "48080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: keymaster:2181
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-wa:8080/syncope-wa/
+       CAS_SERVER_NAME: http://localhost:48080
 
    syncope-sra:
      depends_on:
@@ -107,6 +111,7 @@
        - "58080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: keymaster:2181
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-ha.yml b/docker/src/main/resources/docker-compose/docker-compose-ha.yml
index 8abe5af..82bae44 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-ha.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-ha.yml
@@ -37,7 +37,7 @@
        - "18080:8080"
      restart: always
      environment:
-       DBMS: postgresql
+       SPRING_PROFILES_ACTIVE: docker,postgresql
        DB_URL: jdbc:postgresql://db:5432/syncope
        DB_USER: syncope
        DB_PASSWORD: syncope
@@ -48,6 +48,8 @@
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope1:8080/syncope/rest/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope2:
      depends_on:
@@ -57,7 +59,7 @@
        - "18081:8080"
      restart: always
      environment:
-       DBMS: postgresql
+       SPRING_PROFILES_ACTIVE: docker,postgresql
        DB_URL: jdbc:postgresql://db:5432/syncope
        DB_USER: syncope
        DB_PASSWORD: syncope
@@ -68,6 +70,8 @@
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope2:8080/syncope/rest/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-console:
      depends_on:
@@ -77,10 +81,13 @@
        - "28080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope1:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-console:8080/syncope-console/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-enduser:
      depends_on:
@@ -90,7 +97,10 @@
        - "38080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope1:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml b/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
index 4f353e0..663b049 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
@@ -21,7 +21,7 @@
 
 services:
    db:
-     image: mariadb:10.5
+     image: mariadb:10.6
      restart: always
      environment:
        MYSQL_ROOT_PASSWORD: password
@@ -37,7 +37,7 @@
        - "18080:8080"
      restart: always
      environment:
-       DBMS: mariadb
+       SPRING_PROFILES_ACTIVE: docker,mariadb
        DB_URL: jdbc:mysql://db:3306/syncope?characterEncoding=UTF-8&relaxAutoCommit=true&useSSL=false
        DB_USER: syncope
        DB_PASSWORD: syncope
@@ -48,6 +48,8 @@
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope:8080/syncope/rest/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-console:
      depends_on:
@@ -57,10 +59,13 @@
        - "28080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-console:8080/syncope-console/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-enduser:
      depends_on:
@@ -70,7 +75,10 @@
        - "38080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-myjson.yml b/docker/src/main/resources/docker-compose/docker-compose-myjson.yml
index 91df141..bfe4f7e 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-myjson.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-myjson.yml
@@ -38,7 +38,8 @@
        - "18080:8080"
      restart: always
      environment:
-       DBMS: myjson
+       SPRING_PROFILES_ACTIVE: docker,myjson
+       LOADER_PATH: "/opt/syncope/conf,/opt/syncope/lib,/opt/syncope/jpa-json"
        DB_URL: jdbc:mysql://db:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
        DB_USER: syncope
        DB_PASSWORD: syncope
@@ -49,6 +50,8 @@
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope:8080/syncope/rest/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-console:
      depends_on:
@@ -58,10 +61,13 @@
        - "28080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-console:8080/syncope-console/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-enduser:
      depends_on:
@@ -71,7 +77,10 @@
        - "38080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-mysql.yml b/docker/src/main/resources/docker-compose/docker-compose-mysql.yml
index 56dc133..83b6b7c 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-mysql.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-mysql.yml
@@ -38,7 +38,7 @@
        - "18080:8080"
      restart: always
      environment:
-       DBMS: mysql
+       SPRING_PROFILES_ACTIVE: docker,mysql
        DB_URL: jdbc:mysql://db:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
        DB_USER: syncope
        DB_PASSWORD: syncope
@@ -49,6 +49,8 @@
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope:8080/syncope/rest/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-console:
      depends_on:
@@ -58,10 +60,13 @@
        - "28080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-console:8080/syncope-console/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-enduser:
      depends_on:
@@ -71,7 +76,10 @@
        - "38080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-pgjsonb.yml b/docker/src/main/resources/docker-compose/docker-compose-pgjsonb.yml
index a48a6e9..77e2f2a 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-pgjsonb.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-pgjsonb.yml
@@ -36,7 +36,8 @@
        - "18080:8080"
      restart: always
      environment:
-       DBMS: pgjsonb
+       SPRING_PROFILES_ACTIVE: docker,pgjsonb
+       LOADER_PATH: "/opt/syncope/conf,/opt/syncope/lib,/opt/syncope/jpa-json"
        DB_URL: jdbc:postgresql://db:5432/syncope?stringtype=unspecified
        DB_USER: syncope
        DB_PASSWORD: syncope
@@ -47,6 +48,8 @@
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope:8080/syncope/rest/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-console:
      depends_on:
@@ -56,10 +59,13 @@
        - "28080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-console:8080/syncope-console/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-enduser:
      depends_on:
@@ -69,7 +75,10 @@
        - "38080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml b/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
index c73fcb4..8abc498 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
@@ -36,7 +36,7 @@
        - "18080:8080"
      restart: always
      environment:
-       DBMS: postgresql
+       SPRING_PROFILES_ACTIVE: docker,postgresql
        DB_URL: jdbc:postgresql://db:5432/syncope
        DB_USER: syncope
        DB_PASSWORD: syncope
@@ -47,6 +47,8 @@
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope:8080/syncope/rest/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-console:
      depends_on:
@@ -56,10 +58,13 @@
        - "28080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-console:8080/syncope-console/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-enduser:
      depends_on:
@@ -69,7 +74,10 @@
        - "38080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-mssql.yml b/docker/src/main/resources/docker-compose/docker-compose-sqlserver.yml
similarity index 87%
rename from docker/src/main/resources/docker-compose/docker-compose-mssql.yml
rename to docker/src/main/resources/docker-compose/docker-compose-sqlserver.yml
index 0dc7717..c3d5fbc 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-mssql.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-sqlserver.yml
@@ -38,7 +38,7 @@
        - "18080:8080"
      restart: always
      environment:
-       DBMS: mssql
+       SPRING_PROFILES_ACTIVE: docker,sqlserver
        DB_URL: jdbc:sqlserver://db:1433;databaseName=syncope
        DB_SCHEMA: dbo
        DB_USER: syncope
@@ -50,6 +50,8 @@
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope:8080/syncope/rest/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-console:
      depends_on:
@@ -59,10 +61,13 @@
        - "28080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-console:8080/syncope-console/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
 
    syncope-enduser:
      depends_on:
@@ -72,7 +77,10 @@
        - "38080:8080"
      restart: always
      environment:
+       SPRING_PROFILES_ACTIVE: docker
        KEYMASTER_ADDRESS: http://syncope:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
        SERVICE_DISCOVERY_ADDRESS: http://syncope-enduser:8080/syncope-enduser/
+       ANONYMOUS_USER: ${ANONYMOUS_USER}
+       ANONYMOUS_KEY: ${ANONYMOUS_KEY}
diff --git a/docker/wa/pom.xml b/docker/wa/pom.xml
index faeb8bc..f2acf94 100644
--- a/docker/wa/pom.xml
+++ b/docker/wa/pom.xml
@@ -50,8 +50,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
       <version>${project.version}</version>
     </dependency>
     <dependency>
diff --git a/docker/wa/src/main/resources/Dockerfile b/docker/wa/src/main/resources/Dockerfile
index 584fe11..7aa71e4 100644
--- a/docker/wa/src/main/resources/Dockerfile
+++ b/docker/wa/src/main/resources/Dockerfile
@@ -32,6 +32,8 @@
 
 COPY syncope-docker-wa-*war /opt/syncope/lib/syncope-wa.war
 
+ENV SPRING_PROFILES_ACTIVE=docker
+
 COPY startup.sh /opt/syncope/bin
 RUN chmod 755 /opt/syncope/bin/startup.sh
 CMD ["/opt/syncope/bin/startup.sh"]
diff --git a/docker/wa/src/main/resources/application.properties b/docker/wa/src/main/resources/application.properties
deleted file mode 100644
index b8b2171..0000000
--- a/docker/wa/src/main/resources/application.properties
+++ /dev/null
@@ -1,58 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} WA
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.port=8080
-
-server.servlet.encoding.charset=UTF-8
-server.servlet.encoding.enabled=true
-server.servlet.encoding.force=true
-
-server.servlet.contextPath=/syncope-wa
-
-server.servlet.session.timeout=300
-server.servlet.session.cookie.http-only=true
-server.servlet.session.tracking-modes=COOKIE
-
-spring.web.resources.static-locations=classpath:/thymeleaf/static,classpath:/syncope/static,classpath:/static
-
-cas.monitor.endpoints.endpoint.defaults.access=AUTHENTICATED
-management.endpoints.enabled-by-default=true
-management.endpoints.web.exposure.include=info,health,loggers,ssoSessions,registeredServices
-management.endpoint.health.show-details=ALWAYS
-spring.cloud.discovery.client.health-indicator.enabled=false
-
-# Cache service definitions for 5 minutes
-cas.service-registry.cache.duration=PT5M
-
-# Reload services and hydrate the cache every 5 minutes
-cas.service-registry.schedule.repeat-interval=PT5M
-cas.service-registry.schedule.start-delay=PT30S
-
-cas.events.core.enabled=false
-
-##
-# Allow configuration classes to override bean definitions from Spring Boot
-#
-spring.main.allow-bean-definition-overriding=true
-spring.main.lazy-initialization=false
-
-service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
-
-version=${syncope.version}
diff --git a/docker/wa/src/main/resources/keymaster.properties b/docker/wa/src/main/resources/keymaster.properties
deleted file mode 100644
index 14e8ca6..0000000
--- a/docker/wa/src/main/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=${KEYMASTER_ADDRESS}
-keymaster.username=${KEYMASTER_USERNAME}
-keymaster.password=${KEYMASTER_PASSWORD}
diff --git a/docker/console/src/main/resources/keymaster.properties b/docker/wa/src/main/resources/wa-docker.properties
similarity index 68%
copy from docker/console/src/main/resources/keymaster.properties
copy to docker/wa/src/main/resources/wa-docker.properties
index 14e8ca6..2387727 100644
--- a/docker/console/src/main/resources/keymaster.properties
+++ b/docker/wa/src/main/resources/wa-docker.properties
@@ -17,3 +17,16 @@
 keymaster.address=${KEYMASTER_ADDRESS}
 keymaster.username=${KEYMASTER_USERNAME}
 keymaster.password=${KEYMASTER_PASSWORD}
+
+cas.server.name=${CAS_SERVER_NAME}
+
+conf.directory=/opt/syncope/conf
+cas.standalone.configuration-directory=${conf.directory}
+cas.authn.saml-idp.metadata.http.metadata-backup-location=file:${conf.directory}/saml
+
+service.discovery.address=${SERVICE_DISCOVERY_ADDRESS}
+
+wa.anonymousUser=${ANONYMOUS_USER:anonymous}
+wa.anonymousKey=${ANONYMOUS_KEY:anonymousKey}
+
+logging.config=file:///opt/syncope/conf/log4j2.xml
diff --git a/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/init/CamelRouteLoader.java b/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/init/CamelRouteLoader.java
index ec82cf6..b522f7d 100644
--- a/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/init/CamelRouteLoader.java
+++ b/ext/camel/logic/src/main/java/org/apache/syncope/core/logic/init/CamelRouteLoader.java
@@ -32,7 +32,6 @@
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.CamelEntitlement;
 import org.apache.syncope.common.lib.types.EntitlementsHolder;
-import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
 import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
 import org.apache.syncope.core.persistence.api.entity.CamelRoute;
 import org.slf4j.Logger;
@@ -74,13 +73,13 @@
     }
 
     @javax.annotation.Resource(name = "userRoutes")
-    private ResourceWithFallbackLoader userRoutesLoader;
+    private Resource userRoutes;
 
     @javax.annotation.Resource(name = "groupRoutes")
-    private ResourceWithFallbackLoader groupRoutesLoader;
+    private Resource groupRoutes;
 
     @javax.annotation.Resource(name = "anyObjectRoutes")
-    private ResourceWithFallbackLoader anyObjectRoutesLoader;
+    private Resource anyObjectRoutes;
 
     @Override
     public int getOrder() {
@@ -94,9 +93,9 @@
 
     @Override
     public void load(final String domain, final DataSource datasource) {
-        loadRoutes(domain, datasource, userRoutesLoader.getResource(), AnyTypeKind.USER);
-        loadRoutes(domain, datasource, groupRoutesLoader.getResource(), AnyTypeKind.GROUP);
-        loadRoutes(domain, datasource, anyObjectRoutesLoader.getResource(), AnyTypeKind.ANY_OBJECT);
+        loadRoutes(domain, datasource, userRoutes, AnyTypeKind.USER);
+        loadRoutes(domain, datasource, groupRoutes, AnyTypeKind.GROUP);
+        loadRoutes(domain, datasource, anyObjectRoutes, AnyTypeKind.ANY_OBJECT);
     }
 
     private static String nodeToString(final Node content, final DOMImplementationLS domImpl) {
@@ -130,7 +129,7 @@
     }
 
     private static void loadRoutes(
-        final String domain, final DataSource dataSource, final Resource resource, final AnyTypeKind anyTypeKind) {
+            final String domain, final DataSource dataSource, final Resource resource, final AnyTypeKind anyTypeKind) {
 
         JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
         boolean shouldLoadRoutes = jdbcTemplate.queryForList(
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/ProvisioningCamelContext.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/ProvisioningCamelContext.java
index 3f5b12b..45540d9 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/ProvisioningCamelContext.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/ProvisioningCamelContext.java
@@ -18,40 +18,28 @@
  */
 package org.apache.syncope.core.provisioning.camel;
 
-import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
 
 @ComponentScan("org.apache.syncope.core.provisioning.camel")
 @Configuration
 public class ProvisioningCamelContext {
 
-    @Value("${camel.directory}")
-    private String camelDirectory;
-
     @Bean
-    public ResourceWithFallbackLoader userRoutes() {
-        ResourceWithFallbackLoader routes = new ResourceWithFallbackLoader();
-        routes.setPrimary("file:" + camelDirectory + "/userRoutes.xml");
-        routes.setFallback("classpath:userRoutes.xml");
-        return routes;
+    public Resource userRoutes() {
+        return new ClassPathResource("userRoutes.xml");
     }
 
     @Bean
-    public ResourceWithFallbackLoader groupRoutes() {
-        ResourceWithFallbackLoader routes = new ResourceWithFallbackLoader();
-        routes.setPrimary("file:" + camelDirectory + "/groupRoutes.xml");
-        routes.setFallback("classpath:groupRoutes.xml");
-        return routes;
+    public Resource groupRoutes() {
+        return new ClassPathResource("groupRoutes.xml");
     }
 
     @Bean
-    public ResourceWithFallbackLoader anyObjectRoutes() {
-        ResourceWithFallbackLoader routes = new ResourceWithFallbackLoader();
-        routes.setPrimary("file:" + camelDirectory + "/anyObjectRoutes.xml");
-        routes.setFallback("classpath:anyObjectRoutes.xml");
-        return routes;
+    public Resource anyObjectRoutes() {
+        return new ClassPathResource("anyObjectRoutes.xml");
     }
 }
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/ext/camel/provisioning-camel/src/main/resources/core-camel.properties
similarity index 69%
copy from ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to ext/camel/provisioning-camel/src/main/resources/core-camel.properties
index 450ff50..79038ad 100644
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/ext/camel/provisioning-camel/src/main/resources/core-camel.properties
@@ -14,5 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-userDetails=User details
-userForm=Edit User
+
+provisioning.userProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelUserProvisioningManager
+provisioning.groupProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelGroupProvisioningManager
+provisioning.anyObjectProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelAnyObjectProvisioningManager
diff --git a/ext/camel/provisioning-camel/src/main/resources/provisioning.properties b/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
deleted file mode 100644
index 65c9058..0000000
--- a/ext/camel/provisioning-camel/src/main/resources/provisioning.properties
+++ /dev/null
@@ -1,38 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-camel.directory=${conf.directory}
-
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
-quartz.sql=tables_postgres.sql
-quartz.disableInstance=false
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/core-elasticsearch.properties
similarity index 88%
rename from ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
rename to ext/elasticsearch/persistence-jpa/src/main/resources/core-elasticsearch.properties
index 450ff50..3360988 100644
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/ext/elasticsearch/persistence-jpa/src/main/resources/core-elasticsearch.properties
@@ -14,5 +14,5 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-userDetails=User details
-userForm=Edit User
+
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO
diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
deleted file mode 100644
index 6cbab38..0000000
--- a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO
-any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditConfDAO
-openjpa.RemoteCommitProvider=sjvm
diff --git a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/Flowable.java b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/Flowable.java
index ae6f9d2..6037f05 100644
--- a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/Flowable.java
+++ b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/pages/Flowable.java
@@ -78,15 +78,14 @@
             protected void populateItem(final Item<UserRequest> item) {
                 final UserRequest userRequest = item.getModelObject();
                 item.add(new Accordion("userRequestDetails", Collections.<ITab>singletonList(new AbstractTab(
-                    new StringResourceModel("user.requests.accordion", container, Model.of(userRequest))) {
+                        new StringResourceModel("user.requests.accordion", container, Model.of(userRequest))) {
 
                     private static final long serialVersionUID = 1037272333056449378L;
 
                     @Override
                     public WebMarkupContainer getPanel(final String panelId) {
                         // find the form associated to the current request, if any
-                        return new UserRequestDetails(
-                            panelId, userRequest, container, notificationPanel, getPageReference());
+                        return new UserRequestDetails(panelId, userRequest, container, notificationPanel);
                     }
                 }), Model.of(-1)).setOutputMarkupId(true));
             }
@@ -109,7 +108,7 @@
                     } catch (Exception e) {
                         LOG.error("Unable to start bpmnProcess [{}]", bpmnProcessModel.getObject(), e);
                         SyncopeEnduserSession.get()
-                            .error(String.format("Unable to start bpmnProcess [%s]", e.getMessage()));
+                                .error(String.format("Unable to start bpmnProcess [%s]", e.getMessage()));
                         notificationPanel.refresh(target);
                     }
                     target.add(container);
diff --git a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserRequestDetails.java b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserRequestDetails.java
index cc3bf6b..42701db 100644
--- a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserRequestDetails.java
+++ b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserRequestDetails.java
@@ -28,7 +28,6 @@
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.ExecStatus;
 import org.apache.syncope.ext.client.common.ui.panels.UserRequestFormPanel;
-import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
@@ -52,8 +51,7 @@
             final String id,
             final UserRequest userRequest,
             final WebMarkupContainer container,
-            final NotificationPanel notificationPanel,
-            final PageReference pageRef) {
+            final NotificationPanel notificationPanel) {
 
         super(id);
 
@@ -70,7 +68,7 @@
         } else {
             Form<Void> form = new Form<>("userRequestWrapForm");
 
-            form.add(new UserRequestFormPanel("userRequestFormPanel", pageRef, formTO, false) {
+            form.add(new UserRequestFormPanel("userRequestFormPanel", formTO, false) {
 
                 private static final long serialVersionUID = 3617895525072546591L;
 
diff --git a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.java b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.java
deleted file mode 100644
index c93fefd..0000000
--- a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.ext.client.common.ui.panels;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.math.NumberUtils;
-import org.apache.commons.lang3.time.FastDateFormat;
-import org.apache.syncope.client.ui.commons.MapChoiceRenderer;
-import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
-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.markup.html.form.FieldPanel;
-import org.apache.syncope.common.lib.to.UserRequestForm;
-import org.apache.syncope.common.lib.to.UserRequestFormProperty;
-import org.apache.syncope.common.lib.to.UserRequestFormPropertyValue;
-import org.apache.syncope.common.lib.types.IdRepoEntitlement;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
-import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.LoadableDetachableModel;
-import org.apache.wicket.model.PropertyModel;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.stream.Collectors;
-
-public abstract class UserRequestFormPanel extends Panel {
-
-    private static final long serialVersionUID = -8847854414429745216L;
-
-    protected static final Logger LOG = LoggerFactory.getLogger(UserRequestFormPanel.class);
-
-    public UserRequestFormPanel(final String id, final PageReference pageRef, final UserRequestForm form) {
-        this(id, pageRef, form, true);
-    }
-
-    public UserRequestFormPanel(final String id, final PageReference pageRef, final UserRequestForm form,
-            final boolean showDetails) {
-        super(id);
-
-        IModel<List<UserRequestFormProperty>> formProps = new LoadableDetachableModel<>() {
-
-            private static final long serialVersionUID = 3169142472626817508L;
-
-            @Override
-            protected List<UserRequestFormProperty> load() {
-                return form.getProperties();
-            }
-        };
-
-        ListView<UserRequestFormProperty> propView = new ListView<>("propView", formProps) {
-
-            private static final long serialVersionUID = 9101744072914090143L;
-
-            @Override
-            @SuppressWarnings({"unchecked", "rawtypes"})
-            protected void populateItem(final ListItem<UserRequestFormProperty> item) {
-                final UserRequestFormProperty prop = item.getModelObject();
-
-                String label = StringUtils.isBlank(prop.getName()) ? prop.getId() : prop.getName();
-
-                FieldPanel field;
-                switch (prop.getType()) {
-                    case Boolean:
-                        field = new AjaxDropDownChoicePanel("value", label, new PropertyModel<String>(prop, "value") {
-
-                            private static final long serialVersionUID = -3743432456095828573L;
-
-                            @Override
-                            public String getObject() {
-                                return StringUtils.isBlank(prop.getValue())
-                                    ? null
-                                    : prop.getValue().equals("true") ? "Yes" : "No";
-                            }
-
-                            @Override
-                            public void setObject(final String object) {
-                                prop.setValue(String.valueOf(object.equalsIgnoreCase("yes")));
-                            }
-
-                        }, false).setChoices(Arrays.asList(new String[]{"Yes", "No"}));
-                        break;
-
-                    case Date:
-                        FastDateFormat formatter = FastDateFormat.getInstance(prop.getDatePattern());
-                        field = new AjaxDateTimeFieldPanel("value", label, new PropertyModel<>(prop, "value") {
-
-                            private static final long serialVersionUID = -3743432456095828573L;
-
-                            @Override
-                            public Date getObject() {
-                                try {
-                                    return StringUtils.isBlank(prop.getValue())
-                                        ? null
-                                        : formatter.parse(prop.getValue());
-                                } catch (ParseException e) {
-                                    LOG.error("Unparsable date: {}", prop.getValue(), e);
-                                    return null;
-                                }
-                            }
-
-                            @Override
-                            public void setObject(final Date object) {
-                                prop.setValue(formatter.format(object));
-                            }
-
-                        }, formatter);
-                        break;
-
-                    case Enum:
-                        field = new AjaxDropDownChoicePanel(
-                            "value", label, new PropertyModel<String>(prop, "value"), false).
-                            setChoiceRenderer(new MapChoiceRenderer(prop.getEnumValues().stream().
-                                collect(Collectors.toMap(
-                                    UserRequestFormPropertyValue::getKey,
-                                    UserRequestFormPropertyValue::getValue)))).
-                            setChoices(new ArrayList<>(prop.getEnumValues().stream().
-                                map(UserRequestFormPropertyValue::getKey).collect(Collectors.toList())));
-                        break;
-
-                    case Dropdown:
-                        field = new AjaxDropDownChoicePanel(
-                            "value", label, new PropertyModel<String>(prop, "value"), false).
-                            setChoiceRenderer(new MapChoiceRenderer(prop.getDropdownValues().stream().
-                                collect(Collectors.toMap(
-                                    UserRequestFormPropertyValue::getKey,
-                                    UserRequestFormPropertyValue::getValue)))).
-                            setChoices(prop.getDropdownValues().stream().
-                                map(UserRequestFormPropertyValue::getKey).collect(Collectors.toList()));
-                        break;
-
-                    case Long:
-                        field = new AjaxSpinnerFieldPanel.Builder<Long>().build(
-                            "value",
-                            label,
-                            Long.class,
-                            new PropertyModel<>(prop, "value") {
-
-                                private static final long serialVersionUID = -7688359318035249200L;
-
-                                @Override
-                                public Long getObject() {
-                                    return StringUtils.isBlank(prop.getValue())
-                                        ? null
-                                        : NumberUtils.toLong(prop.getValue());
-                                }
-
-                                @Override
-                                public void setObject(final Long object) {
-                                    prop.setValue(String.valueOf(object));
-                                }
-                            });
-                        break;
-
-                    case Password:
-                        field = new AjaxPasswordFieldPanel("value", label, new PropertyModel<>(prop, "value"), false);
-                        break;
-
-                    case String:
-                    default:
-                        field = new AjaxTextFieldPanel("value", label, new PropertyModel<>(prop, "value"), false);
-                        break;
-                }
-
-                field.setReadOnly(!prop.isWritable());
-                if (prop.isRequired()) {
-                    field.addRequiredLabel();
-                }
-
-                item.add(field);
-            }
-        };
-
-        AjaxLink<String> userDetails = new AjaxLink<>("userDetails") {
-
-            private static final long serialVersionUID = -4804368561204623354L;
-
-            @Override
-            public void onClick(final AjaxRequestTarget target) {
-                viewDetails(target);
-            }
-        };
-        MetaDataRoleAuthorizationStrategy.authorize(userDetails, ENABLE, IdRepoEntitlement.USER_READ);
-
-        boolean enabled = form.getUserTO() != null;
-        userDetails.setVisible(enabled && showDetails).setEnabled(enabled);
-
-        add(propView);
-        add(userDetails);
-    }
-
-    protected abstract void viewDetails(AjaxRequestTarget target);
-}
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html b/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
deleted file mode 100644
index 7a9b26b..0000000
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html>
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <wicket:panel>
-    <div wicket:id="propView">
-      <span wicket:id="value">[value]</span>
-    </div>
-
-    <div style="margin: 20px 0">
-      <a href="#" alt="user details" class="btn btn-success btn-circle btn-lg" wicket:id="userDetails" wicket:message="title:userDetails">
-        <i class="fas fa-eye"></i>
-      </a>
-    </div>
-  </wicket:panel>
-</html>
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_it.properties b/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_it.properties
deleted file mode 100644
index 92c475d..0000000
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_it.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-userDetails=Dettagli utente
-userForm=Modifica utente
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
deleted file mode 100644
index 5a9cc2d..0000000
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_pt_BR.properties b/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_pt_BR.properties
deleted file mode 100644
index 00a8971..0000000
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_pt_BR.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-userDetails=Detalhes do Usu\u00e1rio
-userForm=Detalhes do Usu\u00e1rio
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ru.properties b/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ru.properties
deleted file mode 100644
index 89dd721..0000000
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ru.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-userDetails=\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435
-userForm=\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/WorkflowFlowableContext.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/WorkflowFlowableContext.java
index 049294b..4d28490 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/WorkflowFlowableContext.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/WorkflowFlowableContext.java
@@ -26,39 +26,40 @@
 import org.apache.syncope.core.flowable.support.SyncopeEntitiesVariableType;
 import org.apache.syncope.core.flowable.support.SyncopeFormHandlerHelper;
 import org.apache.syncope.core.flowable.support.SyncopeIdmIdentityService;
-import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
 import org.apache.syncope.core.workflow.java.WorkflowContext;
 import org.flowable.common.engine.impl.AbstractEngineConfiguration;
 import org.flowable.common.engine.impl.cfg.IdGenerator;
-import org.flowable.common.engine.impl.history.HistoryLevel;
 import org.flowable.common.engine.impl.persistence.StrongUuidGenerator;
 import org.flowable.idm.spring.SpringIdmEngineConfiguration;
 import org.flowable.idm.spring.configurator.SpringIdmEngineConfigurator;
 import org.flowable.spring.SpringProcessEngineConfiguration;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.Scope;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
 
 @Import(WorkflowContext.class)
 @ComponentScan("org.apache.syncope.core.flowable")
+@EnableConfigurationProperties(WorkflowFlowableProperties.class)
 @Configuration
 public class WorkflowFlowableContext {
 
     @Autowired
+    private WorkflowFlowableProperties props;
+
+    @Autowired
+    private ResourceLoader resourceLoader;
+
+    @Autowired
     private ConfigurableApplicationContext ctx;
 
-    @Value("${wf.directory}")
-    private String wfDirectory;
-
-    @Value("${historyLevel:ACTIVITY}")
-    private HistoryLevel historyLevel;
-
     @ConditionalOnMissingBean
     @Bean
     public SpringIdmEngineConfiguration syncopeIdmEngineConfiguration() {
@@ -119,7 +120,7 @@
         conf.setDatabaseSchemaUpdate(AbstractEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
         conf.setJpaHandleTransaction(true);
         conf.setJpaCloseEntityManager(false);
-        conf.setHistoryLevel(historyLevel);
+        conf.setHistoryLevel(props.getHistoryLevel());
         conf.setIdmEngineConfigurator(syncopeIdmEngineConfigurator());
         conf.setCustomPreVariableTypes(List.of(syncopeEntitiesVariableType()));
         conf.setFormHandlerHelper(syncopeFormHandlerHelper());
@@ -129,10 +130,7 @@
     }
 
     @Bean
-    public ResourceWithFallbackLoader userWorkflowDef() {
-        ResourceWithFallbackLoader userWorkflowDef = new ResourceWithFallbackLoader();
-        userWorkflowDef.setPrimary("file:" + wfDirectory + "/userWorkflow.bpmn20.xml");
-        userWorkflowDef.setFallback("classpath:userWorkflow.bpmn20.xml");
-        return userWorkflowDef;
+    public Resource userWorkflowDef() {
+        return resourceLoader.getResource(props.getUserWorkflowDef());
     }
 }
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/WorkflowFlowableProperties.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/WorkflowFlowableProperties.java
new file mode 100644
index 0000000..6f26148
--- /dev/null
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/WorkflowFlowableProperties.java
@@ -0,0 +1,46 @@
+/*
+ * 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.flowable;
+
+import org.flowable.common.engine.impl.history.HistoryLevel;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("workflow.flowable")
+public class WorkflowFlowableProperties {
+
+    private HistoryLevel historyLevel = HistoryLevel.ACTIVITY;
+
+    private String userWorkflowDef = "classpath:/userWorkflow.bpmn20.xml";
+
+    public HistoryLevel getHistoryLevel() {
+        return historyLevel;
+    }
+
+    public void setHistoryLevel(final HistoryLevel historyLevel) {
+        this.historyLevel = historyLevel;
+    }
+
+    public String getUserWorkflowDef() {
+        return userWorkflowDef;
+    }
+
+    public void setUserWorkflowDef(final String userWorkflowDef) {
+        this.userWorkflowDef = userWorkflowDef;
+    }
+}
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/ext/flowable/flowable-bpmn/src/main/resources/core-flowable.properties
similarity index 79%
copy from ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to ext/flowable/flowable-bpmn/src/main/resources/core-flowable.properties
index 450ff50..7585e60 100644
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/ext/flowable/flowable-bpmn/src/main/resources/core-flowable.properties
@@ -14,5 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-userDetails=User details
-userForm=Edit User
+
+workflow.uwfAdapter=org.apache.syncope.core.flowable.impl.FlowableUserWorkflowAdapter
+workflow.flowable.historyLevel=ACTIVITY
+workflow.flowable.userWorkflowDef=classpath:/userWorkflow.bpmn20.xml
diff --git a/ext/flowable/flowable-bpmn/src/main/resources/workflow.properties b/ext/flowable/flowable-bpmn/src/main/resources/workflow.properties
deleted file mode 100644
index be8a7bb..0000000
--- a/ext/flowable/flowable-bpmn/src/main/resources/workflow.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-wf.directory=${conf.directory}
-historyLevel=activity
-uwfAdapter=org.apache.syncope.core.flowable.impl.FlowableUserWorkflowAdapter
-gwfAdapter=org.apache.syncope.core.workflow.java.DefaultGroupWorkflowAdapter
-awfAdapter=org.apache.syncope.core.workflow.java.DefaultAnyObjectWorkflowAdapter
diff --git a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/init/FlowableLoader.java b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/init/FlowableLoader.java
index 333c91d..172de77 100644
--- a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/init/FlowableLoader.java
+++ b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/init/FlowableLoader.java
@@ -22,7 +22,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
-import javax.annotation.Resource;
 import javax.sql.DataSource;
 import org.apache.commons.io.IOUtils;
 import org.apache.syncope.common.lib.types.EntitlementsHolder;
@@ -31,13 +30,13 @@
 import org.apache.syncope.core.flowable.impl.FlowableRuntimeUtils;
 import org.apache.syncope.core.flowable.support.DomainProcessEngine;
 import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
-import org.apache.syncope.core.spring.ResourceWithFallbackLoader;
 import org.flowable.engine.ProcessEngine;
 import org.flowable.engine.impl.db.DbIdGenerator;
 import org.flowable.engine.repository.ProcessDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -45,8 +44,8 @@
 
     private static final Logger LOG = LoggerFactory.getLogger(FlowableLoader.class);
 
-    @Resource(name = "userWorkflowDef")
-    private ResourceWithFallbackLoader userWorkflowDef;
+    @javax.annotation.Resource(name = "userWorkflowDef")
+    private Resource userWorkflowDef;
 
     @Autowired
     private DomainProcessEngine dpEngine;
@@ -65,10 +64,10 @@
     public void load(final String domain, final DataSource datasource) {
         byte[] wfDef = new byte[0];
 
-        try (InputStream wfIn = userWorkflowDef.getResource().getInputStream()) {
+        try (InputStream wfIn = userWorkflowDef.getInputStream()) {
             wfDef = IOUtils.toByteArray(wfIn);
         } catch (IOException e) {
-            LOG.error("While loading " + userWorkflowDef.getResource().getFilename(), e);
+            LOG.error("While loading " + userWorkflowDef.getFilename(), e);
         }
 
         ProcessEngine processEngine = dpEngine.getEngines().get(domain);
@@ -83,7 +82,7 @@
             // Only loads process definition from file if not found in repository
             if (processes.isEmpty()) {
                 processEngine.getRepositoryService().createDeployment().addInputStream(
-                        userWorkflowDef.getResource().getFilename(), new ByteArrayInputStream(wfDef)).deploy();
+                        userWorkflowDef.getFilename(), new ByteArrayInputStream(wfDef)).deploy();
 
                 ProcessDefinition procDef = processEngine.getRepositoryService().createProcessDefinitionQuery().
                         processDefinitionKey(FlowableRuntimeUtils.WF_PROCESS_ID).latestVersion().
diff --git a/ext/pom.xml b/ext/pom.xml
index 67cc21e..1fb1222 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -83,7 +83,6 @@
     <module>oidcc4ui</module>
     <module>elasticsearch</module>
     <module>scimv2</module>
-    <module>self-keymaster</module>
   </modules>
 
 </project>
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UIContext.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UIContext.java
new file mode 100644
index 0000000..ca6bdfc
--- /dev/null
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UIContext.java
@@ -0,0 +1,27 @@
+/*
+ * 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 org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@EnableConfigurationProperties(SAML2SP4UIProperties.class)
+@Configuration
+public class SAML2SP4UIContext {
+}
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UIProperties.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UIProperties.java
new file mode 100644
index 0000000..a589893
--- /dev/null
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UIProperties.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("saml2.sp4ui")
+public class SAML2SP4UIProperties {
+
+    private String keystore;
+
+    private String keystoreType;
+
+    private String keystoreStorepass;
+
+    private String keystoreKeypass;
+
+    private long skew;
+
+    public String getKeystore() {
+        return keystore;
+    }
+
+    public void setKeystore(final String keystore) {
+        this.keystore = keystore;
+    }
+
+    public String getKeystoreType() {
+        return keystoreType;
+    }
+
+    public void setKeystoreType(final String keystoreType) {
+        this.keystoreType = keystoreType;
+    }
+
+    public String getKeystoreStorepass() {
+        return keystoreStorepass;
+    }
+
+    public void setKeystoreStorepass(final String keystoreStorepass) {
+        this.keystoreStorepass = keystoreStorepass;
+    }
+
+    public String getKeystoreKeypass() {
+        return keystoreKeypass;
+    }
+
+    public void setKeystoreKeypass(final String keystoreKeypass) {
+        this.keystoreKeypass = keystoreKeypass;
+    }
+
+    public long getSkew() {
+        return skew;
+    }
+
+    public void setSkew(final long skew) {
+        this.skew = skew;
+    }
+}
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SP4UILoader.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SP4UILoader.java
index 4f0c2ee..ce7eded 100644
--- a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SP4UILoader.java
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SP4UILoader.java
@@ -22,18 +22,15 @@
 import java.security.KeyStore;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
-import java.util.Properties;
-import org.apache.syncope.common.lib.PropertyUtils;
 import org.apache.syncope.common.lib.types.EntitlementsHolder;
 import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
 import org.apache.syncope.common.lib.types.SAML2SP4UIEntitlement;
 import org.apache.syncope.common.lib.types.SAML2SP4UIImplementationType;
+import org.apache.syncope.core.logic.SAML2SP4UIProperties;
 import org.apache.syncope.core.logic.saml2.NoOpLogoutHandler;
 import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
 import org.pac4j.saml.config.SAML2Configuration;
 import org.pac4j.saml.metadata.keystore.BaseSAML2KeystoreGenerator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.FileUrlResource;
 import org.springframework.core.io.support.ResourcePatternResolver;
@@ -42,15 +39,12 @@
 @Component
 public class SAML2SP4UILoader implements SyncopeCoreLoader {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SAML2SP4UILoader.class);
-
-    private static final String SAML2SP4UI_LOGIC_PROPERTIES = "saml2sp4ui-logic.properties";
+    @Autowired
+    private SAML2SP4UIProperties props;
 
     @Autowired
     private ResourcePatternResolver resourceResolver;
 
-    private Properties props;
-
     @Override
     public int getOrder() {
         return 1000;
@@ -60,18 +54,16 @@
     public void load() {
         EntitlementsHolder.getInstance().addAll(SAML2SP4UIEntitlement.values());
         ImplementationTypesHolder.getInstance().putAll(SAML2SP4UIImplementationType.values());
-
-        props = PropertyUtils.read(getClass(), SAML2SP4UI_LOGIC_PROPERTIES, "conf.directory");
     }
 
     public SAML2Configuration newSAML2Configuration() {
         SAML2Configuration cfg = new SAML2Configuration(
-                resourceResolver.getResource(props.getProperty("saml2.sp4ui.keystore")),
-                props.getProperty("saml2.sp4ui.keystore.storepass"),
-                props.getProperty("saml2.sp4ui.keystore.keypass"),
+                resourceResolver.getResource(props.getKeystore()),
+                props.getKeystoreStorepass(),
+                props.getKeystoreKeypass(),
                 null);
 
-        cfg.setKeystoreType(props.getProperty("saml2.sp4ui.keystore.type"));
+        cfg.setKeystoreType(props.getKeystoreType());
         if (cfg.getKeystoreResource() instanceof FileUrlResource) {
             cfg.setKeystoreGenerator(new BaseSAML2KeystoreGenerator(cfg) {
 
@@ -94,13 +86,7 @@
         cfg.setWantsAssertionsSigned(true);
         cfg.setAuthnRequestSigned(true);
         cfg.setSpLogoutRequestSigned(true);
-
-        try {
-            cfg.setAcceptedSkew(Integer.valueOf(props.getProperty("saml2.sp4ui.skew")));
-        } catch (NumberFormatException e) {
-            LOG.error("Invalid value provided for 'saml2.sp4ui.skew': {}", props.getProperty("saml2.sp4ui.skew"), e);
-        }
-
+        cfg.setAcceptedSkew(props.getSkew());
         cfg.setLogoutHandler(new NoOpLogoutHandler());
 
         return cfg;
diff --git a/ext/self-keymaster/rest-cxf/src/main/resources/META-INF/spring.factories b/ext/saml2sp4ui/logic/src/main/resources/META-INF/spring.factories
similarity index 92%
copy from ext/self-keymaster/rest-cxf/src/main/resources/META-INF/spring.factories
copy to ext/saml2sp4ui/logic/src/main/resources/META-INF/spring.factories
index 5f98d3e..6cb5b1c 100644
--- a/ext/self-keymaster/rest-cxf/src/main/resources/META-INF/spring.factories
+++ b/ext/saml2sp4ui/logic/src/main/resources/META-INF/spring.factories
@@ -16,4 +16,4 @@
 # under the License.
 
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-  org.apache.syncope.ext.self.keymaster.cxf.SelfKeymasterContext
+  org.apache.syncope.core.logic.SAML2SP4UIContext
diff --git a/ext/saml2sp4ui/logic/src/main/resources/saml2sp4ui-logic.properties b/ext/saml2sp4ui/logic/src/main/resources/core-saml2sp4ui.properties
similarity index 96%
rename from ext/saml2sp4ui/logic/src/main/resources/saml2sp4ui-logic.properties
rename to ext/saml2sp4ui/logic/src/main/resources/core-saml2sp4ui.properties
index e94b580..73c54d2 100644
--- a/ext/saml2sp4ui/logic/src/main/resources/saml2sp4ui-logic.properties
+++ b/ext/saml2sp4ui/logic/src/main/resources/core-saml2sp4ui.properties
@@ -14,8 +14,6 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-conf.directory=${conf.directory}
-
 saml2.sp4ui.skew=300
 saml2.sp4ui.keystore=file://${conf.directory}/saml.keystore.jks
 saml2.sp4ui.keystore.type=jks
diff --git a/ext/self-keymaster/client/src/main/resources/keymaster.properties b/ext/self-keymaster/client/src/main/resources/keymaster.properties
deleted file mode 100644
index 0b9818a..0000000
--- a/ext/self-keymaster/client/src/main/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:8080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
diff --git a/ext/self-keymaster/logic/pom.xml b/ext/self-keymaster/logic/pom.xml
deleted file mode 100644
index 7bb37ef..0000000
--- a/ext/self-keymaster/logic/pom.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.apache.syncope.ext</groupId>
-    <artifactId>syncope-ext-self-keymaster</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
-  </parent>
-
-  <name>Apache Syncope Ext: Self Keymaster Logic</name>
-  <description>Apache Syncope Ext: Self Keymaster Logic</description>
-  <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-  <artifactId>syncope-ext-self-keymaster-logic</artifactId>
-  <packaging>jar</packaging>
-  
-  <properties>
-    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.syncope.core.idrepo</groupId>
-      <artifactId>syncope-core-idrepo-logic</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-persistence-api</artifactId>
-      <version>${project.version}</version>
-    </dependency>    
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-checkstyle-plugin</artifactId>
-        <configuration>
-          <sourceDirectories>${project.build.sourceDirectory}</sourceDirectories>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git a/ext/self-keymaster/persistence-api/pom.xml b/ext/self-keymaster/persistence-api/pom.xml
deleted file mode 100644
index 554da0c..0000000
--- a/ext/self-keymaster/persistence-api/pom.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.apache.syncope.ext</groupId>
-    <artifactId>syncope-ext-self-keymaster</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
-  </parent>
-
-  <name>Apache Syncope Ext: Self Keymaster Persistence API</name>
-  <description>Apache Syncope Ext: Self Keymaster Persistence API</description>
-  <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-  <artifactId>syncope-ext-self-keymaster-persistence-api</artifactId>
-  <packaging>jar</packaging>
-  
-  <properties>
-    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.syncope.core</groupId>
-      <artifactId>syncope-core-persistence-api</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.syncope.common.keymaster</groupId>
-      <artifactId>syncope-common-keymaster-client-api</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-checkstyle-plugin</artifactId>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git a/ext/self-keymaster/rest-cxf/pom.xml b/ext/self-keymaster/rest-cxf/pom.xml
deleted file mode 100644
index 0526e52..0000000
--- a/ext/self-keymaster/rest-cxf/pom.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.apache.syncope.ext</groupId>
-    <artifactId>syncope-ext-self-keymaster</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
-  </parent>
-
-  <name>Apache Syncope Ext: Self Keymaster REST CXF</name>
-  <description>Apache Syncope Ext: Self Keymaster REST CXF</description>
-  <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-  <artifactId>syncope-ext-self-keymaster-rest-cxf</artifactId>
-  <packaging>jar</packaging>
-  
-  <properties>
-    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.syncope.core.idrepo</groupId>
-      <artifactId>syncope-core-idrepo-rest-cxf</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-rest-api</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-logic</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-checkstyle-plugin</artifactId>
-      </plugin>
-    </plugins>
-    
-    <resources>
-      <resource>
-        <directory>src/main/resources</directory>
-        <filtering>true</filtering>
-      </resource>
-    </resources>
-  </build>
-</project>
diff --git a/fit/console-reference/pom.xml b/fit/console-reference/pom.xml
index 46bce9e..08753d7 100644
--- a/fit/console-reference/pom.xml
+++ b/fit/console-reference/pom.xml
@@ -52,8 +52,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
       <version>${project.version}</version>
     </dependency>
 
@@ -125,7 +125,7 @@
           <configuration>
             <properties>
               <cargo.jvmargs>
-                -Dspring.profiles.active=embedded
+                -Dspring.profiles.active=embedded,all
                 -XX:+CMSClassUnloadingEnabled -Xmx1024m -Xms512m</cargo.jvmargs>
             </properties>
           </configuration>
@@ -203,7 +203,7 @@
               <configuration>
                 <properties>
                   <cargo.jvmargs>
-                    -Dspring.profiles.active=embedded
+                    -Dspring.profiles.active=embedded,all
                     -Dwicket.core.settings.general.configuration-type=development
                     -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
                     -XX:+CMSClassUnloadingEnabled -XX:+UseG1GC -Xmx1024m -Xms512m</cargo.jvmargs>
@@ -272,7 +272,7 @@
               <configuration>
                 <properties>
                   <cargo.jvmargs>
-                    -Dspring.profiles.active=embedded
+                    -Dspring.profiles.active=embedded,all
                     -Dwicket.core.settings.general.configuration-type=development
                     -javaagent:${java.home}/lib/hotswap/hotswap-agent.jar=autoHotswap=true,disablePlugin=Spring,disablePlugin=Hibernate,disablePlugin=CxfJAXRS
                     -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
diff --git a/fit/console-reference/src/main/resources/application-embedded.properties b/fit/console-reference/src/main/resources/console-embedded.properties
similarity index 87%
rename from fit/console-reference/src/main/resources/application-embedded.properties
rename to fit/console-reference/src/main/resources/console-embedded.properties
index 07f1ea4..5346677 100644
--- a/fit/console-reference/src/main/resources/application-embedded.properties
+++ b/fit/console-reference/src/main/resources/console-embedded.properties
@@ -14,6 +14,10 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+keymaster.address=http://localhost:9080/syncope/rest/keymaster
+keymaster.username=${anonymousUser}
+keymaster.password=${anonymousKey}
+
 service.discovery.address=http://localhost:9080/syncope-console/
 
 spring.devtools.livereload.enabled=false
diff --git a/fit/console-reference/src/main/resources/console.properties b/fit/console-reference/src/main/resources/console.properties
deleted file mode 100644
index 32737b9..0000000
--- a/fit/console-reference/src/main/resources/console.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-console.directory=${conf.directory}
-
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-
-useGZIPCompression=true
-maxUploadFileSizeMB=5
-
-# Max wait time on apply changes from modals/wizards (given in seconds)
-maxWaitTimeOnApplyChanges=30
-
-reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
-
-page.dashboard=org.apache.syncope.client.console.pages.Dashboard
-page.realms=org.apache.syncope.client.console.pages.Realms
-page.reports=org.apache.syncope.client.console.pages.Reports
-page.audit=org.apache.syncope.client.console.pages.Audit
-page.implementations=org.apache.syncope.client.console.pages.Implementations
-page.logs=org.apache.syncope.client.console.pages.Logs
-page.security=org.apache.syncope.client.console.pages.Security
-page.types=org.apache.syncope.client.console.pages.Types
-page.policies=org.apache.syncope.client.console.pages.Policies
-page.notifications=org.apache.syncope.client.console.pages.Notifications
-page.parameters=org.apache.syncope.client.console.pages.Parameters
-
-default.any.panel.class=org.apache.syncope.client.console.panels.AnyPanel
-
-topology.corePoolSize=50
-topology.maxPoolSize=100
-topology.queueCapacity=10
-
-x-forward=true
-csrf=true
-
-security.headers.X-XSS-Protection=1; mode=block
-security.headers.Strict-Transport-Security=max-age=31536000; includeSubDomains; preload
-security.headers.X-Content-Type-Options=nosniff
-security.headers.X-Frame-Options=sameorigin
-#security.headers.Content-Security-Policy=default-src https:
diff --git a/fit/console-reference/src/main/resources/keymaster.properties b/fit/console-reference/src/main/resources/keymaster.properties
deleted file mode 100644
index 033fe3b..0000000
--- a/fit/console-reference/src/main/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 983c546..5249865 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -34,6 +34,8 @@
   <packaging>war</packaging>
   
   <properties>
+    <spring.profiles.active>embedded</spring.profiles.active>
+
     <ianal.phase>none</ianal.phase>
 
     <jdbcdriver.groupId>com.h2database</jdbcdriver.groupId>
@@ -68,26 +70,22 @@
     </dependency>
 
     <dependency>
-      <groupId>com.h2database</groupId>
-      <artifactId>h2</artifactId>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-self-keymaster-starter</artifactId>
+      <version>${project.version}</version>
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-rest-cxf</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-persistence-jpa</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
       <artifactId>syncope-common-keymaster-client-zookeeper</artifactId>
       <version>${project.version}</version>
     </dependency>
 
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+    </dependency>
+
     <!-- TEST -->
     <dependency>
       <groupId>org.apache.syncope.fit</groupId>
@@ -97,8 +95,8 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
@@ -313,7 +311,7 @@
           <configuration>
             <properties>
               <cargo.jvmargs>
-                -Dspring.profiles.active=embedded
+                -Dspring.profiles.active=${spring.profiles.active}
                 -XX:+UseG1GC -Xmx1024m -Xms512m</cargo.jvmargs>
             </properties>
           </configuration>
@@ -377,14 +375,7 @@
       <resource>
         <directory>${basedir}/../../core/starter/src/main/resources</directory>
         <includes>
-          <include>application.properties</include>
-        </includes>
-        <filtering>true</filtering>
-      </resource>
-      <resource>
-        <directory>${basedir}/../../core/persistence-jpa/src/main/resources</directory>
-        <includes>
-          <include>persistence.properties</include>
+          <include>core.properties</include>
         </includes>
         <filtering>true</filtering>
       </resource>
@@ -393,13 +384,6 @@
         <targetPath>${project.build.outputDirectory}/domains</targetPath>
         <filtering>true</filtering>
       </resource>
-      <resource>
-        <directory>${basedir}/../../core/spring/src/main/resources</directory>
-        <includes>
-          <include>security.properties</include>
-        </includes>
-        <filtering>true</filtering>
-      </resource>
     </resources>
     <testResources>
       <testResource>
@@ -435,6 +419,10 @@
     <profile>
       <id>zookeeper-it</id>
 
+      <properties>
+        <spring.profiles.active>embedded,zookeeper</spring.profiles.active>
+      </properties>
+
       <build>
         <plugins>
           <plugin>
@@ -512,6 +500,10 @@
     <profile>
       <id>elasticsearch-it</id>
       
+      <properties>
+        <spring.profiles.active>embedded,elasticsearch</spring.profiles.active>
+      </properties>
+
       <dependencies>
         <dependency>
           <groupId>org.apache.syncope.ext.elasticsearch</groupId>
@@ -606,10 +598,6 @@
               <include>errorMessages.properties</include>
             </includes>
           </testResource>
-          <testResource>
-            <directory>src/main/resources/elasticsearch</directory>
-            <filtering>true</filtering>
-          </testResource>
         </testResources>
       </build>
     </profile>
@@ -620,6 +608,8 @@
       <properties>
         <jdbcdriver.groupId>org.postgresql</jdbcdriver.groupId>
         <jdbcdriver.artifactId>postgresql</jdbcdriver.artifactId>
+
+        <spring.profiles.active>embedded,pgjsonb</spring.profiles.active>
       </properties>
 
       <dependencies>
@@ -661,28 +651,6 @@
           </plugin>
 
           <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-antrun-plugin</artifactId>
-            <inherited>true</inherited>
-            <executions>
-              <execution>
-                <id>remove-domain-Two</id>
-                <phase>prepare-package</phase>
-                <configuration>
-                  <target>
-                    <delete>
-                      <fileset dir="${project.build.outputDirectory}/domains" includes="Two*"/>
-                    </delete>
-                  </target>
-                </configuration>
-                <goals>
-                  <goal>run</goal>
-                </goals>
-              </execution>
-            </executions>
-          </plugin>
-
-          <plugin>
             <groupId>io.fabric8</groupId>
             <artifactId>docker-maven-plugin</artifactId>
             <configuration>
@@ -742,34 +710,11 @@
 
         <resources>
           <resource>
-            <directory>src/main/resources</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>provisioning.properties</exclude>
-              <exclude>indexes.xml</exclude>
-              <exclude>views.xml</exclude>
-            </excludes>
-          </resource>
-          <resource>
-            <directory>src/main/resources/pgjsonb</directory>
-            <filtering>true</filtering>
-          </resource>
-          <resource>
             <directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/domains</directory>
             <targetPath>${project.build.outputDirectory}/domains</targetPath>
             <filtering>true</filtering>
           </resource>
         </resources>
-        <testResources>
-          <testResource>
-            <directory>${basedir}/../../core/persistence-jpa-json/src/main/resources/pgjsonb</directory>
-            <includes>
-              <include>persistence.properties</include>
-              <include>indexes.xml</include>
-              <include>views.xml</include>
-            </includes>            
-          </testResource>
-        </testResources>
       </build>
     </profile>
 
@@ -779,6 +724,8 @@
       <properties>
         <jdbcdriver.groupId>org.postgresql</jdbcdriver.groupId>
         <jdbcdriver.artifactId>postgresql</jdbcdriver.artifactId>
+
+        <spring.profiles.active>embedded,postgres</spring.profiles.active>
       </properties>
 
       <dependencies>
@@ -872,31 +819,6 @@
             </configuration>
           </plugin>
         </plugins>
-
-        <resources>
-          <resource>
-            <directory>src/main/resources</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>provisioning.properties</exclude>
-              <exclude>views.xml</exclude>
-            </excludes>
-          </resource>
-          <resource>
-            <directory>src/main/resources/postgres</directory>
-            <filtering>true</filtering>
-          </resource>
-        </resources>
-        <!-- Views need to be customized, so the PostgreSQL-specific views.xml need to be copied to target/test-classes,
-        e.g. the conf directory, in order to override classpath:views.xml -->
-        <testResources>
-          <testResource>
-            <directory>src/main/resources/postgres</directory>
-            <includes>
-              <include>views.xml</include>
-            </includes>            
-          </testResource>
-        </testResources>
       </build>
     </profile>
 
@@ -906,6 +828,8 @@
       <properties>
         <jdbcdriver.groupId>mysql</jdbcdriver.groupId>
         <jdbcdriver.artifactId>mysql-connector-java</jdbcdriver.artifactId>
+
+        <spring.profiles.active>embedded,myjson</spring.profiles.active>
       </properties>
 
       <dependencies>
@@ -947,28 +871,6 @@
           </plugin>
 
           <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-antrun-plugin</artifactId>
-            <inherited>true</inherited>
-            <executions>
-              <execution>
-                <id>remove-domain-Two</id>
-                <phase>prepare-package</phase>
-                <configuration>
-                  <target>
-                    <delete>
-                      <fileset dir="${project.build.directory}/classes/domains" includes="Two*"/>
-                    </delete>
-                  </target>
-                </configuration>
-                <goals>
-                  <goal>run</goal>
-                </goals>
-              </execution>
-            </executions>
-          </plugin>
-
-          <plugin>
             <groupId>io.fabric8</groupId>
             <artifactId>docker-maven-plugin</artifactId>
             <configuration>
@@ -1030,34 +932,11 @@
 
         <resources>
           <resource>
-            <directory>src/main/resources</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>provisioning.properties</exclude>
-              <exclude>indexes.xml</exclude>
-              <exclude>views.xml</exclude>
-            </excludes>
-          </resource>
-          <resource>
-            <directory>src/main/resources/myjson</directory>
-            <filtering>true</filtering>
-          </resource>
-          <resource>
             <directory>${basedir}/../../core/persistence-jpa-json/src/test/resources/domains</directory>
-            <targetPath>${project.build.directory}/classes/domains</targetPath>
+            <targetPath>${project.build.outputDirectory}/domains</targetPath>
             <filtering>true</filtering>
           </resource>
         </resources>
-        <testResources>
-          <testResource>
-            <directory>${basedir}/../../core/persistence-jpa-json/src/main/resources/myjson</directory>
-            <includes>
-              <include>persistence.properties</include>
-              <include>indexes.xml</include>
-              <include>views.xml</include>
-            </includes>            
-          </testResource>
-        </testResources>
       </build>
     </profile>
 
@@ -1067,6 +946,8 @@
       <properties>
         <jdbcdriver.groupId>mysql</jdbcdriver.groupId>
         <jdbcdriver.artifactId>mysql-connector-java</jdbcdriver.artifactId>
+
+        <spring.profiles.active>embedded,mysql</spring.profiles.active>
       </properties>
 
       <dependencies>
@@ -1162,20 +1043,6 @@
             </configuration>
           </plugin>
         </plugins>
-
-        <resources>
-          <resource>
-            <directory>src/main/resources</directory>
-            <excludes>
-              <exclude>provisioning.properties</exclude>
-            </excludes>
-            <filtering>true</filtering>
-          </resource>
-          <resource>
-            <directory>src/main/resources/mysql</directory>
-            <filtering>true</filtering>
-          </resource>
-        </resources>
       </build>
     </profile>
     
@@ -1185,6 +1052,8 @@
       <properties>
         <jdbcdriver.groupId>org.mariadb.jdbc</jdbcdriver.groupId>
         <jdbcdriver.artifactId>mariadb-java-client</jdbcdriver.artifactId>
+
+        <spring.profiles.active>embedded,mariadb</spring.profiles.active>
       </properties>
 
       <dependencies>
@@ -1269,20 +1138,6 @@
             </configuration>
           </plugin>
         </plugins>
-
-        <resources>
-          <resource>
-            <directory>src/main/resources</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>provisioning.properties</exclude>
-            </excludes>
-          </resource>
-          <resource>
-            <directory>src/main/resources/mariadb</directory>
-            <filtering>true</filtering>
-          </resource>
-        </resources>
       </build>
     </profile>
     
@@ -1291,14 +1146,16 @@
       
       <properties>
         <jdbcdriver.groupId>com.oracle.database.jdbc</jdbcdriver.groupId>
-        <jdbcdriver.artifactId>ojdbc10</jdbcdriver.artifactId>
+        <jdbcdriver.artifactId>ojdbc11</jdbcdriver.artifactId>
         <cargo.deployable.ping.timeout>120000</cargo.deployable.ping.timeout>
+
+        <spring.profiles.active>embedded,oracle</spring.profiles.active>
       </properties>
 
       <dependencies>
         <dependency>
           <groupId>com.oracle.database.jdbc</groupId>
-          <artifactId>ojdbc10</artifactId>
+          <artifactId>ojdbc11</artifactId>
           <version>${jdbc.oracle.version}</version>
           <scope>test</scope>
         </dependency>
@@ -1326,10 +1183,7 @@
               <images>
                 <image>
                   <alias>oracle</alias>
-                  <name>database-enterprise</name>
-                  <build>
-                    <dockerFileDir>${project.basedir}/src/main/resources/oracle</dockerFileDir>
-                  </build>
+                  <name>oracleinanutshell/oracle-xe-11g:latest</name>
                   <run>
                     <env>
                       <ORACLE_ALLOW_REMOTE>true</ORACLE_ALLOW_REMOTE>
@@ -1337,8 +1191,13 @@
                     </env>
                     <wait>
                       <log>Disconnected from Oracle Database 11g Express Edition</log>
-                      <time>30000</time>
+                      <time>120000</time>
                     </wait>                
+                    <volumes>
+                      <bind>
+                        <volume>${project.build.outputDirectory}/oracle/:/docker-entrypoint-initdb.d/</volume>
+                      </bind>
+                    </volumes>
                   </run>
                 </image>
               </images>
@@ -1385,13 +1244,6 @@
 
         <resources>
           <resource>
-            <directory>src/main/resources</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>provisioning.properties</exclude>
-            </excludes>
-          </resource>
-          <resource>
             <directory>src/main/resources/oracle</directory>
             <filtering>true</filtering>
           </resource>
@@ -1405,6 +1257,8 @@
       <properties>
         <jdbcdriver.groupId>com.microsoft.sqlserver</jdbcdriver.groupId>
         <jdbcdriver.artifactId>mssql-jdbc</jdbcdriver.artifactId>
+
+        <spring.profiles.active>embedded,sqlserver</spring.profiles.active>
       </properties>
 
       <dependencies>
@@ -1487,31 +1341,6 @@
             </configuration>
           </plugin>
         </plugins>
-
-        <resources>
-          <resource>
-            <directory>src/main/resources</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>provisioning.properties</exclude>
-              <exclude>views.xml</exclude>
-            </excludes>
-          </resource>
-          <resource>
-            <directory>src/main/resources/sqlserver</directory>
-            <filtering>true</filtering>
-          </resource>
-        </resources>
-        <!-- Views need to be customized, so the SQL Server-specific views.xml need to be copied to target/test-classes,
-        e.g. the conf directory, in order to override classpath:views.xml -->
-        <testResources>
-          <testResource>
-            <directory>src/main/resources/sqlserver</directory>
-            <includes>
-              <include>views.xml</include>
-            </includes>            
-          </testResource>
-        </testResources>
       </build>
     </profile>
     
@@ -1584,7 +1413,7 @@
               <configuration>
                 <properties>
                   <cargo.glassfish.removeDefaultDatasource>false</cargo.glassfish.removeDefaultDatasource>
-                  <cargo.jvmargs>-Dspring.profiles.active=embedded
+                  <cargo.jvmargs>-Dspring.profiles.active=embedded,payara
                     -XX:+UseG1GC -Xmx2048m -Xms1024m</cargo.jvmargs>
                 </properties>
               </configuration>
@@ -1687,7 +1516,7 @@
               </container>
               <configuration>
                 <properties>
-                  <cargo.jvmargs>-Djava.net.preferIPv4Stack=true -Dspring.profiles.active=wildfly,embedded
+                  <cargo.jvmargs>-Djava.net.preferIPv4Stack=true -Dspring.profiles.active=embedded,wildfly
                     -XX:+UseG1GC -Xmx2048m -Xms1024m</cargo.jvmargs>
                 </properties>
               </configuration>
@@ -1733,7 +1562,7 @@
               <configuration>
                 <properties>
                   <cargo.jvmargs>
-                    -Dspring.profiles.active=embedded
+                    -Dspring.profiles.active=${spring.profiles.active}
                     -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
                     -XX:+UseG1GC -Xmx1024m -Xms512m</cargo.jvmargs>
                 </properties>
@@ -1764,7 +1593,7 @@
               <configuration>
                 <properties>
                   <cargo.jvmargs>
-                    -Dspring.profiles.active=embedded
+                    -Dspring.profiles.active=${spring.profiles.active}
                     -javaagent:${java.home}/lib/hotswap/hotswap-agent.jar=autoHotswap=true,disablePlugin=Spring,disablePlugin=Hibernate
                     -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
                     -XX:+UseConcMarkSweepGC -Xmx1024m -Xms512m</cargo.jvmargs>
@@ -1788,10 +1617,15 @@
 
     <profile>
       <id>all</id>
+
       <activation>
         <activeByDefault>true</activeByDefault>
       </activation>
       
+      <properties>
+        <spring.profiles.active>embedded,all</spring.profiles.active> 
+      </properties>
+
       <dependencies>
         <dependency>
           <groupId>org.apache.syncope.ext.flowable</groupId>
@@ -1869,23 +1703,8 @@
                 </configuration>
               </execution>
             </executions>
-          </plugin>                    
+          </plugin>     
         </plugins>
-        
-        <resources>
-          <resource>
-            <directory>src/main/resources</directory>
-            <filtering>true</filtering>
-            <excludes>
-              <exclude>workflow.properties</exclude>
-              <exclude>provisioning.properties</exclude>
-            </excludes>
-          </resource>
-          <resource>
-            <directory>src/main/resources/all</directory>
-            <filtering>true</filtering>
-          </resource>
-        </resources>
       </build>
     </profile>
     
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/core/logic/init/CoreReferenceLoader.java b/fit/core-reference/src/main/java/org/apache/syncope/core/logic/init/CoreReferenceLoader.java
index 3c5f549..966cc1b 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/core/logic/init/CoreReferenceLoader.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/core/logic/init/CoreReferenceLoader.java
@@ -18,10 +18,18 @@
  */
 package org.apache.syncope.core.logic.init;
 
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.fit.core.reference.ITImplementationLookup;
+import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 
 @ComponentScan("org.apache.syncope.fit.core.reference")
 @Configuration
 public class CoreReferenceLoader {
+
+    @Bean
+    public ImplementationLookup implementationLookup() {
+        return new ITImplementationLookup();
+    }
 }
diff --git a/fit/core-reference/src/main/resources/all/provisioning.properties b/fit/core-reference/src/main/resources/all/provisioning.properties
deleted file mode 100644
index dead149..0000000
--- a/fit/core-reference/src/main/resources/all/provisioning.properties
+++ /dev/null
@@ -1,39 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-camel.directory=${conf.directory}
-
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_h2.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/all/saml2sp4ui-logic.properties b/fit/core-reference/src/main/resources/all/saml2sp4ui-logic.properties
deleted file mode 100644
index e94b580..0000000
--- a/fit/core-reference/src/main/resources/all/saml2sp4ui-logic.properties
+++ /dev/null
@@ -1,23 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-conf.directory=${conf.directory}
-
-saml2.sp4ui.skew=300
-saml2.sp4ui.keystore=file://${conf.directory}/saml.keystore.jks
-saml2.sp4ui.keystore.type=jks
-saml2.sp4ui.keystore.storepass=changeit
-saml2.sp4ui.keystore.keypass=changeit
diff --git a/fit/core-reference/src/main/resources/all/workflow.properties b/fit/core-reference/src/main/resources/all/workflow.properties
deleted file mode 100644
index be8a7bb..0000000
--- a/fit/core-reference/src/main/resources/all/workflow.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-wf.directory=${conf.directory}
-historyLevel=activity
-uwfAdapter=org.apache.syncope.core.flowable.impl.FlowableUserWorkflowAdapter
-gwfAdapter=org.apache.syncope.core.workflow.java.DefaultGroupWorkflowAdapter
-awfAdapter=org.apache.syncope.core.workflow.java.DefaultAnyObjectWorkflowAdapter
diff --git a/fit/core-reference/src/main/resources/application-embedded.properties b/fit/core-reference/src/main/resources/application-embedded.properties
deleted file mode 100644
index c96481a..0000000
--- a/fit/core-reference/src/main/resources/application-embedded.properties
+++ /dev/null
@@ -1,30 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-service.discovery.address=http://localhost:9080/syncope/rest/
-
-spring.devtools.livereload.enabled=false
-spring.devtools.restart.enabled=false
-
-# H2
-spring.h2.console.enabled=true
-spring.h2.console.path=/h2
-
-# Datasource
-spring.datasource.url=jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1
-spring.datasource.username=sa
-spring.datasource.password=
-spring.datasource.driver-class-name=org.h2.Driver
diff --git a/fit/core-reference/src/main/resources/connid.properties b/fit/core-reference/src/main/resources/connid.properties
deleted file mode 100644
index 22ed0a8..0000000
--- a/fit/core-reference/src/main/resources/connid.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-connid.locations=${connid.location},\
-connid://${testconnectorserver.key}@localhost:${testconnectorserver.port}
-
-## for test only
-testdb.url=${testdb.url}
-connid.soap.version=${connid.soap.version}
-connid.database.version=${connid.database.version}
-
diff --git a/ext/saml2sp4ui/logic/src/main/resources/saml2sp4ui-logic.properties b/fit/core-reference/src/main/resources/core-all.properties
similarity index 61%
copy from ext/saml2sp4ui/logic/src/main/resources/saml2sp4ui-logic.properties
copy to fit/core-reference/src/main/resources/core-all.properties
index e94b580..5ff2325 100644
--- a/ext/saml2sp4ui/logic/src/main/resources/saml2sp4ui-logic.properties
+++ b/fit/core-reference/src/main/resources/core-all.properties
@@ -14,10 +14,15 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-conf.directory=${conf.directory}
 
-saml2.sp4ui.skew=300
+workflow.uwfAdapter=org.apache.syncope.core.flowable.impl.FlowableUserWorkflowAdapter
+
+provisioning.userProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelUserProvisioningManager
+provisioning.groupProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelGroupProvisioningManager
+provisioning.anyObjectProvisioningManager=org.apache.syncope.core.provisioning.camel.CamelAnyObjectProvisioningManager
+
 saml2.sp4ui.keystore=file://${conf.directory}/saml.keystore.jks
-saml2.sp4ui.keystore.type=jks
-saml2.sp4ui.keystore.storepass=changeit
-saml2.sp4ui.keystore.keypass=changeit
+saml2.sp4ui.keystore-type=jks
+saml2.sp4ui.keystore-storepass=changeit
+saml2.sp4ui.keystore-keypass=changeit
+saml2.sp4ui.skew=300
diff --git a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/fit/core-reference/src/main/resources/core-elasticsearch.properties
similarity index 88%
copy from ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to fit/core-reference/src/main/resources/core-elasticsearch.properties
index 450ff50..3360988 100644
--- a/ext/flowable/client-enduser/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/fit/core-reference/src/main/resources/core-elasticsearch.properties
@@ -14,5 +14,5 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-userDetails=User details
-userForm=Edit User
+
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO
diff --git a/fit/core-reference/src/main/resources/core-embedded.properties b/fit/core-reference/src/main/resources/core-embedded.properties
new file mode 100644
index 0000000..6ace161
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-embedded.properties
@@ -0,0 +1,71 @@
+# 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.
+keymaster.address=http://localhost:9080/syncope/rest/keymaster
+keymaster.username=${anonymousUser}
+keymaster.password=${anonymousKey}
+
+service.discovery.address=http://localhost:9080/syncope/rest/
+
+spring.devtools.livereload.enabled=false
+spring.devtools.restart.enabled=false
+
+# H2
+spring.h2.console.enabled=true
+spring.h2.console.path=/h2
+
+# Datasource
+spring.datasource.url=jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1
+spring.datasource.username=sa
+spring.datasource.password=
+spring.datasource.driver-class-name=org.h2.Driver
+
+
+security.adminUser=${adminUser}
+security.anonymousUser=${anonymousUser}
+security.jwsKey=${jwsKey}
+security.secretKey=${secretKey}
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.h2.Driver
+persistence.domain[0].jdbcURL=jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1
+persistence.domain[0].dbUsername=sa
+persistence.domain[0].dbPassword=
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.H2Dictionary
+persistence.domain[0].auditSql=audit.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+persistence.domain[1].key=Two
+persistence.domain[1].jdbcDriver=org.h2.Driver
+persistence.domain[1].jdbcURL=jdbc:h2:mem:syncopetwo;DB_CLOSE_DELAY=-1
+persistence.domain[1].dbUsername=sa
+persistence.domain[1].dbPassword=
+persistence.domain[1].databasePlatform=org.apache.openjpa.jdbc.sql.H2Dictionary
+persistence.domain[1].auditSql=audit.sql
+persistence.domain[1].poolMaxActive=10
+persistence.domain[1].poolMinIdle=2
+persistence.domain[1].adminPassword=2AA60A8FF7FCD473D321E0146AFD9E26DF395147
+persistence.domain[1].adminCipherAlgorithm=SHA
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_h2.sql
+
+provisioning.connIdLocation=${connid.location},\
+connid://${testconnectorserver.key}@localhost:${testconnectorserver.port}
+
+provisioning.smtp.host=localhost
+provisioning.smtp.port=${testmail.smtpport}
diff --git a/fit/core-reference/src/main/resources/core-mariadb.properties b/fit/core-reference/src/main/resources/core-mariadb.properties
new file mode 100644
index 0000000..c7505fb
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-mariadb.properties
@@ -0,0 +1,29 @@
+# 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.
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.mariadb.jdbc.Driver
+persistence.domain[0].jdbcURL=jdbc:mariadb://${DB_CONTAINER_IP}:3306/syncope?characterEncoding=UTF-8
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].auditSql=audit_mariadb.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mariadb.sql
diff --git a/fit/core-reference/src/main/resources/core-myjson.properties b/fit/core-reference/src/main/resources/core-myjson.properties
new file mode 100644
index 0000000..43675d3
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-myjson.properties
@@ -0,0 +1,47 @@
+# 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.
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mysql_innodb.sql
+
+persistence.entityFactory=org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory
+persistence.plainSchemaDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONPlainSchemaDAO
+persistence.plainAttrDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
+persistence.plainAttrValueDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO
+persistence.searchCondVisitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
+persistence.userDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
+persistence.groupDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
+persistence.anyObjectDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
+persistence.auditConfDao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditConfDAO
+
+persistence.indexesXML=classpath:myjson/indexes.xml
+persistence.viewsXML=classpath:myjson/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=com.mysql.cj.jdbc.Driver
+persistence.domain[0].jdbcURL=jdbc:mysql://${DB_CONTAINER_IP}:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].orm=META-INF/spring-orm-myjson.xml
+persistence.domain[0].auditSql=audit_myjson.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mysql_innodb.sql
diff --git a/fit/core-reference/src/main/resources/core-mysql.properties b/fit/core-reference/src/main/resources/core-mysql.properties
new file mode 100644
index 0000000..404b457
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-mysql.properties
@@ -0,0 +1,29 @@
+# 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.
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=com.mysql.cj.jdbc.Driver
+persistence.domain[0].jdbcURL=jdbc:mysql://${DB_CONTAINER_IP}:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
+persistence.domain[0].auditSql=audit_mysql_innodb.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_mysql_innodb.sql
diff --git a/fit/core-reference/src/main/resources/core-oracle.properties b/fit/core-reference/src/main/resources/core-oracle.properties
new file mode 100644
index 0000000..2d40afa
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-oracle.properties
@@ -0,0 +1,31 @@
+# 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.
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=oracle.jdbc.OracleDriver
+persistence.domain[0].jdbcURL=jdbc:oracle:thin:@${DB_CONTAINER_IP}:1521:XE
+persistence.domain[0].schema=SYNCOPE
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.OracleDictionary
+persistence.domain[0].orm=META-INF/spring-orm-oracle.xml
+persistence.domain[0].auditSql=audit_oracle.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
+provisioning.quartz.sql=tables_oracle.sql
diff --git a/fit/core-reference/src/main/resources/core-pgjsonb.properties b/fit/core-reference/src/main/resources/core-pgjsonb.properties
new file mode 100644
index 0000000..3ba1891
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-pgjsonb.properties
@@ -0,0 +1,44 @@
+# 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.
+
+persistence.entityFactory=org.apache.syncope.core.persistence.jpa.entity.PGJPAJSONEntityFactory
+persistence.plainSchemaDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONPlainSchemaDAO
+persistence.plainAttrDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO
+persistence.plainAttrValueDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO
+persistence.anySearchDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO
+persistence.searchCondVisitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
+persistence.userDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
+persistence.groupDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
+persistence.anyObjectDao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
+persistence.auditConfDao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditConfDAO
+
+persistence.indexesXML=classpath:pgjsonb/indexes.xml
+persistence.viewsXML=classpath:pgjsonb/views.xml
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.postgresql.Driver
+persistence.domain[0].jdbcURL=jdbc:postgresql://${DB_CONTAINER_IP}:5432/syncope?stringtype=unspecified
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
+persistence.domain[0].orm=META-INF/spring-orm-pgjsonb.xml
+persistence.domain[0].auditSql=audit_pgjsonb.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
+provisioning.quartz.sql=tables_postgres.sql
diff --git a/fit/core-reference/src/main/resources/core-postgres.properties b/fit/core-reference/src/main/resources/core-postgres.properties
new file mode 100644
index 0000000..479485e
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-postgres.properties
@@ -0,0 +1,29 @@
+# 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.
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.postgresql.Driver
+persistence.domain[0].jdbcURL=jdbc:postgresql://${DB_CONTAINER_IP}:5432/syncope
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=syncope
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
+persistence.domain[0].auditSql=audit.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
+provisioning.quartz.sql=tables_postgres.sql
diff --git a/fit/core-reference/src/main/resources/core-sqlserver.properties b/fit/core-reference/src/main/resources/core-sqlserver.properties
new file mode 100644
index 0000000..f041866
--- /dev/null
+++ b/fit/core-reference/src/main/resources/core-sqlserver.properties
@@ -0,0 +1,33 @@
+# 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.
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=com.microsoft.sqlserver.jdbc.SQLServerDriver
+persistence.domain[0].jdbcURL=jdbc:sqlserver://${DB_CONTAINER_IP}:1433;databaseName=syncope
+persistence.domain[0].schema=dbo
+persistence.domain[0].dbUsername=syncope
+persistence.domain[0].dbPassword=Syncope123
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.SQLServerDictionary
+persistence.domain[0].orm=META-INF/spring-orm-sqlserver.xml
+persistence.domain[0].auditSql=audit_sqlserver.sql
+persistence.domain[0].poolMaxActive=10
+persistence.domain[0].poolMinIdle=2
+
+persistence.viewsXML=classpath:sqlserver_views.xml
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.MSSQLDelegate
+provisioning.quartz.sql=tables_sqlServer.sql
diff --git a/fit/core-reference/src/main/resources/application-wildfly.properties b/fit/core-reference/src/main/resources/core-wildfly.properties
similarity index 68%
rename from fit/core-reference/src/main/resources/application-wildfly.properties
rename to fit/core-reference/src/main/resources/core-wildfly.properties
index 97ce62f..8c5d9c8 100644
--- a/fit/core-reference/src/main/resources/application-wildfly.properties
+++ b/fit/core-reference/src/main/resources/core-wildfly.properties
@@ -14,8 +14,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-service.discovery.address=http://localhost:9080/syncope/rest/
+persistence.metaDataFactory=jpa(URLs=vfs:${project.build.directory}/cargo/configurations/wildfly23x/deployments/syncope.war/WEB-INF/lib/syncope-core-persistence-jpa-${syncope.version}.jar; vfs:${project.build.directory}/cargo/configurations/wildfly23x/deployments/syncope.war/WEB-INF/lib/syncope-core-self-keymaster-starter-${syncope.version}.jar, Resources=##orm##)
 
-openjpaMetaDataFactory=jpa(URLs=vfs:${project.build.directory}/cargo/configurations/wildfly23x/deployments/syncope.war/WEB-INF/lib/syncope-core-persistence-jpa-${syncope.version}.jar; vfs:${project.build.directory}/cargo/configurations/wildfly23x/deployments/syncope.war/WEB-INF/lib/syncope-ext-self-keymaster-persistence-jpa-${syncope.version}.jar, Resources=##orm##)
 javadocPaths=/WEB-INF/lib/syncope-common-idrepo-rest-api-${syncope.version}-javadoc.jar,\
 /WEB-INF/lib/syncope-common-idm-rest-api-${syncope.version}-javadoc.jar
diff --git a/common/keymaster/client-zookeeper/src/main/resources/keymaster.properties b/fit/core-reference/src/main/resources/core-zookeeper.properties
similarity index 100%
copy from common/keymaster/client-zookeeper/src/main/resources/keymaster.properties
copy to fit/core-reference/src/main/resources/core-zookeeper.properties
diff --git a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
deleted file mode 100644
index 6cbab38..0000000
--- a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-content.directory=${conf.directory}
-entity.factory=org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory
-plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainSchemaDAO
-plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrDAO
-plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO
-any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO
-any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisitor
-user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
-group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
-anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditConfDAO
-openjpa.RemoteCommitProvider=sjvm
diff --git a/fit/core-reference/src/main/resources/keymaster.properties b/fit/core-reference/src/main/resources/keymaster.properties
deleted file mode 100644
index 033fe3b..0000000
--- a/fit/core-reference/src/main/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
diff --git a/fit/core-reference/src/main/resources/mariadb/domains/Master.properties b/fit/core-reference/src/main/resources/mariadb/domains/Master.properties
deleted file mode 100644
index 0cff2c4..0000000
--- a/fit/core-reference/src/main/resources/mariadb/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.mariadb.jdbc.Driver
-Master.url=jdbc:mariadb://${DB_CONTAINER_IP}:3306/syncope?characterEncoding=UTF-8
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MariaDBDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_mariadb.sql
diff --git a/fit/core-reference/src/main/resources/mariadb/provisioning.properties b/fit/core-reference/src/main/resources/mariadb/provisioning.properties
deleted file mode 100644
index 8ed216e..0000000
--- a/fit/core-reference/src/main/resources/mariadb/provisioning.properties
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_mariadb.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/myjson/domains/Master.properties b/fit/core-reference/src/main/resources/myjson/domains/Master.properties
deleted file mode 100644
index 5b36d41..0000000
--- a/fit/core-reference/src/main/resources/myjson/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=com.mysql.cj.jdbc.Driver
-Master.url=jdbc:mysql://${DB_CONTAINER_IP}:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
-Master.orm=META-INF/spring-orm-myjson.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_myjson.sql
diff --git a/fit/core-reference/src/main/resources/myjson/provisioning.properties b/fit/core-reference/src/main/resources/myjson/provisioning.properties
deleted file mode 100644
index f2b79a1..0000000
--- a/fit/core-reference/src/main/resources/myjson/provisioning.properties
+++ /dev/null
@@ -1,40 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.poolSize=5-25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_mysql_innodb.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/mysql/domains/Master.properties b/fit/core-reference/src/main/resources/mysql/domains/Master.properties
deleted file mode 100644
index 8bac6d2..0000000
--- a/fit/core-reference/src/main/resources/mysql/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=com.mysql.cj.jdbc.Driver
-Master.url=jdbc:mysql://${DB_CONTAINER_IP}:3306/syncope?useSSL=false&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3)
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_mysql_innodb.sql
diff --git a/fit/core-reference/src/main/resources/mysql/provisioning.properties b/fit/core-reference/src/main/resources/mysql/provisioning.properties
deleted file mode 100644
index 6dfcbef..0000000
--- a/fit/core-reference/src/main/resources/mysql/provisioning.properties
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_mysql_innodb.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/oracle/Dockerfile b/fit/core-reference/src/main/resources/oracle/Dockerfile
deleted file mode 100644
index ede2331..0000000
--- a/fit/core-reference/src/main/resources/oracle/Dockerfile
+++ /dev/null
@@ -1,21 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-FROM oracleinanutshell/oracle-xe-11g
-
-ADD init.sql /docker-entrypoint-initdb.d/
-
diff --git a/fit/core-reference/src/main/resources/oracle/domains/Master.properties b/fit/core-reference/src/main/resources/oracle/domains/Master.properties
deleted file mode 100644
index 6267bbd..0000000
--- a/fit/core-reference/src/main/resources/oracle/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=oracle.jdbc.OracleDriver
-Master.url=jdbc:oracle:thin:@${DB_CONTAINER_IP}:1521:XE
-Master.schema=SYNCOPE
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.OracleDictionary
-Master.orm=META-INF/spring-orm-oracle.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_oracle.sql
diff --git a/fit/core-reference/src/main/resources/oracle/provisioning.properties b/fit/core-reference/src/main/resources/oracle/provisioning.properties
deleted file mode 100644
index c7cfb42..0000000
--- a/fit/core-reference/src/main/resources/oracle/provisioning.properties
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
-quartz.sql=tables_oracle.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties b/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties
deleted file mode 100644
index 4f42b56..0000000
--- a/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.postgresql.Driver
-Master.url=jdbc:postgresql://${DB_CONTAINER_IP}:5432/syncope?stringtype=unspecified
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
-Master.orm=META-INF/spring-orm-pgjsonb.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_pgjsonb.sql
diff --git a/fit/core-reference/src/main/resources/pgjsonb/provisioning.properties b/fit/core-reference/src/main/resources/pgjsonb/provisioning.properties
deleted file mode 100644
index 8ff6643..0000000
--- a/fit/core-reference/src/main/resources/pgjsonb/provisioning.properties
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
-quartz.sql=tables_postgres.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/postgres/domains/Master.properties b/fit/core-reference/src/main/resources/postgres/domains/Master.properties
deleted file mode 100644
index 078d5de..0000000
--- a/fit/core-reference/src/main/resources/postgres/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=org.postgresql.Driver
-Master.url=jdbc:postgresql://${DB_CONTAINER_IP}:5432/syncope
-Master.schema=
-Master.username=syncope
-Master.password=syncope
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
-Master.orm=META-INF/spring-orm.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit.sql
diff --git a/fit/core-reference/src/main/resources/postgres/provisioning.properties b/fit/core-reference/src/main/resources/postgres/provisioning.properties
deleted file mode 100644
index 8ff6643..0000000
--- a/fit/core-reference/src/main/resources/postgres/provisioning.properties
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
-quartz.sql=tables_postgres.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/provisioning.properties b/fit/core-reference/src/main/resources/provisioning.properties
deleted file mode 100644
index 1f54e71..0000000
--- a/fit/core-reference/src/main/resources/provisioning.properties
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
-quartz.sql=tables_h2.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/sqlserver/domains/Master.properties b/fit/core-reference/src/main/resources/sqlserver/domains/Master.properties
deleted file mode 100644
index 20a6820..0000000
--- a/fit/core-reference/src/main/resources/sqlserver/domains/Master.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-Master.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
-Master.url=jdbc:sqlserver://${DB_CONTAINER_IP}:1433;databaseName=syncope
-Master.schema=dbo
-Master.username=syncope
-Master.password=Syncope123
-Master.databasePlatform=org.apache.openjpa.jdbc.sql.SQLServerDictionary
-Master.orm=META-INF/spring-orm-sqlserver.xml
-
-Master.pool.maxActive=10
-Master.pool.minIdle=2
-
-Master.audit.sql=audit_sqlserver.sql
diff --git a/fit/core-reference/src/main/resources/sqlserver/provisioning.properties b/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
deleted file mode 100644
index 32fead9..0000000
--- a/fit/core-reference/src/main/resources/sqlserver/provisioning.properties
+++ /dev/null
@@ -1,37 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-asyncConnectorFacadeExecutor.corePoolSize=5
-asyncConnectorFacadeExecutor.maxPoolSize=25
-asyncConnectorFacadeExecutor.queueCapacity=100
-
-propagationTaskExecutorAsyncExecutor.corePoolSize=5
-propagationTaskExecutorAsyncExecutor.maxPoolSize=25
-propagationTaskExecutorAsyncExecutor.queueCapacity=100
-propagationTaskExecutor=org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor
-
-userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager
-groupProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultGroupProvisioningManager
-anyObjectProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultAnyObjectProvisioningManager
-virAttrCache=org.apache.syncope.core.provisioning.java.cache.CaffeineVirAttrCache
-virAttrCacheSpec=maximumSize=5000,expireAfterAccess=1m
-notificationManager=org.apache.syncope.core.provisioning.java.notification.DefaultNotificationManager
-auditManager=org.apache.syncope.core.provisioning.java.DefaultAuditManager
-
-quartz.jobstore=org.quartz.impl.jdbcjobstore.MSSQLDelegate
-quartz.sql=tables_sqlServer.sql
-quartz.scheduler.idleWaitTime=5000
-quartz.disableInstance=false
diff --git a/fit/core-reference/src/main/resources/sqlserver/views.xml b/fit/core-reference/src/main/resources/sqlserver/views.xml
deleted file mode 100644
index 529901e..0000000
--- a/fit/core-reference/src/main/resources/sqlserver/views.xml
+++ /dev/null
@@ -1,226 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
-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.
--->
-<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
-<properties>
-  
-  <entry key="UDynGroupMembers">
-    CREATE TABLE UDynGroupMembers(
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(any_id, group_id))
-  </entry>
-  <entry key="ADynGroupMembers">
-    CREATE TABLE ADynGroupMembers(
-    anyType_id VARCHAR(255),
-    any_id CHAR(36),
-    group_id CHAR(36),
-    UNIQUE(anyType_id, any_id, group_id))
-  </entry>
-  <entry key="DynRoleMembers">
-    CREATE TABLE DynRoleMembers(
-    any_id CHAR(36),
-    role_id VARCHAR(255),
-    UNIQUE(any_id, role_id))
-  </entry>
-  <entry key="DynRealmMembers">
-    CREATE TABLE DynRealmMembers(
-    any_id CHAR(36),
-    dynRealm_id VARCHAR(255),
-    UNIQUE(any_id, dynRealm_id))
-  </entry>
-
-  <!-- user -->
-  <entry key="user_search">
-    CREATE VIEW user_search AS
- 
-    SELECT u.id as any_id, u.* FROM SyncopeUser u
-  </entry>
-  <entry key="user_search_unique_attr">
-    CREATE VIEW user_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrUniqueValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_attr">
-    CREATE VIEW user_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM UPlainAttrValue uav, UPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="user_search_urelationship">
-    CREATE VIEW user_search_urelationship AS
-
-    SELECT m.user_id AS any_id, m.anyObject_id AS right_any_id, m.type_id AS type
-    FROM URelationship m
-  </entry>
-  <entry key="user_search_umembership">
-    CREATE VIEW user_search_umembership AS
-
-    SELECT m.user_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM UMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="user_search_role">
-    CREATE VIEW user_search_role AS
-
-    SELECT ss.user_id AS any_id, ss.role_id AS role_id
-    FROM SyncopeUser_SyncopeRole ss
-  </entry>
-  <entry key="user_search_priv">
-    CREATE VIEW user_search_priv AS
-
-    SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
-    FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
-    WHERE ss.role_id = sp.role_id
-  </entry>
-  <entry key="user_search_dynpriv">
-    CREATE VIEW user_search_dynpriv AS
-
-    SELECT any_id, privilege_id
-    FROM DynRoleMembers drm, SyncopeRole_Privilege rp
-    WHERE drm.role_id = rp.role_id
-  </entry>
-  <entry key="user_search_resource">
-    CREATE VIEW user_search_resource AS
-
-    SELECT st.user_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeUser_ExternalResource st
-  </entry>
-  <entry key="user_search_group_res">
-    CREATE VIEW user_search_group_res AS
-
-    SELECT m.user_id AS any_id, st.resource_id AS resource_id
-    FROM UMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- anyObject -->
-  <entry key="anyObject_search">
-    CREATE VIEW anyObject_search AS
- 
-    SELECT a.id as any_id, a.* FROM AnyObject a
-  </entry>
-  <entry key="anyObject_search_unique_attr">
-    CREATE VIEW anyObject_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrUniqueValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_attr">
-    CREATE VIEW anyObject_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM APlainAttrValue uav, APlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="anyObject_search_arelationship">
-    CREATE VIEW anyObject_search_arelationship AS
-
-    SELECT m.left_anyObject_id AS any_id, m.right_anyObject_id AS right_any_id, m.type_id AS type
-    FROM ARelationship m
-  </entry>
-  <entry key="anyObject_search_amembership">
-    CREATE VIEW anyObject_search_amembership AS
-
-    SELECT m.anyObject_id AS any_id, g.id AS group_id, g.name AS group_name
-    FROM AMembership m, SyncopeGroup g
-    WHERE m.group_id = g.id
-  </entry>
-  <entry key="anyObject_search_resource">
-    CREATE VIEW anyObject_search_resource AS
-
-    SELECT st.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AnyObject_ExternalResource st
-  </entry>
-  <entry key="anyObject_search_group_res">
-    CREATE VIEW anyObject_search_group_res AS
-
-    SELECT m.anyObject_id AS any_id, st.resource_id AS resource_id
-    FROM AMembership m, SyncopeGroup r, SyncopeGroup_ExternalResource st
-    WHERE m.group_id = r.id AND st.group_id = r.id
-  </entry>
-
-  <!-- group -->
-  <entry key="group_search">
-    CREATE VIEW group_search AS
- 
-    SELECT r.id as any_id, r.* FROM SyncopeGroup r
-  </entry>
-  <entry key="group_search_unique_attr">
-    CREATE VIEW group_search_unique_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrUniqueValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_attr">
-    CREATE VIEW group_search_attr AS
-
-    SELECT ua.owner_id AS any_id,
-    ua.schema_id AS schema_id,
-    uav.booleanvalue AS booleanvalue,
-    uav.datevalue AS datevalue,
-    uav.doublevalue AS doublevalue,
-    uav.longvalue AS longvalue,
-    uav.stringvalue AS stringvalue
-    FROM GPlainAttrValue uav, GPlainAttr ua
-    WHERE uav.attribute_id = ua.id
-  </entry>
-  <entry key="group_search_resource">
-    CREATE VIEW group_search_resource AS
-
-    SELECT st.group_id AS any_id, st.resource_id AS resource_id
-    FROM SyncopeGroup_ExternalResource st
-  </entry>
-
-</properties>
diff --git a/fit/core-reference/src/main/resources/workflow.properties b/fit/core-reference/src/main/resources/workflow.properties
deleted file mode 100644
index 8e5f662..0000000
--- a/fit/core-reference/src/main/resources/workflow.properties
+++ /dev/null
@@ -1,20 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-wf.directory=${conf.directory}
-uwfAdapter=org.apache.syncope.core.workflow.java.DefaultUserWorkflowAdapter
-gwfAdapter=org.apache.syncope.core.workflow.java.DefaultGroupWorkflowAdapter
-awfAdapter=org.apache.syncope.core.workflow.java.DefaultAnyObjectWorkflowAdapter
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 f7e71d1..c52de8a 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
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.fit;
 
+import static org.apache.syncope.fit.AbstractUIITCase.ANONYMOUS_KEY;
+import static org.apache.syncope.fit.AbstractUIITCase.ANONYMOUS_UNAME;
 import static org.awaitility.Awaitility.await;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -108,6 +110,7 @@
 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.AuditService;
 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.CamelRouteService;
@@ -152,19 +155,45 @@
 import org.apache.syncope.common.rest.api.service.wa.U2FRegistrationService;
 import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
 import org.apache.syncope.common.rest.api.service.wa.WebAuthnRegistrationService;
+import org.apache.syncope.fit.AbstractITCase.KeymasterInitializer;
 import org.apache.syncope.fit.core.CoreITContext;
 import org.apache.syncope.fit.core.UserITCase;
 import org.junit.jupiter.api.BeforeAll;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
-import org.apache.syncope.common.rest.api.service.AuditService;
+import org.springframework.test.context.support.TestPropertySourceUtils;
 
-@SpringJUnitConfig({ CoreITContext.class, SelfKeymasterClientContext.class, ZookeeperKeymasterClientContext.class })
+@SpringJUnitConfig(
+        classes = { CoreITContext.class, SelfKeymasterClientContext.class, ZookeeperKeymasterClientContext.class },
+        initializers = KeymasterInitializer.class)
+@TestPropertySource("classpath:test.properties")
 public abstract class AbstractITCase {
 
+    static class KeymasterInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
+
+        @Override
+        public void initialize(final ConfigurableApplicationContext ctx) {
+            String profiles = ctx.getEnvironment().getProperty("springActiveProfiles");
+            if (profiles.contains("zookeeper")) {
+                TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                        ctx, "keymaster.address=127.0.0.1:2181");
+            } else {
+                TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                        ctx, "keymaster.address=http://localhost:9080/syncope/rest/keymaster");
+            }
+            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                    ctx, "keymaster.username=" + ANONYMOUS_UNAME);
+            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                    ctx, "keymaster.password=" + ANONYMOUS_KEY);
+        }
+    }
+
     protected static final Logger LOG = LoggerFactory.getLogger(AbstractITCase.class);
 
     protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@@ -355,23 +384,31 @@
 
     @BeforeAll
     public static void securitySetup() {
-        try (InputStream propStream = AbstractITCase.class.getResourceAsStream("/security.properties")) {
+        try (InputStream propStream = AbstractITCase.class.getResourceAsStream("/core.properties")) {
             Properties props = new Properties();
             props.load(propStream);
 
-            ANONYMOUS_UNAME = props.getProperty("anonymousUser");
-            ANONYMOUS_KEY = props.getProperty("anonymousKey");
-            JWT_ISSUER = props.getProperty("jwtIssuer");
-            JWS_ALGORITHM = JWSAlgorithm.parse(props.getProperty("jwsAlgorithm"));
-            JWS_KEY = props.getProperty("jwsKey");
+            ANONYMOUS_UNAME = props.getProperty("security.anonymousUser");
+            ANONYMOUS_KEY = props.getProperty("security.anonymousKey");
+            JWT_ISSUER = props.getProperty("security.jwtIssuer");
+            JWS_ALGORITHM = JWSAlgorithm.parse(props.getProperty("security.jwsAlgorithm"));
+            JWS_KEY = props.getProperty("security.jwsKey");
         } catch (Exception e) {
-            LOG.error("Could not read security.properties", e);
+            LOG.error("Could not read core.properties", e);
         }
 
         assertNotNull(ANONYMOUS_UNAME);
         assertNotNull(ANONYMOUS_KEY);
         assertNotNull(JWS_KEY);
         assertNotNull(JWT_ISSUER);
+
+        anonymusClient = clientFactory.create(new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
+
+        googleMfaAuthTokenService = anonymusClient.getService(GoogleMfaAuthTokenService.class);
+        googleMfaAuthAccountService = anonymusClient.getService(GoogleMfaAuthAccountService.class);
+        u2fRegistrationService = anonymusClient.getService(U2FRegistrationService.class);
+        webAuthnRegistrationService = anonymusClient.getService(WebAuthnRegistrationService.class);
+        impersonationService = anonymusClient.getService(ImpersonationService.class);
     }
 
     @BeforeAll
@@ -430,14 +467,6 @@
         authProfileService = adminClient.getService(AuthProfileService.class);
         oidcJWKSService = adminClient.getService(OIDCJWKSService.class);
         waConfigService = adminClient.getService(WAConfigService.class);
-
-        anonymusClient = clientFactory.create(new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
-
-        googleMfaAuthTokenService = anonymusClient.getService(GoogleMfaAuthTokenService.class);
-        googleMfaAuthAccountService = anonymusClient.getService(GoogleMfaAuthAccountService.class);
-        u2fRegistrationService = anonymusClient.getService(U2FRegistrationService.class);
-        webAuthnRegistrationService = anonymusClient.getService(WebAuthnRegistrationService.class);
-        impersonationService = anonymusClient.getService(ImpersonationService.class);
     }
 
     protected static String getUUIDString() {
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUIITCase.java
similarity index 86%
rename from fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUITCase.java
rename to fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUIITCase.java
index eed1c08..df9064d 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractUIITCase.java
@@ -19,10 +19,13 @@
 package org.apache.syncope.fit;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.fail;
 
+import java.io.InputStream;
 import java.io.Serializable;
 import java.lang.reflect.Method;
+import java.util.Properties;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.apache.syncope.common.rest.api.service.SyncopeService;
@@ -35,12 +38,13 @@
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.util.tester.WicketTester;
 import org.apache.wicket.util.visit.IVisit;
+import org.junit.jupiter.api.BeforeAll;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public abstract class AbstractUITCase {
+public abstract class AbstractUIITCase {
 
-    protected static final Logger LOG = LoggerFactory.getLogger(AbstractUITCase.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractUIITCase.class);
 
     protected static final String ADMIN_UNAME = "admin";
 
@@ -52,10 +56,30 @@
 
     protected static final String SCHEMA = "schema";
 
+    protected static String ANONYMOUS_UNAME;
+
+    protected static String ANONYMOUS_KEY;
+
     protected static WicketTester TESTER;
 
     protected static SyncopeService SYNCOPE_SERVICE;
 
+    @BeforeAll
+    public static void securitySetup() {
+        try (InputStream propStream = AbstractITCase.class.getResourceAsStream("/core.properties")) {
+            Properties props = new Properties();
+            props.load(propStream);
+
+            ANONYMOUS_UNAME = props.getProperty("security.anonymousUser");
+            ANONYMOUS_KEY = props.getProperty("security.anonymousKey");
+        } catch (Exception e) {
+            LOG.error("Could not read core.properties", e);
+        }
+
+        assertNotNull(ANONYMOUS_UNAME);
+        assertNotNull(ANONYMOUS_KEY);
+    }
+
     protected static <V extends Serializable> Component findComponentByProp(
             final String property, final String path, final V key) {
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/AbstractConsoleITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/AbstractConsoleITCase.java
index b67c386..2e5e220 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/AbstractConsoleITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/AbstractConsoleITCase.java
@@ -18,14 +18,19 @@
  */
 package org.apache.syncope.fit.console;
 
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
 import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration;
 import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository;
 import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder;
 import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties;
 import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator.WicketEndpointRepositoryDefault;
+import java.io.InputStream;
 import java.util.List;
 import java.util.Locale;
+import java.util.Properties;
 import java.util.Set;
+import org.apache.syncope.client.console.ConsoleProperties;
 import org.apache.syncope.client.console.SyncopeAMConsoleContext;
 import org.apache.syncope.client.console.SyncopeIdMConsoleContext;
 import org.apache.syncope.client.console.SyncopeWebApplication;
@@ -41,7 +46,7 @@
 import org.apache.syncope.common.keymaster.client.self.SelfKeymasterClientContext;
 import org.apache.syncope.common.keymaster.client.zookeeper.ZookeeperKeymasterClientContext;
 import org.apache.syncope.common.rest.api.service.SyncopeService;
-import org.apache.syncope.fit.AbstractUITCase;
+import org.apache.syncope.fit.AbstractUIITCase;
 import org.apache.wicket.util.tester.FormTester;
 import org.apache.wicket.util.tester.WicketTester;
 import org.junit.jupiter.api.BeforeAll;
@@ -50,16 +55,27 @@
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
+import org.springframework.test.context.support.TestPropertySourceUtils;
 
-public abstract class AbstractConsoleITCase extends AbstractUITCase {
+public abstract class AbstractConsoleITCase extends AbstractUIITCase {
 
     @ImportAutoConfiguration(classes = { SelfKeymasterClientContext.class, ZookeeperKeymasterClientContext.class })
-    @PropertySource("classpath:console.properties")
     @Configuration
     public static class SyncopeConsoleWebApplicationTestConfig {
 
         @Bean
+        public ConsoleProperties consoleProperties() {
+            ConsoleProperties consoleProperties = new ConsoleProperties();
+
+            consoleProperties.setAnonymousUser(ANONYMOUS_UNAME);
+            consoleProperties.setAnonymousKey(ANONYMOUS_KEY);
+
+            consoleProperties.setCsrf(false);
+
+            return consoleProperties;
+        }
+
+        @Bean
         public GeneralSettingsProperties generalSettingsProperties() {
             return new GeneralSettingsProperties();
         }
@@ -125,10 +141,35 @@
         Locale.setDefault(Locale.ENGLISH);
 
         AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+
         ctx.register(SyncopeConsoleWebApplicationTestConfig.class);
         ctx.register(SyncopeWebApplication.class);
         ctx.register(SyncopeAMConsoleContext.class);
         ctx.register(SyncopeIdMConsoleContext.class);
+
+        String springActiveProfiles = null;
+        try (InputStream propStream = AbstractConsoleITCase.class.getResourceAsStream("/test.properties")) {
+            Properties props = new Properties();
+            props.load(propStream);
+
+            springActiveProfiles = props.getProperty("springActiveProfiles");
+        } catch (Exception e) {
+            LOG.error("Could not load /test.properties", e);
+        }
+        assertNotNull(springActiveProfiles);
+
+        if (springActiveProfiles.contains("zookeeper")) {
+            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                    ctx, "keymaster.address=127.0.0.1:2181");
+        } else {
+            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                    ctx, "keymaster.address=http://localhost:9080/syncope/rest/keymaster");
+        }
+        TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                ctx, "keymaster.username=" + ANONYMOUS_UNAME);
+        TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                ctx, "keymaster.password=" + ANONYMOUS_KEY);
+
         ctx.refresh();
 
         TESTER = new WicketTester(ctx.getBean(SyncopeWebApplication.class));
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
index d0ad892..54f229b 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java
@@ -76,23 +76,31 @@
 
     @BeforeAll
     public static void setUpConnIdBundles() throws IOException {
-        try (InputStream propStream = ConnectorITCase.class.getResourceAsStream("/connid.properties")) {
+        try (InputStream propStream = ConnectorITCase.class.getResourceAsStream("/test.properties")) {
             Properties props = new Properties();
             props.load(propStream);
 
-            for (String location : props.getProperty("connid.locations").split(",")) {
-                if (!location.startsWith("file")) {
-                    connectorServerLocation = location;
-                }
-            }
-
             connIdSoapVersion = props.getProperty("connid.soap.version");
             connIdDbVersion = props.getProperty("connid.database.version");
 
             testJDBCURL = props.getProperty("testdb.url");
         } catch (Exception e) {
-            LOG.error("Could not load /connid.properties", e);
+            LOG.error("Could not load /test.properties", e);
         }
+
+        try (InputStream propStream = ConnectorITCase.class.getResourceAsStream("/core-embedded.properties")) {
+            Properties props = new Properties();
+            props.load(propStream);
+
+            for (String location : props.getProperty("provisioning.connIdLocation").split(",")) {
+                if (!location.startsWith("file")) {
+                    connectorServerLocation = location;
+                }
+            }
+        } catch (Exception e) {
+            LOG.error("Could not load /core-embedded.properties", e);
+        }
+
         assertNotNull(connectorServerLocation);
         assertNotNull(connIdSoapVersion);
         assertNotNull(connIdDbVersion);
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
index 4259e94..2579036 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
@@ -25,9 +25,11 @@
 import static org.junit.jupiter.api.Assertions.fail;
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
+import java.nio.charset.StandardCharsets;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.request.AttrPatch;
@@ -53,7 +55,6 @@
 import org.apache.syncope.common.rest.api.service.UserService;
 import org.apache.syncope.fit.AbstractITCase;
 import org.apache.syncope.fit.ElasticsearchDetector;
-import org.apache.tika.io.IOUtils;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.util.InputStreamContentProvider;
@@ -224,7 +225,7 @@
 
     private static ArrayNode fetchDynRealmsFromElasticsearch(final String userKey) throws Exception {
         String body =
-            '{'
+                '{'
                 + "    \"query\": {"
                 + "        \"match\": {\"_id\": \"" + userKey + "\"}"
                 + "    }"
@@ -235,7 +236,7 @@
         ContentResponse response = httpClient.newRequest("http://localhost:9200/master_user/_search").
                 method(HttpMethod.GET).
                 header(HttpHeader.CONTENT_TYPE, MediaType.APPLICATION_JSON).
-                content(new InputStreamContentProvider(IOUtils.toInputStream(body))).
+                content(new InputStreamContentProvider(IOUtils.toInputStream(body, StandardCharsets.UTF_8))).
                 send();
         assertEquals(HttpStatus.OK_200, response.getStatus());
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
index bceccc7..81df217 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
@@ -98,6 +98,7 @@
 import org.apache.syncope.core.provisioning.java.job.TaskJob;
 import org.apache.syncope.core.spring.security.Encryptor;
 import org.apache.syncope.fit.AbstractITCase;
+import org.apache.syncope.fit.ElasticsearchDetector;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -977,6 +978,14 @@
             assertEquals(TaskJob.Status.SUCCESS.name(), execs.get().get(0).getStatus());
 
             // 6. verify that the user above is now fond on LDAP
+            if (ElasticsearchDetector.isElasticSearchEnabled(adminClient.platform())) {
+                try {
+                    Thread.sleep(2000);
+                } catch (InterruptedException ex) {
+                    // ignore
+                }
+            }
+
             ConnObjectTO userOnLdap =
                     resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
             assertNotNull(userOnLdap);
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AbstractEnduserITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AbstractEnduserITCase.java
index 080bdc6..0e08057 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AbstractEnduserITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AbstractEnduserITCase.java
@@ -18,11 +18,14 @@
  */
 package org.apache.syncope.fit.enduser;
 
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
 import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration;
 import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository;
 import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder;
 import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties;
 import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator.WicketEndpointRepositoryDefault;
+import java.io.InputStream;
 import org.apache.commons.lang3.time.DateFormatUtils;
 import org.apache.syncope.client.enduser.SyncopeWebApplication;
 import org.apache.syncope.client.enduser.commons.PreviewUtils;
@@ -41,7 +44,7 @@
 import org.apache.syncope.common.rest.api.service.SecurityQuestionService;
 import org.apache.syncope.common.rest.api.service.SyncopeService;
 import org.apache.syncope.common.rest.api.service.UserService;
-import org.apache.syncope.fit.AbstractUITCase;
+import org.apache.syncope.fit.AbstractUIITCase;
 import org.apache.wicket.util.tester.FormTester;
 import org.apache.wicket.util.tester.WicketTester;
 import org.junit.jupiter.api.AfterAll;
@@ -53,16 +56,32 @@
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
-import org.springframework.context.annotation.PropertySource;
+import java.util.Properties;
+import org.apache.syncope.client.enduser.EnduserProperties;
+import org.apache.syncope.fit.console.AbstractConsoleITCase;
+import org.springframework.test.context.support.TestPropertySourceUtils;
 
-public abstract class AbstractEnduserITCase extends AbstractUITCase {
+public abstract class AbstractEnduserITCase extends AbstractUIITCase {
 
     @ImportAutoConfiguration(classes = { SelfKeymasterClientContext.class, ZookeeperKeymasterClientContext.class })
-    @PropertySource("classpath:enduser.properties")
     @Configuration
     public static class SyncopeEnduserWebApplicationTestConfig {
 
         @Bean
+        public EnduserProperties enduserProperties() {
+            EnduserProperties enduserProperties = new EnduserProperties();
+
+            enduserProperties.setAdminUser(ADMIN_UNAME);
+
+            enduserProperties.setAnonymousUser(ANONYMOUS_UNAME);
+            enduserProperties.setAnonymousKey(ANONYMOUS_KEY);
+
+            enduserProperties.setCsrf(false);
+
+            return enduserProperties;
+        }
+
+        @Bean
         public GeneralSettingsProperties generalSettingsProperties() {
             return new GeneralSettingsProperties();
         }
@@ -120,8 +139,33 @@
         Locale.setDefault(Locale.ENGLISH);
 
         AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+
         ctx.register(SyncopeEnduserWebApplicationTestConfig.class);
         ctx.register(SyncopeWebApplication.class);
+
+        String springActiveProfiles = null;
+        try (InputStream propStream = AbstractConsoleITCase.class.getResourceAsStream("/test.properties")) {
+            Properties props = new Properties();
+            props.load(propStream);
+
+            springActiveProfiles = props.getProperty("springActiveProfiles");
+        } catch (Exception e) {
+            LOG.error("Could not load /test.properties", e);
+        }
+        assertNotNull(springActiveProfiles);
+
+        if (springActiveProfiles.contains("zookeeper")) {
+            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                    ctx, "keymaster.address=127.0.0.1:2181");
+        } else {
+            TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                    ctx, "keymaster.address=http://localhost:9080/syncope/rest/keymaster");
+        }
+        TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                ctx, "keymaster.username=" + ANONYMOUS_UNAME);
+        TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
+                ctx, "keymaster.password=" + ANONYMOUS_KEY);
+
         ctx.refresh();
 
         TESTER = new WicketTester(ctx.getBean(SyncopeWebApplication.class));
diff --git a/fit/core-reference/src/main/resources/assignPrinterRequest.bpmn20.xml b/fit/core-reference/src/test/resources/assignPrinterRequest.bpmn20.xml
similarity index 100%
rename from fit/core-reference/src/main/resources/assignPrinterRequest.bpmn20.xml
rename to fit/core-reference/src/test/resources/assignPrinterRequest.bpmn20.xml
diff --git a/fit/core-reference/src/test/resources/console.properties b/fit/core-reference/src/test/resources/console.properties
deleted file mode 100644
index 5df90c4..0000000
--- a/fit/core-reference/src/test/resources/console.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-console.directory=${conf.directory}
-
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-
-useGZIPCompression=true
-maxUploadFileSizeMB=5
-
-# Max wait time on apply changes from modals/wizards (given in seconds)
-maxWaitTimeOnApplyChanges=30
-
-reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
-
-page.dashboard=org.apache.syncope.client.console.pages.Dashboard
-page.realms=org.apache.syncope.client.console.pages.Realms
-page.reports=org.apache.syncope.client.console.pages.Reports
-page.audit=org.apache.syncope.client.console.pages.Audit
-page.implementations=org.apache.syncope.client.console.pages.Implementations
-page.logs=org.apache.syncope.client.console.pages.Logs
-page.security=org.apache.syncope.client.console.pages.Security
-page.types=org.apache.syncope.client.console.pages.Types
-page.policies=org.apache.syncope.client.console.pages.Policies
-page.notifications=org.apache.syncope.client.console.pages.Notifications
-page.parameters=org.apache.syncope.client.console.pages.Parameters
-
-default.any.panel.class=org.apache.syncope.client.console.panels.AnyPanel
-
-executor.corePoolSize=50
-executor.maxPoolSize=100
-executor.queueCapacity=10
-
-x-forward=true
-csrf=false
-
-security.headers.X-XSS-Protection=1; mode=block
-security.headers.Strict-Transport-Security=max-age=31536000; includeSubDomains; preload
-security.headers.X-Content-Type-Options=nosniff
-security.headers.X-Frame-Options=sameorigin
-#security.headers.Content-Security-Policy=default-src https:
diff --git a/fit/core-reference/src/main/resources/directorGroupRequest.bpmn20.xml b/fit/core-reference/src/test/resources/directorGroupRequest.bpmn20.xml
similarity index 100%
rename from fit/core-reference/src/main/resources/directorGroupRequest.bpmn20.xml
rename to fit/core-reference/src/test/resources/directorGroupRequest.bpmn20.xml
diff --git a/fit/core-reference/src/test/resources/enduser.properties b/fit/core-reference/src/test/resources/enduser.properties
deleted file mode 100644
index 09a8d6e..0000000
--- a/fit/core-reference/src/test/resources/enduser.properties
+++ /dev/null
@@ -1,41 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-enduser.directory=${conf.directory}
-
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-adminUser=${adminUser}
-useGZIPCompression=true
-
-x-forward=true
-captcha=false
-csrf=false
-
-# Sidebar
-sidebar=org.apache.syncope.client.enduser.panels.Sidebar
-
-# Page
-page.profile=org.apache.syncope.client.enduser.pages.Dashboard
-page.edituser=org.apache.syncope.client.enduser.pages.EditUser
-page.editchangepassword=org.apache.syncope.client.enduser.pages.EditChangePassword
-page.editsecurityquestion=org.apache.syncope.client.enduser.pages.EditSecurityQuestion
-
-security.headers.X-XSS-Protection=1; mode=block
-security.headers.Strict-Transport-Security=max-age=31536000; includeSubDomains; preload
-security.headers.X-Content-Type-Options=nosniff
-security.headers.X-Frame-Options=sameorigin
-#security.headers.Content-Security-Policy=default-src https:
diff --git a/fit/core-reference/src/main/resources/invalidRequest.bpmn20.xml b/fit/core-reference/src/test/resources/invalidRequest.bpmn20.xml
similarity index 100%
rename from fit/core-reference/src/main/resources/invalidRequest.bpmn20.xml
rename to fit/core-reference/src/test/resources/invalidRequest.bpmn20.xml
diff --git a/fit/core-reference/src/test/resources/logic.properties b/fit/core-reference/src/test/resources/logic.properties
deleted file mode 100644
index abab00d..0000000
--- a/fit/core-reference/src/test/resources/logic.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-version=${syncope.version}
-buildNumber=${buildNumber}
-logicInvocationHandler=org.apache.syncope.core.logic.LogicInvocationHandler
-classPathScanImplementationLookup=org.apache.syncope.fit.core.reference.ITImplementationLookup
-enable.jdbcAuditAppender=true
diff --git a/fit/core-reference/src/test/resources/mail.properties b/fit/core-reference/src/test/resources/mail.properties
deleted file mode 100644
index 09300fe..0000000
--- a/fit/core-reference/src/test/resources/mail.properties
+++ /dev/null
@@ -1,30 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-conf.directory=${conf.directory}
-
-smtpHost=localhost
-smtpPort=${testmail.smtpport}
-smtpUser=
-smtpPassword=
-smtpProtocol=smtp
-smtpEncoding=UTF-8
-mail.debug=false
-
-# Add more properties starting with mail.smtp.* from
-# https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties
-mail.smtp.connectiontimeout=3000
-mail.smtp.starttls.enable=false
diff --git a/fit/core-reference/src/test/resources/test.properties b/fit/core-reference/src/test/resources/test.properties
index 65f9c87..b8a67ff 100644
--- a/fit/core-reference/src/test/resources/test.properties
+++ b/fit/core-reference/src/test/resources/test.properties
@@ -28,3 +28,8 @@
 testdb.url=${testdb.url}
 testdb.username=${testdb.username}
 testdb.password=${testdb.password}
+
+connid.soap.version=${connid.soap.version}
+connid.database.version=${connid.database.version}
+
+springActiveProfiles=${spring.profiles.active}
diff --git a/fit/core-reference/src/main/resources/verifyAddedVariables.bpmn20.xml b/fit/core-reference/src/test/resources/verifyAddedVariables.bpmn20.xml
similarity index 100%
rename from fit/core-reference/src/main/resources/verifyAddedVariables.bpmn20.xml
rename to fit/core-reference/src/test/resources/verifyAddedVariables.bpmn20.xml
diff --git a/fit/enduser-reference/pom.xml b/fit/enduser-reference/pom.xml
index 8bc2218..5149416 100644
--- a/fit/enduser-reference/pom.xml
+++ b/fit/enduser-reference/pom.xml
@@ -47,8 +47,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
       <version>${project.version}</version>
     </dependency>
     
@@ -114,7 +114,7 @@
           <configuration>
             <properties>
               <cargo.jvmargs>
-                -Dspring.profiles.active=embedded
+                -Dspring.profiles.active=embedded,all
                 -XX:+CMSClassUnloadingEnabled -Xmx1024m -Xms512m</cargo.jvmargs>
             </properties>
           </configuration>
@@ -198,7 +198,7 @@
               <configuration>
                 <properties>
                   <cargo.jvmargs>
-                    -Dspring.profiles.active=embedded
+                    -Dspring.profiles.active=embedded,all
                     -Dwicket.core.settings.general.configuration-type=development
                     -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
                     -XX:+CMSClassUnloadingEnabled -XX:+UseG1GC -Xmx1024m -Xms512m</cargo.jvmargs>
@@ -270,7 +270,7 @@
               <configuration>
                 <properties>
                   <cargo.jvmargs>
-                    -Dspring.profiles.active=embedded
+                    -Dspring.profiles.active=embedded,all
                     -Dwicket.core.settings.general.configuration-type=development
                     -javaagent:${java.home}/lib/hotswap/hotswap-agent.jar=autoHotswap=true,disablePlugin=Spring,disablePlugin=Hibernate,disablePlugin=CxfJAXRS
                     -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
diff --git a/fit/enduser-reference/src/main/resources/customFormAttributes.json b/fit/enduser-reference/src/main/resources/customFormAttributes.json
deleted file mode 100644
index 9e26dfe..0000000
--- a/fit/enduser-reference/src/main/resources/customFormAttributes.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
\ No newline at end of file
diff --git a/fit/enduser-reference/src/main/resources/customTemplate.json b/fit/enduser-reference/src/main/resources/customTemplate.json
deleted file mode 100644
index e55ed4c..0000000
--- a/fit/enduser-reference/src/main/resources/customTemplate.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
-  "templates": 
-          {
-            "login":
-                    {
-                      "templateUrl": "views/templates/selfTemplate.html",
-                      "css": [
-                        "css/login.css"
-                      ]
-                    },
-
-            "edit_user":
-                    {
-                      "templateUrl": "views/templates/editUserTemplate.html",
-                      "css": [
-                        "css/editUser.css"
-                      ]
-                    },
-
-            "password_reset":
-                    {
-                      "templateUrl": "views/templates/passwordresetTemplate.html",
-                      "css": [
-                        "css/editUser.css",
-                        "css/passwordReset.css"
-                      ]
-                    },
-            "must_change_password":
-                    {
-                      "templateUrl": "views/templates/mustChangePasswordTemplate.html",
-                      "css": [
-                        "css/editUser.css",
-                        "css/passwordReset.css"
-                      ]
-                    },
-            "confirm_password_reset":
-                    {
-                      "templateUrl": "views/templates/confirmPasswordResetTemplate.html",
-                      "css": [
-                        "css/editUser.css",
-                        "css/passwordReset.css"
-                      ]
-                    }
-          },
-
-  "generalAssets": 
-          {
-            "css": [
-              "css/notification.css",
-              "css/app.css",
-              "css/accessibility.css"
-            ]
-          },
-
-  "wizard": 
-          {
-            "firstStep": "credentials",
-            "steps": {
-              "credentials": {
-                "url": "/credentials"
-              },
-              "groups": {
-                "url": "/groups"
-              },
-              "plainSchemas": {
-                "url": "/plainSchemas"
-              },
-              "derivedSchemas": {
-                "url": "/derivedSchemas"
-              },
-              "virtualSchemas": {
-                "url": "/virtualSchemas"
-              },
-              "resources": {
-                "url": "/resources"
-              },
-              "finish": {
-                "url": "/finish"
-              }
-            }
-          }
-}
\ No newline at end of file
diff --git a/fit/enduser-reference/src/main/resources/application-embedded.properties b/fit/enduser-reference/src/main/resources/enduser-embedded.properties
similarity index 87%
rename from fit/enduser-reference/src/main/resources/application-embedded.properties
rename to fit/enduser-reference/src/main/resources/enduser-embedded.properties
index e420cad..69009e5 100644
--- a/fit/enduser-reference/src/main/resources/application-embedded.properties
+++ b/fit/enduser-reference/src/main/resources/enduser-embedded.properties
@@ -14,6 +14,10 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+keymaster.address=http://localhost:9080/syncope/rest/keymaster
+keymaster.username=${anonymousUser}
+keymaster.password=${anonymousKey}
+
 service.discovery.address=http://localhost:9080/syncope-enduser/
 
 spring.devtools.livereload.enabled=false
diff --git a/fit/enduser-reference/src/main/resources/enduser.properties b/fit/enduser-reference/src/main/resources/enduser.properties
deleted file mode 100644
index b4061af..0000000
--- a/fit/enduser-reference/src/main/resources/enduser.properties
+++ /dev/null
@@ -1,42 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-enduser.directory=${conf.directory}
-
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-adminUser=${adminUser}
-useGZIPCompression=true
-maxUploadFileSizeMB=5
-
-x-forward=true
-captcha=true
-csrf=true
-
-# Sidebar
-sidebar=org.apache.syncope.client.enduser.panels.Sidebar
-
-# Page
-page.profile=org.apache.syncope.client.enduser.pages.Dashboard
-page.edituser=org.apache.syncope.client.enduser.pages.EditUser
-page.editchangepassword=org.apache.syncope.client.enduser.pages.EditChangePassword
-page.editsecurityquestion=org.apache.syncope.client.enduser.pages.EditSecurityQuestion
-
-security.headers.X-XSS-Protection=1; mode=block
-security.headers.Strict-Transport-Security=max-age=31536000; includeSubDomains; preload
-security.headers.X-Content-Type-Options=nosniff
-security.headers.X-Frame-Options=sameorigin
-#security.headers.Content-Security-Policy=default-src https:
diff --git a/fit/enduser-reference/src/main/resources/keymaster.properties b/fit/enduser-reference/src/main/resources/keymaster.properties
deleted file mode 100644
index 033fe3b..0000000
--- a/fit/enduser-reference/src/main/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
diff --git a/fit/wa-reference/pom.xml b/fit/wa-reference/pom.xml
index e7f5357..df8d66b 100644
--- a/fit/wa-reference/pom.xml
+++ b/fit/wa-reference/pom.xml
@@ -47,8 +47,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-      <artifactId>syncope-ext-self-keymaster-client</artifactId>
+      <groupId>org.apache.syncope.common.keymaster.self</groupId>
+      <artifactId>syncope-common-keymaster-client-self</artifactId>
       <version>${project.version}</version>
     </dependency>
 
@@ -132,7 +132,7 @@
           <configuration>
             <properties>
               <cargo.jvmargs>
-                -Dspring.profiles.active=embedded 
+                -Dspring.profiles.active=embedded,all
                 -XX:+CMSClassUnloadingEnabled -Xmx1024m -Xms512m</cargo.jvmargs>
             </properties>
           </configuration>
@@ -276,7 +276,7 @@
               <configuration>
                 <properties>
                   <cargo.jvmargs>
-                    -Dspring.profiles.active=embedded
+                    -Dspring.profiles.active=embedded,all
                     -Dwicket.core.settings.general.configuration-type=development
                     -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
                     -XX:+CMSClassUnloadingEnabled -XX:+UseG1GC -Xmx1024m -Xms512m</cargo.jvmargs>
@@ -332,7 +332,7 @@
               <configuration>
                 <properties>
                   <cargo.jvmargs>
-                    -Dspring.profiles.active=embedded
+                    -Dspring.profiles.active=embedded,all
                     -Dwicket.core.settings.general.configuration-type=development
                     -javaagent:${java.home}/lib/hotswap/hotswap-agent.jar=autoHotswap=true,disablePlugin=Spring,disablePlugin=Hibernate,disablePlugin=CxfJAXRS
                     -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
diff --git a/fit/wa-reference/src/main/resources/application-embedded.properties b/fit/wa-reference/src/main/resources/application-embedded.properties
deleted file mode 100644
index 464c8c8..0000000
--- a/fit/wa-reference/src/main/resources/application-embedded.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.main.banner-mode=console
-service.discovery.address=http://localhost:9080/syncope-wa/
diff --git a/fit/wa-reference/src/main/resources/keymaster.properties b/fit/wa-reference/src/main/resources/keymaster.properties
deleted file mode 100644
index 033fe3b..0000000
--- a/fit/wa-reference/src/main/resources/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
diff --git a/fit/wa-reference/src/main/resources/log4j2.xml b/fit/wa-reference/src/main/resources/log4j2.xml
index 8916f42..5eca0fd 100644
--- a/fit/wa-reference/src/main/resources/log4j2.xml
+++ b/fit/wa-reference/src/main/resources/log4j2.xml
@@ -36,17 +36,19 @@
   </appenders>
 
   <loggers>
-    <asyncLogger name="org.apereo.cas" additivity="false" level="DEBUG">
+    <asyncLogger name="org.apereo.cas" additivity="false" level="INFO">
       <appender-ref ref="main"/>
     </asyncLogger>
-    <asyncLogger name="org.apereo.services.persondir" additivity="false" level="DEBUG">
+
+    <asyncLogger name="org.apereo.services.persondir" additivity="false" level="INFO">
       <appender-ref ref="main"/>
     </asyncLogger>
+
     <asyncLogger name="org.apereo.inspektr.audit.support" additivity="false" level="INFO">
       <appender-ref ref="main"/>
     </asyncLogger>
 
-    <asyncLogger name="org.pac4j" additivity="false" level="INFO">
+    <asyncLogger name="org.pac4j" additivity="false" level="ERROR">
       <appender-ref ref="main"/>
     </asyncLogger>
 
@@ -68,7 +70,6 @@
     <root level="INFO">
       <appender-ref ref="main"/>
     </root>
-  
   </loggers>
   
 </configuration>
diff --git a/client/idrepo/console/src/test/resources/application-debug.properties b/fit/wa-reference/src/main/resources/wa-embedded.properties
similarity index 67%
copy from client/idrepo/console/src/test/resources/application-debug.properties
copy to fit/wa-reference/src/main/resources/wa-embedded.properties
index 1ac160d..87fae00 100644
--- a/client/idrepo/console/src/test/resources/application-debug.properties
+++ b/fit/wa-reference/src/main/resources/wa-embedded.properties
@@ -18,5 +18,15 @@
 keymaster.username=${anonymousUser}
 keymaster.password=${anonymousKey}
 
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
+cas.server.name=http://localhost:9080
+cas.server.prefix=${cas.server.name}/syncope-wa
+
+conf.directory=${conf.directory}
+cas.standalone.configuration-directory=${conf.directory}
+
+cas.authn.saml-idp.core.entity-id=${cas.server.prefix}/saml
+cas.authn.saml-idp.metadata.http.metadata-backup-location=file:${conf.directory}/saml
+
+cas.authn.oidc.core.issuer=${cas.server.prefix}/oidc
+
+service.discovery.address=http://localhost:9080/syncope-wa/
diff --git a/fit/wa-reference/src/main/resources/wa.properties b/fit/wa-reference/src/main/resources/wa.properties
deleted file mode 100644
index 6d76e2a..0000000
--- a/fit/wa-reference/src/main/resources/wa.properties
+++ /dev/null
@@ -1,62 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-
-useGZIPCompression=true
-
-# Conf directories
-conf.directory=${conf.directory}
-cas.standalone.configuration-directory=${conf.directory}
-
-cas.server.name=http://localhost:9080
-cas.server.prefix=${cas.server.name}/syncope-wa
-cas.server.scope=syncope.org
-
-cas.tgc.secure=false
-cas.logout.follow-service-redirects=true
-
-cas.authn.saml-idp.core.entity-id=${cas.server.name}/syncope-wa/saml
-cas.authn.saml-idp.metadata.http.metadata-backup-location=file:${conf.directory}/saml
-
-cas.authn.oidc.core.issuer=${cas.server.name}/syncope-wa/oidc
-cas.authn.oidc.discovery.id-token-signing-alg-values-supported=RS256,RS384,RS512,PS256,PS384,PS512,ES256,ES384,ES512,HS256,HS384,HS512
-cas.authn.oidc.discovery.user-info-signing-alg-values-supported=RS256,RS384,RS512,PS256,PS384,PS512,ES256,ES384,ES512,HS256,HS384,HS512
-cas.authn.oauth.user-profile-view-type=FLAT
-
-# Disable access to the login endpoint
-# if no target application is specified.
-cas.sso.allow-missing-service-parameter=true
-
-# Disable the acceptable usage policy
-# by default for now.
-cas.acceptable-usage-policy.core.enabled=false
-
-spring.security.user.name=${anonymousUser}
-spring.security.user.password=${anonymousKey}
-
-springdoc.show-actuator=true
-springdoc.model-and-view-allowed=true
-springdoc.writer-with-default-pretty-printer=true
-springdoc.swagger-ui.displayRequestDuration=true
-
-cas.authn.mfa.web-authn.core.allowed-origins=${cas.server.name}
-cas.authn.mfa.web-authn.core.application-id=https://localhost:8443
-cas.authn.mfa.web-authn.core.relying-party-name=Syncope 
-cas.authn.mfa.web-authn.core.relying-party-id=syncope.apache.org
-
-cas.authn.syncope.url=${cas.server.name}/syncope/rest/
diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
index f03574e..22d0e7c 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/AbstractSRAITCase.java
@@ -171,7 +171,7 @@
                 "-Dreactor.netty.http.server.accessLogEnabled=true",
                 "-jar",
                 "-Xdebug",
-                "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5006",
+                "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8002",
                 sraJar);
         processBuilder.inheritIO();
 
diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/OAUTH2SRAITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/OAUTH2SRAITCase.java
index 430c1ac..bcd7369 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/OAUTH2SRAITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/OAUTH2SRAITCase.java
@@ -49,16 +49,16 @@
         assumeTrue(OAUTH2SRAITCase.class.equals(MethodHandles.lookup().lookupClass()));
 
         Properties props = new Properties();
-        try (InputStream propStream = OAUTH2SRAITCase.class.getResourceAsStream("/application-oauth2.properties")) {
+        try (InputStream propStream = OAUTH2SRAITCase.class.getResourceAsStream("/sra-oauth2.properties")) {
             props.load(propStream);
         } catch (Exception e) {
-            fail("Could not load /application-oauth2.properties", e);
+            fail("Could not load /sra-oauth2.properties", e);
         }
-        CLIENT_ID = props.getProperty("am.oauth2.client.id");
+        CLIENT_ID = props.getProperty("sra.oauth2.client-id");
         assertNotNull(CLIENT_ID);
-        CLIENT_SECRET = props.getProperty("am.oauth2.client.secret");
+        CLIENT_SECRET = props.getProperty("sra.oauth2.client-secret");
         assertNotNull(CLIENT_SECRET);
-        TOKEN_URI = props.getProperty("am.oauth2.tokenUri");
+        TOKEN_URI = props.getProperty("sra.oauth2.tokenUri");
         assertNotNull(TOKEN_URI);
 
         oidcClientAppSetup(OAUTH2SRAITCase.class.getName(), "OAUTH2", 2L, CLIENT_ID, CLIENT_SECRET);
diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/OIDCSRAITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/OIDCSRAITCase.java
index af5fdfd..24d1ec8 100644
--- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/OIDCSRAITCase.java
+++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/sra/OIDCSRAITCase.java
@@ -125,14 +125,14 @@
         assumeTrue(OIDCSRAITCase.class.equals(MethodHandles.lookup().lookupClass()));
 
         Properties props = new Properties();
-        try (InputStream propStream = OIDCSRAITCase.class.getResourceAsStream("/application-oidc.properties")) {
+        try (InputStream propStream = OIDCSRAITCase.class.getResourceAsStream("/sra-oidc.properties")) {
             props.load(propStream);
         } catch (Exception e) {
-            fail("Could not load /application-oidc.properties", e);
+            fail("Could not load /sra-oidc.properties", e);
         }
-        CLIENT_ID = props.getProperty("am.oidc.client.id");
+        CLIENT_ID = props.getProperty("sra.oidc.client-id");
         assertNotNull(CLIENT_ID);
-        CLIENT_SECRET = props.getProperty("am.oidc.client.secret");
+        CLIENT_SECRET = props.getProperty("sra.oidc.client-secret");
         assertNotNull(CLIENT_SECRET);
         TOKEN_URI = WA_ADDRESS + "/oidc/accessToken";
 
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 24115ea..151c06d 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
@@ -119,6 +119,7 @@
         assertEquals(1, idps.size());
 
         SAML2SP4UIIdPTO cas = idps.get(0);
+        cas.setEntityID(WA_ADDRESS + "/saml");
         cas.setName("CAS");
         cas.setCreateUnmatching(true);
         cas.setSelfRegUnmatching(false);
@@ -184,7 +185,7 @@
         assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
 
         // 2. click on the SAML 2.0 IdP
-        get = new HttpGet(baseURL + SAML2SP4UIConstants.URL_CONTEXT 
+        get = new HttpGet(baseURL + SAML2SP4UIConstants.URL_CONTEXT
                 + "/login?idp=http%3A//localhost%3A9080/syncope-wa/saml");
         response = httpclient.execute(get, context);
 
diff --git a/fit/wa-reference/src/test/resources/application-cas.properties b/fit/wa-reference/src/test/resources/application-cas.properties
deleted file mode 100644
index acc6237..0000000
--- a/fit/wa-reference/src/test/resources/application-cas.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-am.type=CAS
-am.cas.server.name=http://localhost:80
-am.cas.url.prefix=http://localhost:9080/syncope-wa/
-
-global.postLogout=http://localhost:8080/logout
diff --git a/fit/wa-reference/src/test/resources/application-oauth2.properties b/fit/wa-reference/src/test/resources/application-oauth2.properties
deleted file mode 100644
index eb8df47..0000000
--- a/fit/wa-reference/src/test/resources/application-oauth2.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-am.type=OAUTH2
-am.oauth2.tokenUri=http://localhost:9080/syncope-wa/oauth2.0/accessToken
-am.oauth2.authorizationUri=http://localhost:9080/syncope-wa/oauth2.0/authorize
-am.oauth2.userInfoUri=http://localhost:9080/syncope-wa/oauth2.0/profile
-am.oauth2.userNameAttributeName=id
-am.oauth2.scopes=
-am.oauth2.jwkSetUri=
-am.oauth2.issuer=http://localhost:9080/syncope-wa
-am.oauth2.client.id=oauth2TestClientId
-am.oauth2.client.secret=oauth2TestClientSecret
-
-global.postLogout=http://localhost:8080/logout
diff --git a/fit/wa-reference/src/test/resources/application-oidc.properties b/fit/wa-reference/src/test/resources/application-oidc.properties
deleted file mode 100644
index ae41565..0000000
--- a/fit/wa-reference/src/test/resources/application-oidc.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-am.type=OIDC
-am.oidc.configuration=http://localhost:9080/syncope-wa/oidc
-am.oidc.client.id=oidcTestClientId
-am.oidc.client.secret=oidcTestClientSecret
-
-global.postLogout=http://localhost:8080/logout
diff --git a/fit/wa-reference/src/test/resources/application-saml2.properties b/fit/wa-reference/src/test/resources/application-saml2.properties
deleted file mode 100644
index 48f70ed..0000000
--- a/fit/wa-reference/src/test/resources/application-saml2.properties
+++ /dev/null
@@ -1,30 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-am.type=SAML2
-am.saml2.sp.authnrequest.binding=POST
-am.saml2.sp.logout.request.binding=POST
-am.saml2.sp.logout.response.binding=REDIRECT
-am.saml2.sp.entityId=http://localhost:8080
-am.saml2.sp.metadata=/tmp/saml2-sp-metadata.xml
-am.saml2.sp.skew=300
-am.saml2.idp=http://localhost:9080/syncope-wa/idp/metadata
-am.saml2.keystore=classpath:/saml.keystore.jks
-am.saml2.keystore.type=jks
-am.saml2.keystore.storepass=changeit
-am.saml2.keystore.keypass=changeit
-
-global.postLogout=http://localhost:8080/logout
diff --git a/client/idrepo/console/src/test/resources/application-debug.properties b/fit/wa-reference/src/test/resources/sra-cas.properties
similarity index 84%
copy from client/idrepo/console/src/test/resources/application-debug.properties
copy to fit/wa-reference/src/test/resources/sra-cas.properties
index 1ac160d..6a93eea 100644
--- a/client/idrepo/console/src/test/resources/application-debug.properties
+++ b/fit/wa-reference/src/test/resources/sra-cas.properties
@@ -18,5 +18,8 @@
 keymaster.username=${anonymousUser}
 keymaster.password=${anonymousKey}
 
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
+sra.am-type=CAS
+sra.cas.server-name=http://localhost:9080
+sra.cas.server-prefix=http://localhost:9080/syncope-wa/
+
+sra.global.postLogout=http://localhost:8080/logout
diff --git a/fit/wa-reference/src/test/resources/keymaster.properties b/fit/wa-reference/src/test/resources/sra-oauth2.properties
similarity index 64%
rename from fit/wa-reference/src/test/resources/keymaster.properties
rename to fit/wa-reference/src/test/resources/sra-oauth2.properties
index 033fe3b..79caad4 100644
--- a/fit/wa-reference/src/test/resources/keymaster.properties
+++ b/fit/wa-reference/src/test/resources/sra-oauth2.properties
@@ -17,3 +17,16 @@
 keymaster.address=http://localhost:9080/syncope/rest/keymaster
 keymaster.username=${anonymousUser}
 keymaster.password=${anonymousKey}
+
+sra.am-type=OAUTH2
+sra.oauth2.tokenUri=http://localhost:9080/syncope-wa/oauth2.0/accessToken
+sra.oauth2.authorizationUri=http://localhost:9080/syncope-wa/oauth2.0/authorize
+sra.oauth2.userInfoUri=http://localhost:9080/syncope-wa/oauth2.0/profile
+sra.oauth2.userNameAttributeName=id
+sra.oauth2.scopes=
+sra.oauth2.jwkSetUri=
+sra.oauth2.issuer=http://localhost:9080/syncope-wa
+sra.oauth2.client-id=oauth2TestClientId
+sra.oauth2.client-secret=oauth2TestClientSecret
+
+sra.global.postLogout=http://localhost:8080/logout
diff --git a/client/idrepo/console/src/test/resources/application-debug.properties b/fit/wa-reference/src/test/resources/sra-oidc.properties
similarity index 81%
copy from client/idrepo/console/src/test/resources/application-debug.properties
copy to fit/wa-reference/src/test/resources/sra-oidc.properties
index 1ac160d..ee407ec 100644
--- a/client/idrepo/console/src/test/resources/application-debug.properties
+++ b/fit/wa-reference/src/test/resources/sra-oidc.properties
@@ -18,5 +18,9 @@
 keymaster.username=${anonymousUser}
 keymaster.password=${anonymousKey}
 
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
+sra.am-type=OIDC
+sra.oidc.configuration=http://localhost:9080/syncope-wa/oidc
+sra.oidc.client-id=oidcTestClientId
+sra.oidc.client-secret=oidcTestClientSecret
+
+sra.global.postLogout=http://localhost:8080/logout
diff --git a/client/idrepo/console/src/test/resources/application-debug.properties b/fit/wa-reference/src/test/resources/sra-saml2.properties
similarity index 63%
copy from client/idrepo/console/src/test/resources/application-debug.properties
copy to fit/wa-reference/src/test/resources/sra-saml2.properties
index 1ac160d..4553f98 100644
--- a/client/idrepo/console/src/test/resources/application-debug.properties
+++ b/fit/wa-reference/src/test/resources/sra-saml2.properties
@@ -18,5 +18,17 @@
 keymaster.username=${anonymousUser}
 keymaster.password=${anonymousKey}
 
-server.port=9090
-service.discovery.address=http://localhost:9090/syncope-console/
+sra.am-type=SAML2
+sra.saml2.authn-request-binding=POST
+sra.saml2.logout-request-binding=POST
+sra.saml2.logout-response-binding=REDIRECT
+sra.saml2.entityId=http://localhost:8080
+sra.saml2.skew=300
+sra.saml2.sp-metadata-file-path=/tmp/saml2-sp-metadata.xml
+sra.saml2.idp-metadata=http://localhost:9080/syncope-wa/idp/metadata
+sra.saml2.keystore=classpath:/saml.keystore.jks
+sra.saml2.keystore-type=jks
+sra.saml2.keystore-storepass=changeit
+sra.saml2.keystore-keypass=changeit
+
+sra.global.postLogout=http://localhost:8080/logout
diff --git a/fit/wa-reference/src/test/resources/test.properties b/fit/wa-reference/src/test/resources/test.properties
index 44f0b1a..ee29130 100644
--- a/fit/wa-reference/src/test/resources/test.properties
+++ b/fit/wa-reference/src/test/resources/test.properties
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 java.home=${java.home}
-sra.jar=${settings.localRepository}/org/apache/syncope/syncope-sra/${project.version}/syncope-sra-${project.version}.jar
-keymaster-api.jar=${settings.localRepository}/org/apache/syncope/ext/self-keymaster/syncope-ext-self-keymaster-rest-api/${project.version}/syncope-ext-self-keymaster-rest-api-${project.version}.jar
-keymaster-client.jar=${settings.localRepository}/org/apache/syncope/ext/self-keymaster/syncope-ext-self-keymaster-client/${project.version}/syncope-ext-self-keymaster-client-${project.version}.jar
+sra.jar=${basedir}/../../sra/target/syncope-sra-${project.version}-exec.jar
+keymaster-api.jar=${settings.localRepository}/org/apache/syncope/common/keymaster/self/syncope-common-keymaster-self-rest-api/${project.version}/syncope-common-keymaster-self-rest-api-${project.version}.jar
+keymaster-client.jar=${settings.localRepository}/org/apache/syncope/common/keymaster/self/syncope-common-keymaster-client-self/${project.version}/syncope-common-keymaster-client-self-${project.version}.jar
 targetTestClasses=${project.build.testOutputDirectory}
diff --git a/pom.xml b/pom.xml
index feb5a76..c4e1f70 100644
--- a/pom.xml
+++ b/pom.xml
@@ -411,7 +411,7 @@
 
     <jackson.version>2.12.4</jackson.version>
 
-    <spring-boot.version>2.5.2</spring-boot.version>
+    <spring-boot.version>2.5.3</spring-boot.version>
     <spring-cloud-gateway.version>3.0.3</spring-cloud-gateway.version>
 
     <openjpa.version>3.2.0</openjpa.version>
@@ -518,11 +518,11 @@
     <docker.mysql.version>8.0</docker.mysql.version>
     <docker.mariadb.version>10.6</docker.mariadb.version>
 
-    <jdbc.postgresql.version>42.2.22</jdbc.postgresql.version>
+    <jdbc.postgresql.version>42.2.23</jdbc.postgresql.version>
     <jdbc.mysql.version>8.0.22</jdbc.mysql.version>
     <jdbc.mariadb.version>2.7.3</jdbc.mariadb.version>
     <jdbc.mssql.version>9.2.1.jre</jdbc.mssql.version>
-    <jdbc.oracle.version>19.11.0.0</jdbc.oracle.version>
+    <jdbc.oracle.version>21.1.0.0</jdbc.oracle.version>
 
     <conf.directory>${project.build.directory}/test-classes</conf.directory>
     <bundles.directory>${project.build.directory}/bundles</bundles.directory>
@@ -925,6 +925,18 @@
 
       <dependency>
         <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-validation</artifactId>
+        <version>${spring-boot.version}</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.hibernate.validator</groupId>
+            <artifactId>hibernate-validator</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+
+      <dependency>
+        <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-logging</artifactId>
         <exclusions>
           <exclusion>
@@ -1035,7 +1047,7 @@
       <dependency>
         <groupId>org.apache.tika</groupId>
         <artifactId>tika-core</artifactId>
-        <version>1.27</version>
+        <version>2.0.0</version>
       </dependency>
 
       <dependency>
diff --git a/sra/pom.xml b/sra/pom.xml
index 15a34c1..cfa3a79 100644
--- a/sra/pom.xml
+++ b/sra/pom.xml
@@ -111,6 +111,11 @@
     </dependency>
 
     <dependency>
+      <groupId>org.apache.bval</groupId>
+      <artifactId>bval-jsr</artifactId>
+    </dependency>
+
+    <dependency>
       <groupId>org.gaul</groupId>
       <artifactId>modernizer-maven-annotations</artifactId>
     </dependency>
@@ -197,18 +202,23 @@
       <plugin>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-maven-plugin</artifactId>
-        <configuration>
-          <layout>ZIP</layout>
-        </configuration>
         <executions>
           <execution>
+            <id>build-info</id>
             <goals>
               <goal>build-info</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>repackage</id>
+            <configuration>
+              <layout>ZIP</layout>
+              <classifier>exec</classifier>
+              <attach>false</attach>
+            </configuration>
+            <goals>
               <goal>repackage</goal>
             </goals>
-            <configuration>
-              <outputDirectory>${project.build.outputDirectory}</outputDirectory>
-            </configuration>
           </execution>
         </executions>
       </plugin>
@@ -264,8 +274,8 @@
 
       <dependencies>
         <dependency>
-          <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-          <artifactId>syncope-ext-self-keymaster-client</artifactId>
+          <groupId>org.apache.syncope.common.keymaster.self</groupId>
+          <artifactId>syncope-common-keymaster-client-self</artifactId>
           <version>${project.version}</version>
         </dependency>
       </dependencies>
diff --git a/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java b/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
index 296a881..8c404ac 100644
--- a/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
+++ b/sra/src/main/java/org/apache/syncope/sra/RouteProvider.java
@@ -44,8 +44,6 @@
 import org.apache.syncope.sra.predicates.CustomRoutePredicateFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.gateway.filter.GatewayFilter;
 import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
 import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory;
@@ -88,7 +86,6 @@
 import org.springframework.core.Ordered;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatus;
-import org.springframework.stereotype.Component;
 import org.springframework.util.unit.DataSize;
 import org.springframework.web.server.ServerWebExchange;
 import org.apache.syncope.common.rest.api.service.SRARouteService;
@@ -98,32 +95,40 @@
 import org.springframework.cloud.gateway.filter.factory.SetRequestHostHeaderGatewayFilterFactory;
 import org.springframework.cloud.gateway.handler.predicate.WeightRoutePredicateFactory;
 
-@Component
 public class RouteProvider {
 
-    private static final Logger LOG = LoggerFactory.getLogger(RouteProvider.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(RouteProvider.class);
 
-    @Autowired
-    private ServiceOps serviceOps;
+    protected final ServiceOps serviceOps;
 
-    @Autowired
-    private ConfigurableApplicationContext ctx;
+    protected final ConfigurableApplicationContext ctx;
 
-    @Value("${anonymousUser}")
-    private String anonymousUser;
+    protected final String anonymousUser;
 
-    @Value("${anonymousKey}")
-    private String anonymousKey;
+    protected final String anonymousKey;
 
-    @Value("${useGZIPCompression}")
-    private boolean useGZIPCompression;
+    protected final boolean useGZIPCompression;
 
-    private SyncopeClient client;
+    protected SyncopeClient client;
 
-    private final List<SRARouteTO> routeTOs = new ArrayList<>();
+    protected final List<SRARouteTO> routeTOs = new ArrayList<>();
+
+    public RouteProvider(
+            final ServiceOps serviceOps,
+            final ConfigurableApplicationContext ctx,
+            final String anonymousUser,
+            final String anonymousKey,
+            final boolean useGZIPCompression) {
+
+        this.serviceOps = serviceOps;
+        this.ctx = ctx;
+        this.anonymousUser = anonymousUser;
+        this.anonymousKey = anonymousKey;
+        this.useGZIPCompression = useGZIPCompression;
+    }
 
     @SuppressWarnings("unchecked")
-    private GatewayFilter toFilter(final SRARouteTO route, final SRARouteFilter gwfilter)
+    protected GatewayFilter toFilter(final SRARouteTO route, final SRARouteFilter gwfilter)
             throws ClassNotFoundException, NumberFormatException {
 
         GatewayFilter filter;
@@ -399,7 +404,7 @@
         return filter instanceof Ordered ? filter : new OrderedGatewayFilter(filter, 0);
     }
 
-    private AsyncPredicate<ServerWebExchange> toPredicate(final SRARoutePredicate gwpredicate, final boolean negate)
+    protected AsyncPredicate<ServerWebExchange> toPredicate(final SRARoutePredicate gwpredicate, final boolean negate)
             throws ClassNotFoundException, NumberFormatException {
 
         AsyncPredicate<ServerWebExchange> predicate;
@@ -504,7 +509,7 @@
         return negate ? predicate.negate() : predicate;
     }
 
-    private Route.AsyncBuilder toRoute(final SRARouteTO gwroute) {
+    protected Route.AsyncBuilder toRoute(final SRARouteTO gwroute) {
         Route.AsyncBuilder builder = new Route.AsyncBuilder().
                 id(gwroute.getKey()).order(gwroute.getOrder()).uri(gwroute.getTarget());
 
diff --git a/sra/src/main/java/org/apache/syncope/sra/SRAProperties.java b/sra/src/main/java/org/apache/syncope/sra/SRAProperties.java
new file mode 100644
index 0000000..b9fb36f
--- /dev/null
+++ b/sra/src/main/java/org/apache/syncope/sra/SRAProperties.java
@@ -0,0 +1,395 @@
+/*
+ * 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.sra;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.syncope.common.lib.SyncopeProperties;
+import org.apache.syncope.common.lib.types.SAML2BindingType;
+import org.jasig.cas.client.Protocol;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.security.oauth2.core.oidc.OidcScopes;
+
+@ConfigurationProperties(SRAProperties.PREFIX)
+public class SRAProperties extends SyncopeProperties {
+
+    public static final String PREFIX = "sra";
+
+    public static final String AM_TYPE = "am-type";
+
+    public static class Global {
+
+        private URI error = URI.create("/error");
+
+        private URI postLogout = URI.create("/logout");
+
+        public URI getError() {
+            return error;
+        }
+
+        public void setError(final URI error) {
+            this.error = error;
+        }
+
+        public URI getPostLogout() {
+            return postLogout;
+        }
+
+        public void setPostLogout(final URI postLogout) {
+            this.postLogout = postLogout;
+        }
+    }
+
+    public enum AMType {
+        OIDC,
+        OAUTH2,
+        SAML2,
+        CAS
+
+    }
+
+    public static class OIDC {
+
+        private String configuration;
+
+        private String clientId;
+
+        private String clientSecret;
+
+        private List<String> scopes = Arrays.asList(
+                OidcScopes.OPENID,
+                OidcScopes.ADDRESS,
+                OidcScopes.EMAIL,
+                OidcScopes.PHONE,
+                OidcScopes.PROFILE);
+
+        public String getConfiguration() {
+            return configuration;
+        }
+
+        public void setConfiguration(final String configuration) {
+            this.configuration = configuration;
+        }
+
+        public String getClientId() {
+            return clientId;
+        }
+
+        public void setClientId(final String clientId) {
+            this.clientId = clientId;
+        }
+
+        public String getClientSecret() {
+            return clientSecret;
+        }
+
+        public void setClientSecret(final String clientSecret) {
+            this.clientSecret = clientSecret;
+        }
+
+        public List<String> getScopes() {
+            return scopes;
+        }
+
+        public void setScopes(final List<String> scopes) {
+            this.scopes = scopes;
+        }
+    }
+
+    public static class OAUTH2 {
+
+        private String tokenUri;
+
+        private String authorizationUri;
+
+        private String userInfoUri;
+
+        private String userNameAttributeName;
+
+        private String jwkSetUri;
+
+        private String issuer;
+
+        private String clientId;
+
+        private String clientSecret;
+
+        private List<String> scopes = new ArrayList<>();
+
+        public String getTokenUri() {
+            return tokenUri;
+        }
+
+        public void setTokenUri(final String tokenUri) {
+            this.tokenUri = tokenUri;
+        }
+
+        public String getAuthorizationUri() {
+            return authorizationUri;
+        }
+
+        public void setAuthorizationUri(final String authorizationUri) {
+            this.authorizationUri = authorizationUri;
+        }
+
+        public String getUserInfoUri() {
+            return userInfoUri;
+        }
+
+        public void setUserInfoUri(final String userInfoUri) {
+            this.userInfoUri = userInfoUri;
+        }
+
+        public String getUserNameAttributeName() {
+            return userNameAttributeName;
+        }
+
+        public void setUserNameAttributeName(final String userNameAttributeName) {
+            this.userNameAttributeName = userNameAttributeName;
+        }
+
+        public String getJwkSetUri() {
+            return jwkSetUri;
+        }
+
+        public void setJwkSetUri(final String jwkSetUri) {
+            this.jwkSetUri = jwkSetUri;
+        }
+
+        public String getIssuer() {
+            return issuer;
+        }
+
+        public void setIssuer(final String issuer) {
+            this.issuer = issuer;
+        }
+
+        public String getClientId() {
+            return clientId;
+        }
+
+        public void setClientId(final String clientId) {
+            this.clientId = clientId;
+        }
+
+        public String getClientSecret() {
+            return clientSecret;
+        }
+
+        public void setClientSecret(final String clientSecret) {
+            this.clientSecret = clientSecret;
+        }
+
+        public List<String> getScopes() {
+            return scopes;
+        }
+
+        public void setScopes(final List<String> scopes) {
+            this.scopes = scopes;
+        }
+    }
+
+    public static class SAML2 {
+
+        private SAML2BindingType authnRequestBinding = SAML2BindingType.POST;
+
+        private SAML2BindingType logoutRequestBinding = SAML2BindingType.POST;
+
+        private SAML2BindingType logoutResponseBinding = SAML2BindingType.REDIRECT;
+
+        private String entityId;
+
+        private long skew;
+
+        private String spMetadataFilePath;
+
+        private String idpMetadata;
+
+        private String keystore;
+
+        private String keystoreType;
+
+        private String keystoreStorepass;
+
+        private String keystoreKeypass;
+
+        public SAML2BindingType getAuthnRequestBinding() {
+            return authnRequestBinding;
+        }
+
+        public void setAuthnRequestBinding(final SAML2BindingType authnRequestBinding) {
+            this.authnRequestBinding = authnRequestBinding;
+        }
+
+        public SAML2BindingType getLogoutRequestBinding() {
+            return logoutRequestBinding;
+        }
+
+        public void setLogoutRequestBinding(final SAML2BindingType logoutRequestBinding) {
+            this.logoutRequestBinding = logoutRequestBinding;
+        }
+
+        public SAML2BindingType getLogoutResponseBinding() {
+            return logoutResponseBinding;
+        }
+
+        public void setLogoutResponseBinding(final SAML2BindingType logoutResponseBinding) {
+            this.logoutResponseBinding = logoutResponseBinding;
+        }
+
+        public String getEntityId() {
+            return entityId;
+        }
+
+        public void setEntityId(final String entityId) {
+            this.entityId = entityId;
+        }
+
+        public long getSkew() {
+            return skew;
+        }
+
+        public void setSkew(final int skew) {
+            this.skew = skew;
+        }
+
+        public String getSpMetadataFilePath() {
+            return spMetadataFilePath;
+        }
+
+        public void setSpMetadataFilePath(final String spMetadataFilePath) {
+            this.spMetadataFilePath = spMetadataFilePath;
+        }
+
+        public String getIdpMetadata() {
+            return idpMetadata;
+        }
+
+        public void setIdpMetadata(final String idpMetadata) {
+            this.idpMetadata = idpMetadata;
+        }
+
+        public String getKeystore() {
+            return keystore;
+        }
+
+        public void setKeystore(final String keystore) {
+            this.keystore = keystore;
+        }
+
+        public String getKeystoreType() {
+            return keystoreType;
+        }
+
+        public void setKeystoreType(final String keystoreType) {
+            this.keystoreType = keystoreType;
+        }
+
+        public String getKeystoreStorePass() {
+            return keystoreStorepass;
+        }
+
+        public void setKeystoreStorePass(final String keystoreStorePass) {
+            this.keystoreStorepass = keystoreStorePass;
+        }
+
+        public String getKeystoreKeypass() {
+            return keystoreKeypass;
+        }
+
+        public void setKeystoreKeypass(final String keystoreKeyPass) {
+            this.keystoreKeypass = keystoreKeyPass;
+        }
+    }
+
+    public static class CAS {
+
+        private String serverName;
+
+        private String serverPrefix;
+
+        private Protocol protocol = Protocol.CAS3;
+
+        public String getServerName() {
+            return serverName;
+        }
+
+        public void setServerName(final String serverName) {
+            this.serverName = serverName;
+        }
+
+        public String getServerPrefix() {
+            return serverPrefix;
+        }
+
+        public void setServerPrefix(final String serverPrefix) {
+            this.serverPrefix = serverPrefix;
+        }
+
+        public Protocol getProtocol() {
+            return protocol;
+        }
+
+        public void setProtocol(final Protocol protocol) {
+            this.protocol = protocol;
+        }
+    }
+
+    private final Global global = new Global();
+
+    private AMType amType = AMType.OIDC;
+
+    private final OIDC oidc = new OIDC();
+
+    private final OAUTH2 oauth2 = new OAUTH2();
+
+    private final SAML2 saml2 = new SAML2();
+
+    private final CAS cas = new CAS();
+
+    public Global getGlobal() {
+        return global;
+    }
+
+    public AMType getAmType() {
+        return amType;
+    }
+
+    public void setAmType(final AMType amType) {
+        this.amType = amType;
+    }
+
+    public OIDC getOidc() {
+        return oidc;
+    }
+
+    public OAUTH2 getOauth2() {
+        return oauth2;
+    }
+
+    public SAML2 getSaml2() {
+        return saml2;
+    }
+
+    public CAS getCas() {
+        return cas;
+    }
+}
diff --git a/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java b/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
index 8a285fb..139780d 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SecurityConfig.java
@@ -24,7 +24,6 @@
 import java.security.cert.X509Certificate;
 import java.text.ParseException;
 import java.util.Map;
-import java.util.Objects;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.types.SAML2BindingType;
@@ -37,7 +36,6 @@
 import org.apache.syncope.sra.security.saml2.SAML2MetadataEndpoint;
 import org.apache.syncope.sra.security.saml2.SAML2SecurityConfigUtils;
 import org.apache.syncope.sra.security.saml2.SAML2WebSsoAuthenticationWebFilter;
-import org.jasig.cas.client.Protocol;
 import org.pac4j.core.http.callback.NoParameterCallbackUrlResolver;
 import org.pac4j.saml.client.SAML2Client;
 import org.pac4j.saml.config.SAML2Configuration;
@@ -52,7 +50,6 @@
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
 import org.springframework.core.convert.converter.Converter;
-import org.springframework.core.env.Environment;
 import org.springframework.core.io.FileUrlResource;
 import org.springframework.core.io.support.ResourcePatternResolver;
 import org.springframework.http.HttpMethod;
@@ -66,7 +63,6 @@
 import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
 import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.OAuth2TokenValidator;
-import org.springframework.security.oauth2.core.oidc.OidcScopes;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtValidators;
 import org.springframework.security.oauth2.jwt.MappedJwtClaimSetConverter;
@@ -82,25 +78,15 @@
 @Configuration
 public class SecurityConfig {
 
-    public static final String AM_TYPE = "am.type";
-
-    public enum AMType {
-        OIDC,
-        OAUTH2,
-        SAML2,
-        CAS
-
-    }
-
     @Autowired
     private ResourcePatternResolver resourceResolver;
 
     @Autowired
-    private Environment env;
+    private SRAProperties props;
 
     @Bean
     @Order(0)
-    @ConditionalOnProperty(name = AM_TYPE, havingValue = "SAML2")
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "SAML2")
     public SecurityWebFilterChain saml2SecurityFilterChain(final ServerHttpSecurity http) {
         ServerWebExchangeMatcher metadataMatcher =
                 ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, SAML2MetadataEndpoint.METADATA_URL);
@@ -124,32 +110,30 @@
     @Bean
     public MapReactiveUserDetailsService userDetailsService() {
         UserDetails user = User.builder().
-                username(Objects.requireNonNull(env.getProperty("anonymousUser"))).
-                password("{noop}" + env.getProperty("anonymousKey")).
+                username(props.getAnonymousUser()).
+                password("{noop}" + props.getAnonymousKey()).
                 roles(IdRepoEntitlement.ANONYMOUS).
                 build();
         return new MapReactiveUserDetailsService(user);
     }
 
     @Bean
-    @ConditionalOnProperty(name = AM_TYPE, havingValue = "OIDC")
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "OIDC")
     public InMemoryReactiveClientRegistrationRepository oidcClientRegistrationRepository() {
         return new InMemoryReactiveClientRegistrationRepository(
-                ClientRegistrations.fromOidcIssuerLocation(env.getProperty("am.oidc.configuration")).
-                        registrationId("OIDC").
-                        clientId(env.getProperty("am.oidc.client.id")).
-                        clientSecret(env.getProperty("am.oidc.client.secret")).
-                        scope(env.getProperty("am.oidc.scopes", String[].class,
-                                new String[] { OidcScopes.OPENID, OidcScopes.ADDRESS, OidcScopes.EMAIL,
-                                    OidcScopes.PHONE, OidcScopes.PROFILE })).
+                ClientRegistrations.fromOidcIssuerLocation(props.getOidc().getConfiguration()).
+                        registrationId(SRAProperties.AMType.OIDC.name()).
+                        clientId(props.getOidc().getClientId()).
+                        clientSecret(props.getOidc().getClientSecret()).
+                        scope(props.getOidc().getScopes().toArray(new String[0])).
                         build());
     }
 
     @Bean
     @ConditionalOnMissingBean
-    @ConditionalOnProperty(name = AM_TYPE, havingValue = "OIDC")
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "OIDC")
     public OAuth2TokenValidator<Jwt> oidcJWTValidator() {
-        return JwtValidators.createDefaultWithIssuer(env.getProperty("am.oidc.configuration"));
+        return JwtValidators.createDefaultWithIssuer(props.getOidc().getConfiguration());
     }
 
     @Bean
@@ -160,7 +144,7 @@
 
     @Bean
     @ConditionalOnMissingBean
-    @ConditionalOnProperty(name = AM_TYPE, havingValue = "OIDC")
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "OIDC")
     public ReactiveJwtDecoder oidcJWTDecoder() {
         NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSetUri(
                 oidcClientRegistrationRepository().iterator().next().getProviderDetails().getJwkSetUri()).build();
@@ -170,36 +154,35 @@
     }
 
     @Bean
-    @ConditionalOnProperty(name = AM_TYPE, havingValue = "OAUTH2")
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "OAUTH2")
     public InMemoryReactiveClientRegistrationRepository oauth2ClientRegistrationRepository() {
         return new InMemoryReactiveClientRegistrationRepository(
-                ClientRegistration.withRegistrationId("OAUTH2").
+                ClientRegistration.withRegistrationId(SRAProperties.AMType.OAUTH2.name()).
                         redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}").
-                        tokenUri(env.getProperty("am.oauth2.tokenUri")).
-                        authorizationUri(env.getProperty("am.oauth2.authorizationUri")).
-                        userInfoUri(env.getProperty("am.oauth2.userInfoUri")).
-                        userNameAttributeName(env.getProperty("am.oauth2.userNameAttributeName")).
-                        clientId(env.getProperty("am.oauth2.client.id")).
-                        clientSecret(env.getProperty("am.oauth2.client.secret")).
-                        scope(env.getProperty("am.oauth2.scopes", String[].class)).
+                        tokenUri(props.getOauth2().getTokenUri()).
+                        authorizationUri(props.getOauth2().getAuthorizationUri()).
+                        userInfoUri(props.getOauth2().getUserInfoUri()).
+                        userNameAttributeName(props.getOauth2().getUserNameAttributeName()).
+                        clientId(props.getOauth2().getClientId()).
+                        clientSecret(props.getOauth2().getClientSecret()).
+                        scope(props.getOauth2().getScopes().toArray(new String[0])).
                         authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).
-                        jwkSetUri(env.getProperty("am.oauth2.jwkSetUri")).
+                        jwkSetUri(props.getOauth2().getJwkSetUri()).
                         build());
     }
 
     @Bean
     @ConditionalOnMissingBean
-    @ConditionalOnProperty(name = AM_TYPE, havingValue = "OAUTH2")
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "OAUTH2")
     public OAuth2TokenValidator<Jwt> oauth2JWTValidator() {
-        String issuer = env.getProperty("am.oauth2.issuer");
-        return issuer == null
+        return props.getOauth2().getIssuer() == null
                 ? JwtValidators.createDefault()
-                : JwtValidators.createDefaultWithIssuer(env.getProperty("am.oauth2.issuer"));
+                : JwtValidators.createDefaultWithIssuer(props.getOauth2().getIssuer());
     }
 
     @Bean
     @ConditionalOnMissingBean
-    @ConditionalOnProperty(name = AM_TYPE, havingValue = "OAUTH2")
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "OAUTH2")
     public ReactiveJwtDecoder oauth2JWTDecoder() {
         String jwkSetUri = oauth2ClientRegistrationRepository().iterator().next().getProviderDetails().getJwkSetUri();
         NimbusReactiveJwtDecoder jwtDecoder;
@@ -220,15 +203,16 @@
     }
 
     @Bean
-    @ConditionalOnProperty(name = AM_TYPE, havingValue = "SAML2")
+    @ConditionalOnMissingBean
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "SAML2")
     public SAML2Client saml2Client() {
         SAML2Configuration cfg = new SAML2Configuration(
-                resourceResolver.getResource(env.getProperty("am.saml2.keystore")),
-                env.getProperty("am.saml2.keystore.storepass"),
-                env.getProperty("am.saml2.keystore.keypass"),
-                resourceResolver.getResource(env.getProperty("am.saml2.idp")));
+                resourceResolver.getResource(props.getSaml2().getKeystore()),
+                props.getSaml2().getKeystoreStorePass(),
+                props.getSaml2().getKeystoreKeypass(),
+                resourceResolver.getResource(props.getSaml2().getIdpMetadata()));
 
-        cfg.setKeystoreType(env.getProperty("am.saml2.keystore.type"));
+        cfg.setKeystoreType(props.getSaml2().getKeystoreType());
         if (cfg.getKeystoreResource() instanceof FileUrlResource) {
             cfg.setKeystoreGenerator(new BaseSAML2KeystoreGenerator(cfg) {
 
@@ -248,27 +232,24 @@
             });
         }
 
-        cfg.setAuthnRequestBindingType(
-                SAML2BindingType.valueOf(env.getProperty("am.saml2.sp.authnrequest.binding")).getUri());
+        cfg.setAuthnRequestBindingType(props.getSaml2().getAuthnRequestBinding().getUri());
         cfg.setResponseBindingType(SAML2BindingType.POST.getUri());
-        cfg.setSpLogoutRequestBindingType(
-                SAML2BindingType.valueOf(env.getProperty("am.saml2.sp.logout.request.binding")).getUri());
-        cfg.setSpLogoutResponseBindingType(
-                SAML2BindingType.valueOf(env.getProperty("am.saml2.sp.logout.response.binding")).getUri());
+        cfg.setSpLogoutRequestBindingType(props.getSaml2().getLogoutRequestBinding().getUri());
+        cfg.setSpLogoutResponseBindingType(props.getSaml2().getLogoutResponseBinding().getUri());
 
-        cfg.setServiceProviderEntityId(env.getProperty("am.saml2.sp.entityId"));
+        cfg.setServiceProviderEntityId(props.getSaml2().getEntityId());
 
         cfg.setWantsAssertionsSigned(true);
         cfg.setAuthnRequestSigned(true);
         cfg.setSpLogoutRequestSigned(true);
-        cfg.setServiceProviderMetadataResourceFilepath(env.getProperty("am.saml2.sp.metadata"));
-        cfg.setAcceptedSkew(env.getProperty("am.saml2.sp.skew", int.class));
+        cfg.setServiceProviderMetadataResourceFilepath(props.getSaml2().getSpMetadataFilePath());
+        cfg.setAcceptedSkew(props.getSaml2().getSkew());
 
         cfg.setLogoutHandler(new NoOpLogoutHandler());
 
         SAML2Client saml2Client = new SAML2Client(cfg);
-        saml2Client.setName(AMType.SAML2.name());
-        saml2Client.setCallbackUrl(env.getProperty("am.saml2.sp.entityId")
+        saml2Client.setName(SRAProperties.AMType.SAML2.name());
+        saml2Client.setCallbackUrl(props.getSaml2().getEntityId()
                 + SAML2WebSsoAuthenticationWebFilter.FILTER_PROCESSES_URI);
         saml2Client.setCallbackUrlResolver(new NoParameterCallbackUrlResolver());
         saml2Client.init();
@@ -278,7 +259,7 @@
 
     @Bean
     @Order(2)
-    @ConditionalOnProperty(name = AM_TYPE)
+    @ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE)
     public SecurityWebFilterChain routesSecurityFilterChain(
             final ServerHttpSecurity http,
             final CacheManager cacheManager,
@@ -287,17 +268,15 @@
             final CsrfRouteMatcher csrfRouteMatcher,
             final ConfigurableApplicationContext ctx) {
 
-        AMType amType = AMType.valueOf(env.getProperty(AM_TYPE));
-
         ServerHttpSecurity.AuthorizeExchangeSpec builder = http.authorizeExchange().
                 matchers(publicRouteMatcher).permitAll().
                 anyExchange().authenticated();
 
-        switch (amType) {
+        switch (props.getAmType()) {
             case OIDC:
             case OAUTH2:
-                OAuth2SecurityConfigUtils.forLogin(http, amType, ctx);
-                OAuth2SecurityConfigUtils.forLogout(builder, amType, cacheManager, logoutRouteMatcher, ctx);
+                OAuth2SecurityConfigUtils.forLogin(http, props.getAmType(), ctx);
+                OAuth2SecurityConfigUtils.forLogout(builder, props.getAmType(), cacheManager, logoutRouteMatcher, ctx);
                 http.oauth2ResourceServer().jwt().jwtDecoder(ctx.getBean(ReactiveJwtDecoder.class));
                 break;
 
@@ -310,14 +289,14 @@
             case CAS:
                 CASSecurityConfigUtils.forLogin(
                         http,
-                        env.getProperty("am.cas.server.name"),
-                        Protocol.CAS3,
-                        env.getProperty("am.cas.url.prefix"),
+                        props.getCas().getServerName(),
+                        props.getCas().getProtocol(),
+                        props.getCas().getServerPrefix(),
                         publicRouteMatcher);
                 CASSecurityConfigUtils.forLogout(
                         builder,
                         cacheManager,
-                        env.getProperty("am.cas.url.prefix"),
+                        props.getCas().getServerPrefix(),
                         logoutRouteMatcher,
                         ctx);
                 break;
diff --git a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
index 131c2d8..ceff488 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAApplication.java
@@ -18,40 +18,62 @@
  */
 package org.apache.syncope.sra;
 
+import org.apache.syncope.common.keymaster.client.api.ServiceOps;
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStart;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop;
 import org.apache.syncope.sra.actuate.SRASessions;
 import org.apache.syncope.sra.actuate.SyncopeCoreHealthIndicator;
+import org.apache.syncope.sra.actuate.SyncopeSRAInfoContributor;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.cache.CacheManager;
 import org.springframework.cloud.gateway.route.Route;
 import org.springframework.cloud.gateway.route.RouteLocator;
+import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.PropertySource;
 import reactor.core.publisher.Flux;
 
-@PropertySource("classpath:sra.properties")
-@PropertySource(value = "file:${conf.directory}/sra.properties", ignoreResourceNotFound = true)
 @SpringBootApplication
+@EnableConfigurationProperties(SRAProperties.class)
 public class SyncopeSRAApplication {
 
     public static void main(final String[] args) {
-        SpringApplication.run(SyncopeSRAApplication.class, args);
+        new SpringApplicationBuilder(SyncopeSRAApplication.class).
+                properties("spring.config.name:sra").
+                build().run(args);
     }
 
     @Autowired
-    private RouteProvider provider;
+    private ServiceOps serviceOps;
+
+    @Autowired
+    private SRAProperties props;
 
     @Autowired
     private CacheManager cacheManager;
 
+    @Autowired
+    private ConfigurableApplicationContext ctx;
+
+    @ConditionalOnMissingBean
+    @Bean
+    public RouteProvider routeProvider() {
+        return new RouteProvider(
+                serviceOps,
+                ctx,
+                props.getAnonymousUser(),
+                props.getAnonymousKey(),
+                props.isUseGZIPCompression());
+    }
+
+    @ConditionalOnMissingBean
     @Bean
     public RouteLocator routes() {
-        return () -> Flux.fromIterable(provider.fetch()).map(Route.AbstractBuilder::build);
+        return () -> Flux.fromIterable(routeProvider().fetch()).map(Route.AbstractBuilder::build);
     }
 
     @ConditionalOnMissingBean
@@ -63,7 +85,17 @@
     @ConditionalOnMissingBean
     @Bean
     public SyncopeCoreHealthIndicator syncopeCoreHealthIndicator() {
-        return new SyncopeCoreHealthIndicator();
+        return new SyncopeCoreHealthIndicator(
+                serviceOps,
+                props.getAnonymousUser(),
+                props.getAnonymousKey(),
+                props.isUseGZIPCompression());
+    }
+
+    @ConditionalOnMissingBean
+    @Bean
+    public SyncopeSRAInfoContributor syncopeSRAInfoContributor() {
+        return new SyncopeSRAInfoContributor();
     }
 
     @Bean
diff --git a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAWebExceptionHandler.java b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAWebExceptionHandler.java
index 8c9c91a..38251dc 100644
--- a/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAWebExceptionHandler.java
+++ b/sra/src/main/java/org/apache/syncope/sra/SyncopeSRAWebExceptionHandler.java
@@ -31,7 +31,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
 import org.springframework.cloud.gateway.support.NotFoundException;
 import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
@@ -59,8 +58,8 @@
     @Autowired
     private RouteProvider routeProvider;
 
-    @Value("${global.error}")
-    private URI globalError;
+    @Autowired
+    private SRAProperties props;
 
     @Override
     public void onApplicationEvent(final RefreshRoutesEvent event) {
@@ -68,7 +67,7 @@
     }
 
     private URI getError(final ServerWebExchange exchange) {
-        URI error = globalError;
+        URI error = props.getGlobal().getError();
         String routeId = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
         if (StringUtils.isNotBlank(routeId)) {
             Optional<URI> routeError = Optional.ofNullable(CACHE.get(routeId)).orElseGet(() -> {
diff --git a/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java b/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java
index 8b679f8..6c2af78 100644
--- a/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java
+++ b/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java
@@ -25,8 +25,6 @@
 import org.apache.syncope.common.rest.api.service.SRARouteService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.actuate.health.Health;
 import org.springframework.boot.actuate.health.HealthIndicator;
 import org.springframework.boot.actuate.health.Status;
@@ -35,17 +33,25 @@
 
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeCoreHealthIndicator.class);
 
-    @Autowired
-    protected ServiceOps serviceOps;
+    protected final ServiceOps serviceOps;
 
-    @Value("${anonymousUser}")
-    protected String anonymousUser;
+    protected final String anonymousUser;
 
-    @Value("${anonymousKey}")
-    protected String anonymousKey;
+    protected final String anonymousKey;
 
-    @Value("${useGZIPCompression:false}")
-    protected boolean useGZIPCompression;
+    protected final boolean useGZIPCompression;
+
+    public SyncopeCoreHealthIndicator(
+            final ServiceOps serviceOps,
+            final String anonymousUser,
+            final String anonymousKey,
+            final boolean useGZIPCompression) {
+
+        this.serviceOps = serviceOps;
+        this.anonymousUser = anonymousUser;
+        this.anonymousKey = anonymousKey;
+        this.useGZIPCompression = useGZIPCompression;
+    }
 
     @Override
     public Health health() {
diff --git a/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeSRAInfoContributor.java b/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeSRAInfoContributor.java
new file mode 100644
index 0000000..d8dd1f6
--- /dev/null
+++ b/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeSRAInfoContributor.java
@@ -0,0 +1,37 @@
+/*
+ * 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.sra.actuate;
+
+import org.apache.syncope.sra.SRAProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.info.Info;
+import org.springframework.boot.actuate.info.InfoContributor;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+public class SyncopeSRAInfoContributor implements InfoContributor {
+
+    @Autowired
+    protected SRAProperties sraProperties;
+
+    @PreAuthorize("isAuthenticated()")
+    @Override
+    public void contribute(final Info.Builder builder) {
+        builder.withDetail("sraProperties", sraProperties);
+    }
+}
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/AbstractServerLogoutSuccessHandler.java b/sra/src/main/java/org/apache/syncope/sra/security/AbstractServerLogoutSuccessHandler.java
index 3133d39..b6b8d6c 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/AbstractServerLogoutSuccessHandler.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/AbstractServerLogoutSuccessHandler.java
@@ -25,9 +25,9 @@
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.to.SRARouteTO;
 import org.apache.syncope.sra.RouteProvider;
+import org.apache.syncope.sra.SRAProperties;
 import org.apache.syncope.sra.security.web.server.DoNothingIfCommittedServerRedirectStrategy;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
 import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
 import org.springframework.context.ApplicationListener;
@@ -45,8 +45,8 @@
     @Autowired
     private RouteProvider routeProvider;
 
-    @Value("${global.postLogout}")
-    private URI globalPostLogout;
+    @Autowired
+    private SRAProperties props;
 
     @Override
     public void onApplicationEvent(final RefreshRoutesEvent event) {
@@ -54,7 +54,7 @@
     }
 
     protected URI getPostLogout(final WebFilterExchange exchange) {
-        URI postLogout = globalPostLogout;
+        URI postLogout = props.getGlobal().getPostLogout();
         String routeId = exchange.getExchange().getAttribute(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
         if (StringUtils.isNotBlank(routeId)) {
             Optional<URI> routePostLogout = Optional.ofNullable(CACHE.get(routeId)).orElseGet(() -> {
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/oauth2/OAuth2SecurityConfigUtils.java b/sra/src/main/java/org/apache/syncope/sra/security/oauth2/OAuth2SecurityConfigUtils.java
index a9bb25e..8a9c788 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/oauth2/OAuth2SecurityConfigUtils.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/oauth2/OAuth2SecurityConfigUtils.java
@@ -20,7 +20,7 @@
 
 import java.util.Set;
 import org.apache.syncope.sra.ApplicationContextUtils;
-import org.apache.syncope.sra.SecurityConfig.AMType;
+import org.apache.syncope.sra.SRAProperties;
 import org.apache.syncope.sra.security.LogoutRouteMatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -60,13 +60,13 @@
 
     private static final Logger LOG = LoggerFactory.getLogger(OAuth2SecurityConfigUtils.class);
 
-    private static ReactiveAuthenticationManager authenticationManager(final AMType amType) {
+    private static ReactiveAuthenticationManager authenticationManager(final SRAProperties.AMType amType) {
         WebClientReactiveAuthorizationCodeTokenResponseClient client =
                 new WebClientReactiveAuthorizationCodeTokenResponseClient();
         ReactiveAuthenticationManager authenticationManager =
                 new OAuth2LoginReactiveAuthenticationManager(client, new DefaultReactiveOAuth2UserService());
 
-        if (AMType.OIDC == amType) {
+        if (SRAProperties.AMType.OIDC == amType) {
             OidcAuthorizationCodeReactiveAuthenticationManager oidc =
                     new OidcAuthorizationCodeReactiveAuthenticationManager(client, new OidcReactiveOAuth2UserService());
             authenticationManager = new DelegatingReactiveAuthenticationManager(oidc, authenticationManager);
@@ -77,7 +77,7 @@
 
     public static void forLogin(
             final ServerHttpSecurity http,
-            final AMType amType,
+            final SRAProperties.AMType amType,
             final ApplicationContext ctx) {
 
         ReactiveClientRegistrationRepository clientRegistrationRepository =
@@ -112,7 +112,7 @@
 
     public static void forLogout(
             final ServerHttpSecurity.AuthorizeExchangeSpec builder,
-            final AMType amType,
+            final SRAProperties.AMType amType,
             final CacheManager cacheManager,
             final LogoutRouteMatcher logoutRouteMatcher,
             final ConfigurableApplicationContext ctx) {
@@ -121,7 +121,7 @@
         logoutWebFilter.setRequiresLogoutMatcher(logoutRouteMatcher);
         logoutWebFilter.setLogoutHandler(new OAuth2SessionRemovalServerLogoutHandler(cacheManager));
 
-        if (AMType.OIDC == amType) {
+        if (SRAProperties.AMType.OIDC == amType) {
             try {
                 OidcClientInitiatedServerLogoutSuccessHandler handler = ApplicationContextUtils.getOrCreateBean(
                         ctx,
diff --git a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2MetadataEndpoint.java b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2MetadataEndpoint.java
index 5a9b6a2..8b8a1d2 100644
--- a/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2MetadataEndpoint.java
+++ b/sra/src/main/java/org/apache/syncope/sra/security/saml2/SAML2MetadataEndpoint.java
@@ -20,7 +20,7 @@
 
 import com.google.common.net.HttpHeaders;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.sra.SecurityConfig;
+import org.apache.syncope.sra.SRAProperties;
 import org.opensaml.saml.common.xml.SAMLConstants;
 import org.opensaml.saml.saml2.metadata.EntityDescriptor;
 import org.pac4j.saml.client.SAML2Client;
@@ -36,7 +36,7 @@
 
 @RestController
 @RequestMapping(SAML2MetadataEndpoint.METADATA_URL)
-@ConditionalOnProperty(name = SecurityConfig.AM_TYPE, havingValue = "SAML2")
+@ConditionalOnProperty(prefix = SRAProperties.PREFIX, name = SRAProperties.AM_TYPE, havingValue = "SAML2")
 public class SAML2MetadataEndpoint {
 
     public static final String METADATA_URL = "/saml2/metadata";
diff --git a/sra/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java b/sra/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java
new file mode 100644
index 0000000..eef444b
--- /dev/null
+++ b/sra/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2013-2020 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.springframework.cloud.gateway.config;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchProviderException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.validation.constraints.Max;
+
+import reactor.netty.resources.ConnectionProvider;
+import reactor.netty.tcp.SslProvider;
+import reactor.netty.transport.ProxyProvider;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.server.WebServerException;
+import org.springframework.core.style.ToStringCreator;
+import org.springframework.util.ResourceUtils;
+import org.springframework.util.unit.DataSize;
+//import org.springframework.validation.annotation.Validated;
+
+// CHECKSTYLE:OFF
+/**
+ * Configuration properties for the Netty {@link reactor.netty.http.client.HttpClient}.
+ */
+@ConfigurationProperties("spring.cloud.gateway.httpclient")
+//@Validated
+@SuppressWarnings("deprecation")
+public class HttpClientProperties {
+
+	/** The connect timeout in millis, the default is 45s. */
+	private Integer connectTimeout;
+
+	/** The response timeout. */
+	private Duration responseTimeout;
+
+	/** The max response header size. */
+	private DataSize maxHeaderSize;
+
+	/** The max initial line length. */
+	private DataSize maxInitialLineLength;
+
+	/** Pool configuration for Netty HttpClient. */
+	private Pool pool = new Pool();
+
+	/** Proxy configuration for Netty HttpClient. */
+	private Proxy proxy = new Proxy();
+
+	/** SSL configuration for Netty HttpClient. */
+	private Ssl ssl = new Ssl();
+
+	/** Websocket configuration for Netty HttpClient. */
+	private Websocket websocket = new Websocket();
+
+	/** Enables wiretap debugging for Netty HttpClient. */
+	private boolean wiretap;
+
+	/** Enables compression for Netty HttpClient. */
+	private boolean compression;
+
+	public Integer getConnectTimeout() {
+		return connectTimeout;
+	}
+
+	public void setConnectTimeout(Integer connectTimeout) {
+		this.connectTimeout = connectTimeout;
+	}
+
+	public Duration getResponseTimeout() {
+		return responseTimeout;
+	}
+
+	public void setResponseTimeout(Duration responseTimeout) {
+		this.responseTimeout = responseTimeout;
+	}
+
+	@Max(Integer.MAX_VALUE)
+	public DataSize getMaxHeaderSize() {
+		return maxHeaderSize;
+	}
+
+	public void setMaxHeaderSize(DataSize maxHeaderSize) {
+		this.maxHeaderSize = maxHeaderSize;
+	}
+
+	@Max(Integer.MAX_VALUE)
+	public DataSize getMaxInitialLineLength() {
+		return maxInitialLineLength;
+	}
+
+	public void setMaxInitialLineLength(DataSize maxInitialLineLength) {
+		this.maxInitialLineLength = maxInitialLineLength;
+	}
+
+	public Pool getPool() {
+		return pool;
+	}
+
+	public void setPool(Pool pool) {
+		this.pool = pool;
+	}
+
+	public Proxy getProxy() {
+		return proxy;
+	}
+
+	public void setProxy(Proxy proxy) {
+		this.proxy = proxy;
+	}
+
+	public Ssl getSsl() {
+		return ssl;
+	}
+
+	public void setSsl(Ssl ssl) {
+		this.ssl = ssl;
+	}
+
+	public Websocket getWebsocket() {
+		return this.websocket;
+	}
+
+	public void setWebsocket(Websocket websocket) {
+		this.websocket = websocket;
+	}
+
+	public boolean isWiretap() {
+		return this.wiretap;
+	}
+
+	public void setWiretap(boolean wiretap) {
+		this.wiretap = wiretap;
+	}
+
+	public boolean isCompression() {
+		return compression;
+	}
+
+	public void setCompression(boolean compression) {
+		this.compression = compression;
+	}
+
+	@Override
+	public String toString() {
+		// @formatter:off
+		return new ToStringCreator(this)
+				.append("connectTimeout", connectTimeout)
+				.append("responseTimeout", responseTimeout)
+				.append("maxHeaderSize", maxHeaderSize)
+				.append("maxInitialLineLength", maxInitialLineLength)
+				.append("pool", pool)
+				.append("proxy", proxy)
+				.append("ssl", ssl)
+				.append("websocket", websocket)
+				.append("wiretap", wiretap)
+				.append("compression", compression)
+				.toString();
+		// @formatter:on
+
+	}
+
+	public static class Pool {
+
+		/** Type of pool for HttpClient to use, defaults to ELASTIC. */
+		private PoolType type = PoolType.ELASTIC;
+
+		/** The channel pool map name, defaults to proxy. */
+		private String name = "proxy";
+
+		/**
+		 * Only for type FIXED, the maximum number of connections before starting pending
+		 * acquisition on existing ones.
+		 */
+		private Integer maxConnections = ConnectionProvider.DEFAULT_POOL_MAX_CONNECTIONS;
+
+		/** Only for type FIXED, the maximum time in millis to wait for aquiring. */
+		private Long acquireTimeout = ConnectionProvider.DEFAULT_POOL_ACQUIRE_TIMEOUT;
+
+		/**
+		 * Time in millis after which the channel will be closed. If NULL, there is no max
+		 * idle time.
+		 */
+		private Duration maxIdleTime = null;
+
+		/**
+		 * Duration after which the channel will be closed. If NULL, there is no max life
+		 * time.
+		 */
+		private Duration maxLifeTime = null;
+
+		/**
+		 * Perform regular eviction checks in the background at a specified interval.
+		 * Disabled by default ({@link Duration#ZERO})
+		 */
+		private Duration evictionInterval = Duration.ZERO;
+
+		public PoolType getType() {
+			return type;
+		}
+
+		public void setType(PoolType type) {
+			this.type = type;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			this.name = name;
+		}
+
+		public Integer getMaxConnections() {
+			return maxConnections;
+		}
+
+		public void setMaxConnections(Integer maxConnections) {
+			this.maxConnections = maxConnections;
+		}
+
+		public Long getAcquireTimeout() {
+			return acquireTimeout;
+		}
+
+		public void setAcquireTimeout(Long acquireTimeout) {
+			this.acquireTimeout = acquireTimeout;
+		}
+
+		public Duration getMaxIdleTime() {
+			return maxIdleTime;
+		}
+
+		public void setMaxIdleTime(Duration maxIdleTime) {
+			this.maxIdleTime = maxIdleTime;
+		}
+
+		public Duration getMaxLifeTime() {
+			return maxLifeTime;
+		}
+
+		public void setMaxLifeTime(Duration maxLifeTime) {
+			this.maxLifeTime = maxLifeTime;
+		}
+
+		public Duration getEvictionInterval() {
+			return evictionInterval;
+		}
+
+		public void setEvictionInterval(Duration evictionInterval) {
+			this.evictionInterval = evictionInterval;
+		}
+
+		@Override
+		public String toString() {
+			return "Pool{" + "type=" + type + ", name='" + name + '\'' + ", maxConnections=" + maxConnections
+					+ ", acquireTimeout=" + acquireTimeout + ", maxIdleTime=" + maxIdleTime + ", maxLifeTime="
+					+ maxLifeTime + ", evictionInterval=" + evictionInterval + '}';
+		}
+
+		public enum PoolType {
+
+			/**
+			 * Elastic pool type.
+			 */
+			ELASTIC,
+
+			/**
+			 * Fixed pool type.
+			 */
+			FIXED,
+
+			/**
+			 * Disabled pool type.
+			 */
+			DISABLED
+
+		}
+
+	}
+
+	public static class Proxy {
+
+		/** proxyType for proxy configuration of Netty HttpClient. */
+		private ProxyProvider.Proxy type = ProxyProvider.Proxy.HTTP;
+
+		/** Hostname for proxy configuration of Netty HttpClient. */
+		private String host;
+
+		/** Port for proxy configuration of Netty HttpClient. */
+		private Integer port;
+
+		/** Username for proxy configuration of Netty HttpClient. */
+		private String username;
+
+		/** Password for proxy configuration of Netty HttpClient. */
+		private String password;
+
+		/**
+		 * Regular expression (Java) for a configured list of hosts. that should be
+		 * reached directly, bypassing the proxy
+		 */
+		private String nonProxyHostsPattern;
+
+		public ProxyProvider.Proxy getType() {
+			return type;
+		}
+
+		public void setType(ProxyProvider.Proxy type) {
+			this.type = type;
+		}
+
+		public String getHost() {
+			return host;
+		}
+
+		public void setHost(String host) {
+			this.host = host;
+		}
+
+		public Integer getPort() {
+			return port;
+		}
+
+		public void setPort(Integer port) {
+			this.port = port;
+		}
+
+		public String getUsername() {
+			return username;
+		}
+
+		public void setUsername(String username) {
+			this.username = username;
+		}
+
+		public String getPassword() {
+			return password;
+		}
+
+		public void setPassword(String password) {
+			this.password = password;
+		}
+
+		public String getNonProxyHostsPattern() {
+			return nonProxyHostsPattern;
+		}
+
+		public void setNonProxyHostsPattern(String nonProxyHostsPattern) {
+			this.nonProxyHostsPattern = nonProxyHostsPattern;
+		}
+
+		@Override
+		public String toString() {
+			return "Proxy{" + "type='" + type + '\'' + "host='" + host + '\'' + ", port=" + port + ", username='"
+					+ username + '\'' + ", password='" + password + '\'' + ", nonProxyHostsPattern='"
+					+ nonProxyHostsPattern + '\'' + '}';
+		}
+
+	}
+
+	public static class Ssl {
+
+		/**
+		 * Installs the netty InsecureTrustManagerFactory. This is insecure and not
+		 * suitable for production.
+		 */
+		private boolean useInsecureTrustManager = false;
+
+		/** Trusted certificates for verifying the remote endpoint's certificate. */
+		private List<String> trustedX509Certificates = new ArrayList<>();
+
+		// use netty default SSL timeouts
+		/** SSL handshake timeout. Default to 10000 ms */
+		private Duration handshakeTimeout = Duration.ofMillis(10000);
+
+		/** SSL close_notify flush timeout. Default to 3000 ms. */
+		private Duration closeNotifyFlushTimeout = Duration.ofMillis(3000);
+
+		/** SSL close_notify read timeout. Default to 0 ms. */
+		private Duration closeNotifyReadTimeout = Duration.ZERO;
+
+		/** The default ssl configuration type. Defaults to TCP. */
+		private SslProvider.DefaultConfigurationType defaultConfigurationType = SslProvider.DefaultConfigurationType.TCP;
+
+		/** Keystore path for Netty HttpClient. */
+		private String keyStore;
+
+		/** Keystore type for Netty HttpClient, default is JKS. */
+		private String keyStoreType = "JKS";
+
+		/** Keystore provider for Netty HttpClient, optional field. */
+		private String keyStoreProvider;
+
+		/** Keystore password. */
+		private String keyStorePassword;
+
+		/** Key password, default is same as keyStorePassword. */
+		private String keyPassword;
+
+		public String getKeyStorePassword() {
+			return keyStorePassword;
+		}
+
+		public void setKeyStorePassword(String keyStorePassword) {
+			this.keyStorePassword = keyStorePassword;
+		}
+
+		public String getKeyStoreType() {
+			return keyStoreType;
+		}
+
+		public void setKeyStoreType(String keyStoreType) {
+			this.keyStoreType = keyStoreType;
+		}
+
+		public String getKeyStoreProvider() {
+			return keyStoreProvider;
+		}
+
+		public void setKeyStoreProvider(String keyStoreProvider) {
+			this.keyStoreProvider = keyStoreProvider;
+		}
+
+		public String getKeyStore() {
+			return keyStore;
+		}
+
+		public void setKeyStore(String keyStore) {
+			this.keyStore = keyStore;
+		}
+
+		public String getKeyPassword() {
+			return keyPassword;
+		}
+
+		public void setKeyPassword(String keyPassword) {
+			this.keyPassword = keyPassword;
+		}
+
+		public List<String> getTrustedX509Certificates() {
+			return trustedX509Certificates;
+		}
+
+		public void setTrustedX509Certificates(List<String> trustedX509) {
+			this.trustedX509Certificates = trustedX509;
+		}
+
+		public X509Certificate[] getTrustedX509CertificatesForTrustManager() {
+			try {
+				CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+				ArrayList<Certificate> allCerts = new ArrayList<>();
+				for (String trustedCert : getTrustedX509Certificates()) {
+					try {
+						URL url = ResourceUtils.getURL(trustedCert);
+						Collection<? extends Certificate> certs = certificateFactory
+								.generateCertificates(url.openStream());
+						allCerts.addAll(certs);
+					}
+					catch (IOException e) {
+						throw new WebServerException("Could not load certificate '" + trustedCert + "'", e);
+					}
+				}
+				return allCerts.toArray(new X509Certificate[allCerts.size()]);
+			}
+			catch (CertificateException e1) {
+				throw new WebServerException("Could not load CertificateFactory X.509", e1);
+			}
+		}
+
+		public KeyManagerFactory getKeyManagerFactory() {
+			try {
+				if (getKeyStore() != null && getKeyStore().length() > 0) {
+					KeyManagerFactory keyManagerFactory = KeyManagerFactory
+							.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+					char[] keyPassword = getKeyPassword() != null ? getKeyPassword().toCharArray() : null;
+
+					if (keyPassword == null && getKeyStorePassword() != null) {
+						keyPassword = getKeyStorePassword().toCharArray();
+					}
+
+					keyManagerFactory.init(this.createKeyStore(), keyPassword);
+
+					return keyManagerFactory;
+				}
+
+				return null;
+			}
+			catch (Exception e) {
+				throw new IllegalStateException(e);
+			}
+		}
+
+		public KeyStore createKeyStore() {
+			try {
+				KeyStore store = getKeyStoreProvider() != null
+						? KeyStore.getInstance(getKeyStoreType(), getKeyStoreProvider())
+						: KeyStore.getInstance(getKeyStoreType());
+				try {
+					URL url = ResourceUtils.getURL(getKeyStore());
+					store.load(url.openStream(),
+							getKeyStorePassword() != null ? getKeyStorePassword().toCharArray() : null);
+				}
+				catch (Exception e) {
+					throw new WebServerException("Could not load key store ' " + getKeyStore() + "'", e);
+				}
+
+				return store;
+			}
+			catch (KeyStoreException | NoSuchProviderException e) {
+				throw new WebServerException("Could not load KeyStore for given type and provider", e);
+			}
+		}
+
+		// TODO: support configuration of other trust manager factories
+
+		public boolean isUseInsecureTrustManager() {
+			return useInsecureTrustManager;
+		}
+
+		public void setUseInsecureTrustManager(boolean useInsecureTrustManager) {
+			this.useInsecureTrustManager = useInsecureTrustManager;
+		}
+
+		public Duration getHandshakeTimeout() {
+			return handshakeTimeout;
+		}
+
+		public void setHandshakeTimeout(Duration handshakeTimeout) {
+			this.handshakeTimeout = handshakeTimeout;
+		}
+
+		public Duration getCloseNotifyFlushTimeout() {
+			return closeNotifyFlushTimeout;
+		}
+
+		public void setCloseNotifyFlushTimeout(Duration closeNotifyFlushTimeout) {
+			this.closeNotifyFlushTimeout = closeNotifyFlushTimeout;
+		}
+
+		public Duration getCloseNotifyReadTimeout() {
+			return closeNotifyReadTimeout;
+		}
+
+		public void setCloseNotifyReadTimeout(Duration closeNotifyReadTimeout) {
+			this.closeNotifyReadTimeout = closeNotifyReadTimeout;
+		}
+
+		public SslProvider.DefaultConfigurationType getDefaultConfigurationType() {
+			return defaultConfigurationType;
+		}
+
+		public void setDefaultConfigurationType(SslProvider.DefaultConfigurationType defaultConfigurationType) {
+			this.defaultConfigurationType = defaultConfigurationType;
+		}
+
+		@Override
+		public String toString() {
+			return new ToStringCreator(this).append("useInsecureTrustManager", useInsecureTrustManager)
+					.append("trustedX509Certificates", trustedX509Certificates)
+					.append("handshakeTimeout", handshakeTimeout)
+					.append("closeNotifyFlushTimeout", closeNotifyFlushTimeout)
+					.append("closeNotifyReadTimeout", closeNotifyReadTimeout)
+					.append("defaultConfigurationType", defaultConfigurationType).toString();
+		}
+
+	}
+
+	public static class Websocket {
+
+		/** Max frame payload length. */
+		private Integer maxFramePayloadLength;
+
+		/** Proxy ping frames to downstream services, defaults to true. */
+		private boolean proxyPing = true;
+
+		public Integer getMaxFramePayloadLength() {
+			return this.maxFramePayloadLength;
+		}
+
+		public void setMaxFramePayloadLength(Integer maxFramePayloadLength) {
+			this.maxFramePayloadLength = maxFramePayloadLength;
+		}
+
+		public boolean isProxyPing() {
+			return proxyPing;
+		}
+
+		public void setProxyPing(boolean proxyPing) {
+			this.proxyPing = proxyPing;
+		}
+
+		@Override
+		public String toString() {
+			return new ToStringCreator(this).append("maxFramePayloadLength", maxFramePayloadLength)
+					.append("proxyPing", proxyPing).toString();
+		}
+
+	}
+// CHECKSTYLE:ON
+}
diff --git a/sra/src/main/resources/application.properties b/sra/src/main/resources/application.properties
deleted file mode 100644
index b0b438d..0000000
--- a/sra/src/main/resources/application.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} SRA
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.port=8080
-
-management.endpoint.gateway.enabled=true
-management.endpoints.web.exposure.include=info,health,loggers,metrics,gateway,sraSessions
-management.endpoint.health.show-details=ALWAYS
-spring.cloud.discovery.client.health-indicator.enabled=false
-
-service.discovery.address=http://localhost:8080/
diff --git a/sra/src/main/resources/sra.properties b/sra/src/main/resources/sra.properties
index 0e6263a..b61eeb4 100644
--- a/sra/src/main/resources/sra.properties
+++ b/sra/src/main/resources/sra.properties
@@ -14,10 +14,25 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
+spring.application.name=Apache Syncope ${syncope.version} SRA
+spring.groovy.template.check-template-location=false
+spring.main.banner-mode=log
 
-useGZIPCompression=true
+server.port=8080
 
-global.error=/error
-global.postLogout=/logout
+management.endpoint.gateway.enabled=true
+management.endpoints.web.exposure.include=info,health,loggers,metrics,gateway,sraSessions
+management.endpoint.health.show-details=ALWAYS
+spring.cloud.discovery.client.health-indicator.enabled=false
+
+service.discovery.address=http://localhost:8080/
+
+logging.config=classpath:log4j2.xml
+
+sra.anonymousUser=${anonymousUser}
+sra.anonymousKey=${anonymousKey}
+
+sra.useGZIPCompression=true
+
+sra.global.error=/error
+sra.global.postLogout=/logout
diff --git a/sra/src/test/java/org/apache/syncope/sra/AbstractTest.java b/sra/src/test/java/org/apache/syncope/sra/AbstractTest.java
index b7526e0..f72e9b5 100644
--- a/sra/src/test/java/org/apache/syncope/sra/AbstractTest.java
+++ b/sra/src/test/java/org/apache/syncope/sra/AbstractTest.java
@@ -29,8 +29,10 @@
 import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
 
 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@TestPropertySource(locations = { "classpath:sra.properties", "classpath:test.properties" })
 @ContextConfiguration(initializers = ZookeeperTestingServer.class)
 @AutoConfigureWireMock(port = 0)
 public abstract class AbstractTest {
@@ -57,13 +59,11 @@
     @Value("${wiremock.server.port}")
     protected int wiremockPort;
 
-    @Value("${anonymousUser}")
-    private String anonymousUser;
-
-    @Value("${anonymousKey}")
-    private String anonymousKey;
+    @Autowired
+    private SRAProperties props;
 
     protected String basicAuthHeader() {
-        return "Basic " + Base64.getEncoder().encodeToString((anonymousUser + ":" + anonymousKey).getBytes());
+        return "Basic " + Base64.getEncoder().encodeToString(
+                (props.getAnonymousUser() + ":" + props.getAnonymousKey()).getBytes());
     }
 }
diff --git a/sra/src/test/java/org/apache/syncope/sra/SyncopeCoreTestingServer.java b/sra/src/test/java/org/apache/syncope/sra/SyncopeCoreTestingServer.java
index d30aefb..ea113b9 100644
--- a/sra/src/test/java/org/apache/syncope/sra/SyncopeCoreTestingServer.java
+++ b/sra/src/test/java/org/apache/syncope/sra/SyncopeCoreTestingServer.java
@@ -41,7 +41,9 @@
 @Component
 public class SyncopeCoreTestingServer implements ApplicationListener<ContextRefreshedEvent> {
 
-    public static final String ADDRESS = "http://localhost:9080/syncope/rest";
+    private static final int PORT = 9999;
+
+    public static final String ADDRESS = "http://localhost:" + PORT + "/syncope/rest";
 
     public static final Map<String, SRARouteTO> ROUTES = new ConcurrentHashMap<>();
 
@@ -53,7 +55,7 @@
 
     @Override
     public void onApplicationEvent(final ContextRefreshedEvent event) {
-        if (AbstractTest.available(9080)) {
+        if (AbstractTest.available(PORT)) {
             // 1. start (mocked) Core as embedded CXF
             JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
             sf.setAddress(ADDRESS);
diff --git a/sra/src/test/java/org/apache/syncope/sra/TLSRouteProviderTest.java b/sra/src/test/java/org/apache/syncope/sra/TLSRouteProviderTest.java
index 3c82278..ace2ed1 100644
--- a/sra/src/test/java/org/apache/syncope/sra/TLSRouteProviderTest.java
+++ b/sra/src/test/java/org/apache/syncope/sra/TLSRouteProviderTest.java
@@ -50,10 +50,12 @@
 import org.springframework.http.client.reactive.ClientHttpConnector;
 import org.springframework.http.client.reactive.ReactorClientHttpConnector;
 import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.web.reactive.server.WebTestClient;
 import reactor.netty.http.client.HttpClient;
 
 @ActiveProfiles({ "tls" })
+@TestPropertySource("classpath:sra-tls.properties")
 public class TLSRouteProviderTest extends AbstractTest {
 
     private WebTestClient webClient() throws SSLException {
diff --git a/sra/src/test/java/org/apache/syncope/sra/ZookeeperTestingServer.java b/sra/src/test/java/org/apache/syncope/sra/ZookeeperTestingServer.java
index b9c8762..fe4c299 100644
--- a/sra/src/test/java/org/apache/syncope/sra/ZookeeperTestingServer.java
+++ b/sra/src/test/java/org/apache/syncope/sra/ZookeeperTestingServer.java
@@ -42,7 +42,7 @@
         AtomicReference<Integer> port = new AtomicReference<>();
         AtomicReference<String> username = new AtomicReference<>();
         AtomicReference<String> password = new AtomicReference<>();
-        try (InputStream propStream = getClass().getResourceAsStream("/keymaster.properties")) {
+        try (InputStream propStream = getClass().getResourceAsStream("/test.properties")) {
             Properties props = new Properties();
             props.load(propStream);
 
@@ -50,7 +50,7 @@
             username.set(props.getProperty("keymaster.username"));
             password.set(props.getProperty("keymaster.password"));
         } catch (Exception e) {
-            throw new IllegalStateException("Could not load /keymaster.properties", e);
+            throw new IllegalStateException("Could not load /test.properties", e);
         }
 
         if (AbstractTest.available(port.get())) {
diff --git a/sra/src/test/resources/debug/application-debug.properties b/sra/src/test/resources/debug/application-debug.properties
deleted file mode 100644
index 7f55501..0000000
--- a/sra/src/test/resources/debug/application-debug.properties
+++ /dev/null
@@ -1,50 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-am.type=OIDC
-am.oidc.configuration=http://localhost:9080/syncope-wa/oidc
-am.oidc.client.id=oidcTestClientId
-am.oidc.client.secret=oidcTestClientSecret
-
-#am.type=OAUTH2
-#am.oauth2.tokenUri=http://localhost:9080/syncope-wa/oauth2.0/accessToken
-#am.oauth2.authorizationUri=http://localhost:9080/syncope-wa/oauth2.0/authorize
-#am.oauth2.userInfoUri=http://localhost:9080/syncope-wa/oauth2.0/profile
-#am.oauth2.userNameAttributeName=id
-#am.oauth2.scopes=
-#am.oauth2.jwkSetUri=
-#am.oauth2.issuer=http://localhost:9080/syncope-wa
-#am.oauth2.client.id=oauth2TestClientId
-#am.oauth2.client.secret=oauth2TestClientSecret
-
-#am.type=SAML2
-#am.saml2.sp.authnrequest.binding=POST
-#am.saml2.sp.logout.request.binding=POST
-#am.saml2.sp.logout.response.binding=REDIRECT
-#am.saml2.sp.entityId=http://localhost:8080
-#am.saml2.sp.skew=300
-#am.saml2.sp.metadata=/tmp/saml2-sp-metadata.xml
-#am.saml2.idp=http://localhost:9080/syncope-wa/idp/metadata
-#am.saml2.keystore=classpath:/saml.keystore.jks
-#am.saml2.keystore.type=jks
-#am.saml2.keystore.storepass=changeit
-#am.saml2.keystore.keypass=changeit
-
-#am.type=CAS
-#am.cas.server.name=http://localhost:9080
-#am.cas.url.prefix=http://localhost:9080/syncope-wa/
-
-global.postLogout=http://localhost:8080/logout
diff --git a/sra/src/test/resources/debug/keymaster.properties b/sra/src/test/resources/debug/keymaster.properties
deleted file mode 100644
index 033fe3b..0000000
--- a/sra/src/test/resources/debug/keymaster.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-keymaster.address=http://localhost:9080/syncope/rest/keymaster
-keymaster.username=${anonymousUser}
-keymaster.password=${anonymousKey}
diff --git a/sra/src/test/resources/debug/log4j2.xml b/sra/src/test/resources/debug/log4j2.xml
index f7ac25d..6fdccd1 100644
--- a/sra/src/test/resources/debug/log4j2.xml
+++ b/sra/src/test/resources/debug/log4j2.xml
@@ -19,48 +19,39 @@
 -->
 <configuration status="WARN">
 
-  <Properties>
-    <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
-    <Property name="LOG_LEVEL_PATTERN">%5p</Property>
-    <Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
-    <Property name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{%pid}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
-    <Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} %pid --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
-  </Properties>
-  <Appenders>
-    <Console name="main" target="SYSTEM_OUT" follow="true">
-      <PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
-    </Console>
-    <Console name="access" target="SYSTEM_OUT" follow="true">
-      <PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
-    </Console>
-  </Appenders>
-    
-  <loggers>
+  <appenders>
 
+    <Console name="console" target="SYSTEM_OUT" follow="true">
+      <PatternLayout pattern="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} %highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} [%11.11t] %style{%-60.60c{60}}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
+    </Console>
+    
+  </appenders>
+  
+  <loggers>
+    
     <asyncLogger name="org.apache.syncope.client.lib" additivity="false" level="OFF">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
     <asyncLogger name="org.apache.syncope.sra" additivity="false" level="INFO">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
 
     <asyncLogger name="org.apache.cxf" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
 
     <asyncLogger name="org.springframework.cloud.gateway" additivity="false" level="INFO">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
 
     <!-- Requires -Dreactor.netty.http.server.accessLogEnabled=true to work-->
     <asyncLogger name="reactor.netty.http.server.AccessLog" additivity="false" level="INFO">
-      <appender-ref ref="access"/>
+      <appender-ref ref="console"/>
     </asyncLogger>
 
     <root level="INFO">
-      <appender-ref ref="main"/>
+      <appender-ref ref="console"/>
     </root>
-  
+    
   </loggers>
-  
 </configuration>
diff --git a/sra/src/test/resources/debug/sra-debug.properties b/sra/src/test/resources/debug/sra-debug.properties
new file mode 100644
index 0000000..2cb868e
--- /dev/null
+++ b/sra/src/test/resources/debug/sra-debug.properties
@@ -0,0 +1,54 @@
+# 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.
+keymaster.address=http://localhost:9080/syncope/rest/keymaster
+keymaster.username=${anonymousUser}
+keymaster.password=${anonymousKey}
+
+sra.am-type=OIDC
+sra.oidc.configuration=http://localhost:9080/syncope-wa/oidc
+sra.oidc.client-id=oidcTestClientId
+sra.oidc.client-secret=oidcTestClientSecret
+
+#sra.am-type=OAUTH2
+#sra.oauth2.tokenUri=http://localhost:9080/syncope-wa/oauth2.0/accessToken
+#sra.oauth2.authorizationUri=http://localhost:9080/syncope-wa/oauth2.0/authorize
+#sra.oauth2.userInfoUri=http://localhost:9080/syncope-wa/oauth2.0/profile
+#sra.oauth2.userNameAttributeName=id
+#sra.oauth2.scopes=
+#sra.oauth2.jwkSetUri=
+#sra.oauth2.issuer=http://localhost:9080/syncope-wa
+#sra.oauth2.client-id=oauth2TestClientId
+#sra.oauth2.client-secret=oauth2TestClientSecret
+
+#sra.am-type=SAML2
+#sra.saml2.authn-request-binding=POST
+#sra.saml2.logout-request-binding=POST
+#sra.saml2.logout-response-binding=REDIRECT
+#sra.saml2.entityId=http://localhost:8080
+#sra.saml2.skew=300
+#sra.saml2.sp-metadata-file-path=/tmp/saml2-sp-metadata.xml
+#sra.saml2.idp-metadata=http://localhost:9080/syncope-wa/idp/metadata
+#sra.saml2.keystore=classpath:/saml.keystore.jks
+#sra.saml2.keystore-type=jks
+#sra.saml2.keystore-storepass=changeit
+#sra.saml2.keystore-keypass=changeit
+
+#sra.am-type=CAS
+#sra.cas.server-name=http://localhost:9080
+#sra.cas.server-prefix=http://localhost:9080/syncope-wa/
+
+sra.global.postLogout=http://localhost:8080/logout
diff --git a/sra/src/test/resources/application-tls.properties b/sra/src/test/resources/sra-tls.properties
similarity index 100%
rename from sra/src/test/resources/application-tls.properties
rename to sra/src/test/resources/sra-tls.properties
diff --git a/sra/src/test/resources/keymaster.properties b/sra/src/test/resources/test.properties
similarity index 100%
rename from sra/src/test/resources/keymaster.properties
rename to sra/src/test/resources/test.properties
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java
index ce944a7..1d1dc2dd 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWABootstrapConfiguration.java
@@ -30,13 +30,13 @@
 @PropertySource(value = "file:${conf.directory}/wa.properties", ignoreResourceNotFound = true)
 public class SyncopeWABootstrapConfiguration {
 
-    @Value("${anonymousUser}")
+    @Value("${wa.anonymousUser}")
     private String anonymousUser;
 
-    @Value("${anonymousKey}")
+    @Value("${wa.anonymousKey}")
     private String anonymousKey;
 
-    @Value("${useGZIPCompression}")
+    @Value("${wa.useGZIPCompression:true}")
     private boolean useGZIPCompression;
 
     @Bean
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
index 8d655e6..11e61f7 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/SyncopeWAPropertySourceLocator.java
@@ -72,7 +72,7 @@
 @Order
 public class SyncopeWAPropertySourceLocator implements PropertySourceLocator {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWABootstrapConfiguration.class);
+    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAPropertySourceLocator.class);
 
     private final WARestClient waRestClient;
 
@@ -207,7 +207,7 @@
             final GoogleMfaAuthModuleConf conf) {
 
         GoogleAuthenticatorMultifactorProperties props =
-            new GoogleAuthenticatorMultifactorProperties();
+                new GoogleAuthenticatorMultifactorProperties();
         props.setName(authModule);
         props.getCore().setIssuer(conf.getIssuer());
         props.getCore().setCodeDigits(conf.getCodeDigits());
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAProperties.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAProperties.java
new file mode 100644
index 0000000..f8cf497
--- /dev/null
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAProperties.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.wa.bootstrap;
+
+import org.apache.syncope.common.lib.SyncopeProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties("wa")
+public class WAProperties extends SyncopeProperties {
+
+    private long contextRefreshDelay = 15;
+
+    public long getContextRefreshDelay() {
+        return contextRefreshDelay;
+    }
+
+    public void setContextRefreshDelay(final long contextRefreshDelay) {
+        this.contextRefreshDelay = contextRefreshDelay;
+    }
+}
diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml
index 3478777..9b687af 100644
--- a/wa/starter/pom.xml
+++ b/wa/starter/pom.xml
@@ -163,8 +163,8 @@
       <artifactId>cas-server-support-themes</artifactId>
     </dependency>
     <dependency>
-        <groupId>org.apereo.cas</groupId>
-        <artifactId>cas-server-support-radius-core</artifactId>
+      <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-support-radius-core</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apereo.cas</groupId>
@@ -420,8 +420,8 @@
         </dependency>
 
         <dependency>
-          <groupId>org.apache.syncope.ext.self-keymaster</groupId>
-          <artifactId>syncope-ext-self-keymaster-client</artifactId>
+          <groupId>org.apache.syncope.common.keymaster.self</groupId>
+          <artifactId>syncope-common-keymaster-client-self</artifactId>
           <version>${project.version}</version>
         </dependency>
       </dependencies>
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
index a181c47..7c4b0fd 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAApplication.java
@@ -18,9 +18,11 @@
  */
 package org.apache.syncope.wa.starter;
 
+import org.apache.syncope.wa.bootstrap.WAProperties;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.Date;
+import java.util.Map;
 import org.apache.syncope.wa.starter.config.SyncopeWARefreshContextJob;
 import org.apereo.cas.configuration.CasConfigurationProperties;
 import org.apereo.cas.configuration.CasConfigurationPropertiesValidator;
@@ -33,7 +35,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
 import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
@@ -52,15 +53,12 @@
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
-import org.springframework.context.annotation.PropertySource;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.quartz.SchedulerFactoryBean;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
 
-@PropertySource("classpath:wa.properties")
-@PropertySource(value = "file:${conf.directory}/wa.properties", ignoreResourceNotFound = true)
 @SpringBootApplication(exclude = {
     HibernateJpaAutoConfiguration.class,
     JerseyAutoConfiguration.class,
@@ -75,27 +73,30 @@
     DataSourceTransactionManagerAutoConfiguration.class,
     RedisRepositoriesAutoConfiguration.class
 })
-@EnableConfigurationProperties({ CasConfigurationProperties.class })
+@EnableConfigurationProperties({ WAProperties.class, CasConfigurationProperties.class })
 @EnableAsync
 @EnableAspectJAutoProxy(proxyTargetClass = true)
 @EnableTransactionManagement(proxyTargetClass = true)
 @EnableScheduling
 public class SyncopeWAApplication extends SpringBootServletInitializer {
 
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAApplication.class);
-
-    @Autowired
-    private SchedulerFactoryBean scheduler;
-
-    @Value("${contextRefreshDelay:15}")
-    private long contextRefreshDelay;
+    protected static final Logger LOG = LoggerFactory.getLogger(SyncopeWAApplication.class);
 
     public static void main(final String[] args) {
-        new SpringApplicationBuilder(SyncopeWAApplication.class).run(args);
+        new SpringApplicationBuilder(SyncopeWAApplication.class).
+                properties("spring.config.name:wa").
+                build().run(args);
     }
 
-    private static void validateConfiguration(final ApplicationReadyEvent event) {
-        new CasConfigurationPropertiesValidator(event.getApplicationContext()).validate();
+    @Autowired
+    protected WAProperties waProperties;
+
+    @Autowired
+    protected SchedulerFactoryBean scheduler;
+
+    @Override
+    protected SpringApplicationBuilder configure(final SpringApplicationBuilder builder) {
+        return builder.properties(Map.of("spring.config.name", "wa")).sources(SyncopeWAApplication.class);
     }
 
     /**
@@ -105,13 +106,13 @@
      */
     @EventListener
     public void handleApplicationReadyEvent(final ApplicationReadyEvent event) {
-        validateConfiguration(event);
+        new CasConfigurationPropertiesValidator(event.getApplicationContext()).validate();
         scheduleJobToRefreshContext();
     }
 
-    private void scheduleJobToRefreshContext() {
+    protected void scheduleJobToRefreshContext() {
         try {
-            Date date = Date.from(LocalDateTime.now().plusSeconds(this.contextRefreshDelay).
+            Date date = Date.from(LocalDateTime.now().plusSeconds(waProperties.getContextRefreshDelay()).
                     atZone(ZoneId.systemDefault()).toInstant());
             Trigger trigger = TriggerBuilder.newTrigger().startAt(date).build();
             JobKey jobKey = new JobKey(getClass().getSimpleName());
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeWAInfoContributor.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeWAInfoContributor.java
new file mode 100644
index 0000000..ca78683
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeWAInfoContributor.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.wa.starter.actuate;
+
+import org.apache.syncope.wa.bootstrap.WAProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.info.Info;
+import org.springframework.boot.actuate.info.InfoContributor;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+public class SyncopeWAInfoContributor implements InfoContributor {
+
+    @Autowired
+    protected WAProperties waProperties;
+
+    @PreAuthorize("isAuthenticated()")
+    @Override
+    public void contribute(final Info.Builder builder) {
+        builder.withDetail("waProperties", waProperties);
+    }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
index bcb0764..434c2de 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
@@ -37,6 +37,7 @@
 import org.apache.syncope.common.lib.types.JWSAlgorithm;
 import org.apache.syncope.wa.bootstrap.WARestClient;
 import org.apache.syncope.wa.starter.actuate.SyncopeCoreHealthIndicator;
+import org.apache.syncope.wa.starter.actuate.SyncopeWAInfoContributor;
 import org.apache.syncope.wa.starter.audit.SyncopeWAAuditTrailManager;
 import org.apache.syncope.wa.starter.events.SyncopeWAEventRepository;
 import org.apache.syncope.wa.starter.gauth.SyncopeWAGoogleMfaAuthCredentialRepository;
@@ -315,6 +316,12 @@
         return new SyncopeCoreHealthIndicator(restClient);
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public SyncopeWAInfoContributor syncopeWAInfoContributor() {
+        return new SyncopeWAInfoContributor();
+    }
+
     @Bean
     public KeymasterStart keymasterStart() {
         return new KeymasterStart(NetworkService.Type.WA);
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthTokenRepository.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthTokenRepository.java
index 35c612c..55da063 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthTokenRepository.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/SyncopeWAGoogleMfaAuthTokenRepository.java
@@ -38,10 +38,7 @@
 
     private final long expireTokensInSeconds;
 
-    public SyncopeWAGoogleMfaAuthTokenRepository(
-            final WARestClient waRestClient,
-            final long expireTokensInSeconds) {
-
+    public SyncopeWAGoogleMfaAuthTokenRepository(final WARestClient waRestClient, final long expireTokensInSeconds) {
         this.waRestClient = waRestClient;
         this.expireTokensInSeconds = expireTokensInSeconds;
     }
diff --git a/wa/starter/src/main/resources/application.properties b/wa/starter/src/main/resources/application.properties
deleted file mode 100644
index 4e1b1c2..0000000
--- a/wa/starter/src/main/resources/application.properties
+++ /dev/null
@@ -1,58 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-spring.application.name=Apache Syncope ${syncope.version} WA
-spring.groovy.template.check-template-location=false
-spring.main.banner-mode=log
-
-server.port=8080
-
-server.servlet.encoding.charset=UTF-8
-server.servlet.encoding.enabled=true
-server.servlet.encoding.force=true
-
-server.servlet.contextPath=/syncope-wa
-
-server.servlet.session.timeout=300
-server.servlet.session.cookie.http-only=true
-server.servlet.session.tracking-modes=COOKIE
-
-spring.web.resources.static-locations=classpath:/thymeleaf/static,classpath:/syncope/static,classpath:/static
-
-cas.monitor.endpoints.endpoint.defaults.access=AUTHENTICATED
-management.endpoints.enabled-by-default=true
-management.endpoints.web.exposure.include=info,health,loggers,ssoSessions,registeredServices
-management.endpoint.health.show-details=ALWAYS
-spring.cloud.discovery.client.health-indicator.enabled=false
-
-# Cache service definitions for 5 minutes
-cas.service-registry.cache.duration=PT5M
-
-# Reload services and hydrate the cache every 5 minutes
-cas.service-registry.schedule.repeat-interval=PT5M
-cas.service-registry.schedule.start-delay=PT30S
-
-cas.events.core.enabled=false
-
-##
-# Allow configuration classes to override bean definitions from Spring Boot
-#
-spring.main.allow-bean-definition-overriding=true
-spring.main.lazy-initialization=false
-
-service.discovery.address=http://localhost:8080/syncope-wa/
-
-version=${syncope.version}
diff --git a/wa/starter/src/main/resources/log4j2.xml b/wa/starter/src/main/resources/log4j2.xml
deleted file mode 100644
index e97e441..0000000
--- a/wa/starter/src/main/resources/log4j2.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<configuration status="WARN">
-
-  <appenders>
-
-    <RollingRandomAccessFile name="main" fileName="${log.directory}/wa.log"
-                             filePattern="${log.directory}/wa-%d{yyyy-MM-dd}.log.gz"
-                             immediateFlush="false" append="true">
-      <PatternLayout>
-        <pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
-      </PatternLayout>
-      <Policies>
-        <TimeBasedTriggeringPolicy/>
-        <SizeBasedTriggeringPolicy size="250 MB"/>
-      </Policies>
-    </RollingRandomAccessFile>
-
-  </appenders>
-
-  <loggers>
-
-    <asyncLogger name="org.apereo.cas" additivity="false" level="INFO">
-      <appender-ref ref="main"/>
-    </asyncLogger>
-
-    <asyncLogger name="org.apereo.services.persondir" additivity="false" level="INFO">
-      <appender-ref ref="main"/>
-    </asyncLogger>
-
-    <asyncLogger name="org.apereo.inspektr.audit.support" additivity="false" level="INFO">
-      <appender-ref ref="main"/>
-    </asyncLogger>
-
-    <asyncLogger name="org.pac4j" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
-    </asyncLogger>
-
-    <asyncLogger name="org.springframework" additivity="false" level="INFO">
-      <appender-ref ref="main"/>
-    </asyncLogger>
-
-    <asyncLogger name="org.apache.syncope.client.lib" additivity="false" level="OFF">
-      <appender-ref ref="main"/>
-    </asyncLogger>
-    <asyncLogger name="org.apache.syncope.wa" additivity="false" level="INFO">
-      <appender-ref ref="main"/>
-    </asyncLogger>
-
-    <asyncLogger name="org.apache.cxf" additivity="false" level="ERROR">
-      <appender-ref ref="main"/>
-    </asyncLogger>
-
-    <root level="INFO">
-      <appender-ref ref="main"/>
-    </root>
-  
-  </loggers>
-  
-</configuration>
diff --git a/wa/starter/src/main/resources/wa.properties b/wa/starter/src/main/resources/wa.properties
index 226946c..51840bd 100644
--- a/wa/starter/src/main/resources/wa.properties
+++ b/wa/starter/src/main/resources/wa.properties
@@ -14,10 +14,53 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
+spring.application.name=Apache Syncope ${syncope.version} WA
+spring.groovy.template.check-template-location=false
+spring.main.banner-mode=log
 
-useGZIPCompression=true
+version=${syncope.version}
+
+server.port=8080
+
+server.servlet.encoding.charset=UTF-8
+server.servlet.encoding.enabled=true
+server.servlet.encoding.force=true
+
+server.servlet.contextPath=/syncope-wa
+
+server.servlet.session.timeout=300
+server.servlet.session.cookie.http-only=true
+server.servlet.session.tracking-modes=COOKIE
+
+spring.web.resources.static-locations=classpath:/thymeleaf/static,classpath:/syncope/static,classpath:/static
+
+cas.monitor.endpoints.endpoint.defaults.access=AUTHENTICATED
+management.endpoints.enabled-by-default=true
+management.endpoints.web.exposure.include=info,health,loggers,ssoSessions,registeredServices
+management.endpoint.health.show-details=ALWAYS
+spring.cloud.discovery.client.health-indicator.enabled=false
+
+# Cache service definitions for 5 minutes
+cas.service-registry.cache.duration=PT5M
+
+# Reload services and hydrate the cache every 5 minutes
+cas.service-registry.schedule.repeat-interval=PT5M
+cas.service-registry.schedule.start-delay=PT30S
+
+cas.events.core.enabled=false
+
+##
+# Allow configuration classes to override bean definitions from Spring Boot
+#
+spring.main.allow-bean-definition-overriding=true
+spring.main.lazy-initialization=false
+
+service.discovery.address=http://localhost:8080/syncope-wa/
+
+wa.anonymousUser=${anonymousUser}
+wa.anonymousKey=${anonymousKey}
+
+wa.useGZIPCompression=true
 
 # Conf directories
 conf.directory=${conf.directory}
@@ -30,10 +73,10 @@
 cas.tgc.secure=false
 cas.logout.follow-service-redirects=true
 
-cas.authn.saml-idp.core.entity-id=${cas.server.name}/syncope-wa/saml
+cas.authn.saml-idp.core.entity-id=${cas.server.prefix}/saml
 cas.authn.saml-idp.metadata.http.metadata-backup-location=file:${conf.directory}/saml
 
-cas.authn.oidc.core.issuer=${cas.server.name}/syncope-wa/oidc
+cas.authn.oidc.core.issuer=${cas.server.prefix}/oidc
 cas.authn.oidc.discovery.id-token-signing-alg-values-supported=RS256,RS384,RS512,PS256,PS384,PS512,ES256,ES384,ES512,HS256,HS384,HS512
 cas.authn.oidc.discovery.user-info-signing-alg-values-supported=RS256,RS384,RS512,PS256,PS384,PS512,ES256,ES384,ES512,HS256,HS384,HS512
 cas.authn.oauth.user-profile-view-type=FLAT
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/AbstractTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/AbstractTest.java
index f3c94cd..33ee8ff 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/AbstractTest.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/AbstractTest.java
@@ -25,6 +25,7 @@
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
 
 @SpringBootTest(
         classes = { SyncopeWAApplication.class, AbstractTest.SyncopeTestConfiguration.class },
@@ -34,6 +35,7 @@
             "cas.authn.syncope.url=http://localhost:8080",
             "cas.sso.allow-missing-service-parameter=true"
         })
+@TestPropertySource(locations = { "classpath:wa.properties", "classpath:test.properties" })
 @ContextConfiguration(initializers = ZookeeperTestingServer.class)
 public abstract class AbstractTest {
 
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/ZookeeperTestingServer.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/ZookeeperTestingServer.java
index c5b1ec9..273a6a5 100644
--- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/ZookeeperTestingServer.java
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/ZookeeperTestingServer.java
@@ -42,7 +42,7 @@
         AtomicReference<Integer> port = new AtomicReference<>();
         AtomicReference<String> username = new AtomicReference<>();
         AtomicReference<String> password = new AtomicReference<>();
-        try (InputStream propStream = getClass().getResourceAsStream("/keymaster.properties")) {
+        try (InputStream propStream = getClass().getResourceAsStream("/test.properties")) {
             Properties props = new Properties();
             props.load(propStream);
 
@@ -50,7 +50,7 @@
             username.set(props.getProperty("keymaster.username"));
             password.set(props.getProperty("keymaster.password"));
         } catch (Exception e) {
-            throw new IllegalStateException("Could not load /keymaster.properties", e);
+            throw new IllegalStateException("Could not load /test.properties", e);
         }
 
         Configuration.setConfiguration(new Configuration() {
diff --git a/wa/starter/src/test/resources/application-debug.properties b/wa/starter/src/test/resources/application-debug.properties
deleted file mode 100644
index 5f956d3..0000000
--- a/wa/starter/src/test/resources/application-debug.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-cas.authn.accept.users=admin::password
diff --git a/wa/starter/src/test/resources/debug/application-debug.properties b/wa/starter/src/test/resources/debug/application-debug.properties
deleted file mode 100644
index fffeac6..0000000
--- a/wa/starter/src/test/resources/debug/application-debug.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#debug=true
-
-cas.authn.syncope.url=http://localhost:9081/syncope/rest/
diff --git a/wa/starter/src/test/resources/debug/keymaster.properties b/wa/starter/src/test/resources/debug/wa-debug.properties
similarity index 83%
rename from wa/starter/src/test/resources/debug/keymaster.properties
rename to wa/starter/src/test/resources/debug/wa-debug.properties
index 033fe3b..fdcb208 100644
--- a/wa/starter/src/test/resources/debug/keymaster.properties
+++ b/wa/starter/src/test/resources/debug/wa-debug.properties
@@ -17,3 +17,8 @@
 keymaster.address=http://localhost:9080/syncope/rest/keymaster
 keymaster.username=${anonymousUser}
 keymaster.password=${anonymousKey}
+
+cas.server.name=http://localhost:8080
+cas.server.prefix=${cas.server.name}/syncope-wa
+cas.authn.accept.users=admin::password
+cas.authn.syncope.url=http://localhost:9080/syncope/rest/
diff --git a/wa/starter/src/test/resources/keymaster.properties b/wa/starter/src/test/resources/test.properties
similarity index 100%
rename from wa/starter/src/test/resources/keymaster.properties
rename to wa/starter/src/test/resources/test.properties