You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2019/11/20 15:27:00 UTC

[syncope] branch master updated: [SYNCOPE-1513] Adding options to console.properties and enduser.properties

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

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


View the commit online:
https://github.com/apache/syncope/commit/26e062ac6c276316f0ee1b7ec5e43440e517db9e

The following commit(s) were added to refs/heads/master by this push:
     new 26e062a  [SYNCOPE-1513] Adding options to console.properties and enduser.properties
26e062a is described below

commit 26e062ac6c276316f0ee1b7ec5e43440e517db9e
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Wed Nov 20 13:45:02 2019 +0100

    [SYNCOPE-1513] Adding options to console.properties and
    enduser.properties
---
 .../syncope/client/console/AbstractTest.java       | 141 +++++++++++++++++++++
 .../console/SyncopeConsoleApplicationTest.java     |  69 ++++++++++
 client/console/src/test/resources/log4j2.xml       |  54 ++++++++
 .../console/panels/LinkedAccountModalPanel.java    |   3 -
 client/idrepo/console/pom.xml                      |  24 ++--
 .../client/console/SyncopeWebApplication.java      |  28 ++--
 .../console/src/main/resources/console.properties  |  16 ++-
 client/idrepo/enduser/pom.xml                      |   8 +-
 .../client/enduser/SyncopeWebApplication.java      |  38 ++++--
 .../enduser/src/main/resources/enduser.properties  |   8 +-
 .../client/enduser/pages/Login_it.properties       |   2 +-
 core/starter/pom.xml                               |   8 +-
 .../enduser/src/main/resources/enduser.properties  |  26 ----
 fit/build-tools/pom.xml                            |   8 +-
 .../src/main/resources/console.properties          |  16 ++-
 .../src/test/resources/console.properties          |   9 +-
 .../src/test/resources/enduser.properties          |   8 +-
 .../src/main/resources/enduser.properties          |   8 +-
 .../src/test/resources/enduser.properties          |  25 ----
 pom.xml                                            |  16 ++-
 sra/pom.xml                                        |   8 +-
 21 files changed, 394 insertions(+), 129 deletions(-)

diff --git a/client/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java b/client/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java
new file mode 100644
index 0000000..6e4372b
--- /dev/null
+++ b/client/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Stream;
+import javax.servlet.ServletContext;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.cxf.jaxrs.client.Client;
+import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup;
+import org.apache.syncope.client.console.init.ConsoleInitializer;
+import org.apache.syncope.client.console.init.MIMETypesLoader;
+import org.apache.syncope.client.lib.AuthenticationHandler;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.info.NumbersInfo;
+import org.apache.syncope.common.lib.info.PlatformInfo;
+import org.apache.syncope.common.lib.info.SystemInfo;
+import org.apache.syncope.common.lib.to.DomainTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.service.DomainService;
+import org.apache.syncope.common.rest.api.service.SyncopeService;
+import org.apache.wicket.util.tester.WicketTester;
+import org.junit.jupiter.api.BeforeAll;
+
+public abstract class AbstractTest {
+
+    protected static Properties PROPS;
+
+    public interface SyncopeServiceClient extends SyncopeService, Client {
+    }
+
+    @BeforeAll
+    public static void loadProps() throws IOException {
+        PROPS = new Properties();
+        try (InputStream is = AbstractTest.class.getResourceAsStream("/console.properties")) {
+            PROPS.load(is);
+        }
+    }
+
+    protected static final WicketTester TESTER = new WicketTester(new SyncopeConsoleApplication() {
+
+        @Override
+        protected void init() {
+            ServletContext ctx = getServletContext();
+            ClassPathScanImplementationLookup lookup = new ClassPathScanImplementationLookup();
+            lookup.load();
+            ctx.setAttribute(ConsoleInitializer.CLASSPATH_LOOKUP, lookup);
+
+            MIMETypesLoader mimeTypes = new MIMETypesLoader();
+            mimeTypes.load();
+            ctx.setAttribute(ConsoleInitializer.MIMETYPES_LOADER, mimeTypes);
+
+            super.init();
+        }
+
+        @Override
+        public List<String> getDomains() {
+            return super.getDomains();
+        }
+
+        private SyncopeService getSyncopeService() {
+            SyncopeServiceClient service = mock(SyncopeServiceClient.class);
+            when(service.type(anyString())).thenReturn(service);
+            when(service.accept(anyString())).thenReturn(service);
+
+            when(service.platform()).thenReturn(new PlatformInfo());
+            when(service.system()).thenReturn(new SystemInfo());
+
+            NumbersInfo numbersInfo = new NumbersInfo();
+            Stream.of(NumbersInfo.ConfItem.values()).
+                    forEach(item -> numbersInfo.getConfCompleteness().put(item.name(), true));
+            when(service.numbers()).thenReturn(numbersInfo);
+
+            return service;
+        }
+
+        private UserTO getUserTO() {
+            UserTO userTO = new UserTO();
+            userTO.setUsername("username");
+            return userTO;
+        }
+
+        private DomainService getDomainService() {
+            DomainService domainService = mock(DomainService.class);
+            DomainTO domainTO = new DomainTO();
+            domainTO.setKey(SyncopeConstants.MASTER_DOMAIN);
+            when(domainService.list()).thenReturn(Collections.singletonList(domainTO));
+            return domainService;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public SyncopeClientFactoryBean newClientFactory() {
+            SyncopeClient client = mock(SyncopeClient.class);
+
+            when(client.self()).thenReturn(Pair.of(new HashMap<>(), getUserTO()));
+
+            SyncopeService syncopeService = getSyncopeService();
+            when(client.getService(SyncopeService.class)).thenReturn(syncopeService);
+
+            DomainService domainService = getDomainService();
+            when(client.getService(DomainService.class)).thenReturn(domainService);
+
+            SyncopeClientFactoryBean clientFactory = mock(SyncopeClientFactoryBean.class);
+            when(clientFactory.setDomain(any())).thenReturn(clientFactory);
+            when(clientFactory.create(any(AuthenticationHandler.class))).thenReturn(client);
+            when(clientFactory.create(anyString(), anyString())).thenReturn(client);
+
+            return clientFactory;
+        }
+    });
+
+}
diff --git a/client/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java b/client/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java
new file mode 100644
index 0000000..a1f0bd1
--- /dev/null
+++ b/client/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.pages.Dashboard;
+import org.apache.syncope.client.console.pages.Login;
+import org.apache.wicket.util.tester.FormTester;
+import org.junit.jupiter.api.Test;
+
+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();
+        assertEquals(4, securityHeaders.size());
+
+        // 1. anonymous
+        TESTER.startPage(Login.class);
+        TESTER.assertRenderedPage(Login.class);
+        securityHeaders.forEach((key, value) -> assertEquals(value, TESTER.getLastResponse().getHeader(key)));
+
+        // 2. authenticated
+        FormTester formTester = TESTER.newFormTester("login");
+        formTester.setValue("username", "username");
+        formTester.setValue("password", "password");
+        formTester.submit("submit");
+
+        TESTER.assertRenderedPage(Dashboard.class);
+        securityHeaders.forEach((key, value) -> assertEquals(value, TESTER.getLastResponse().getHeader(key)));
+    }
+}
diff --git a/client/console/src/test/resources/log4j2.xml b/client/console/src/test/resources/log4j2.xml
new file mode 100644
index 0000000..28257a5
--- /dev/null
+++ b/client/console/src/test/resources/log4j2.xml
@@ -0,0 +1,54 @@
+<?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>
+
+    <Console name="main" target="SYSTEM_OUT">
+      <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger - %msg%n"/>
+    </Console>
+    
+  </appenders>
+
+  <loggers>
+
+    <asyncLogger name="org.apache.syncope.client.lib" additivity="false" level="OFF">
+      <appender-ref ref="main"/>
+    </asyncLogger>
+
+    <asyncLogger name="org.apache.syncope.client.console" additivity="false" level="ERROR">
+      <appender-ref ref="main"/>
+    </asyncLogger>
+
+    <asyncLogger name="org.apache.wicket" additivity="false" level="ERROR">
+      <appender-ref ref="main"/>
+    </asyncLogger>
+    
+    <asyncLogger name="org.apache.cxf" additivity="false" level="ERROR">
+      <appender-ref ref="main"/>
+    </asyncLogger>
+    
+    <root level="ERROR">
+      <appender-ref ref="main"/>
+    </root>
+  
+  </loggers>
+  
+</configuration>
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
index a9680e3..a5d64db 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.console.panels;
 
-import static org.apache.syncope.client.console.panels.AbstractModalPanel.LOG;
-
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -228,5 +226,4 @@ public class LinkedAccountModalPanel extends AbstractModalPanel<Serializable> {
     private void checkAddButton() {
         addAjaxLink.setVisible(SyncopeConsoleSession.get().owns(IdRepoEntitlement.USER_UPDATE));
     }
-
 }
diff --git a/client/idrepo/console/pom.xml b/client/idrepo/console/pom.xml
index 30523ab..86e69aa 100644
--- a/client/idrepo/console/pom.xml
+++ b/client/idrepo/console/pom.xml
@@ -117,28 +117,34 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-api</artifactId>
+      <artifactId>log4j-core</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core</artifactId>
+      <artifactId>log4j-slf4j-impl</artifactId>
     </dependency>
     <dependency>
       <groupId>com.lmax</groupId>
       <artifactId>disruptor</artifactId>
     </dependency>
+
+    <!-- required by wicket tester -->
     <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-slf4j-impl</artifactId>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
     </dependency>
+
     <dependency>
-      <groupId>commons-logging</groupId>
-      <artifactId>commons-logging</artifactId>
-      <scope>provided</scope>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>jcl-over-slf4j</artifactId>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter</artifactId>
+      <scope>test</scope>
     </dependency>
   </dependencies>
   
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 0b451fa..41f69d9 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
@@ -23,7 +23,6 @@ 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.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -85,7 +84,7 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
     private static final String CONSOLE_PROPERTIES = "console.properties";
 
     public static final List<Locale> SUPPORTED_LOCALES = List.of(
-        Locale.ENGLISH, Locale.ITALIAN, new Locale("pt", "BR"), new Locale("ru"), Locale.JAPANESE);
+            Locale.ENGLISH, Locale.ITALIAN, new Locale("pt", "BR"), new Locale("ru"), Locale.JAPANESE);
 
     public static SyncopeWebApplication get() {
         return (SyncopeWebApplication) WebApplication.get();
@@ -162,6 +161,17 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
         }
     }
 
+    protected 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));
+            }
+        }
+    }
+
     private NetworkService getNetworkService() {
         NetworkService ns = new NetworkService();
         ns.setType(NetworkService.Type.CONSOLE);
@@ -194,8 +204,6 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
         maxPoolSize = Integer.valueOf(props.getProperty("executor.maxPoolSize", "10"));
         queueCapacity = Integer.valueOf(props.getProperty("executor.queueCapacity", "50"));
 
-        boolean csrf = BooleanUtils.toBoolean(props.getProperty("csrf"));
-
         // process page properties
         pageClasses = new HashMap<>();
         populatePageClasses(props);
@@ -222,10 +230,6 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
         getMarkupSettings().setStripWicketTags(true);
         getMarkupSettings().setCompressWhitespace(true);
 
-        if (csrf) {
-            getRequestCycleListeners().add(new WebSocketAwareCsrfPreventionRequestCycleListener());
-        }
-
         getRequestCycleListeners().add(new SyncopeUIRequestCycleListener() {
 
             @Override
@@ -244,15 +248,15 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
             }
         });
 
+        if (BooleanUtils.toBoolean(props.getProperty("csrf"))) {
+            getRequestCycleListeners().add(new WebSocketAwareCsrfPreventionRequestCycleListener());
+        }
         getRequestCycleListeners().add(new IRequestCycleListener() {
 
             @Override
             public void onEndRequest(final RequestCycle cycle) {
                 if (cycle.getResponse() instanceof WebResponse && !(cycle.getResponse() instanceof WebSocketResponse)) {
-                    WebResponse response = (WebResponse) cycle.getResponse();
-                    response.setHeader("X-XSS-Protection", "1; mode=block");
-                    response.setHeader("X-Content-Type-Options", "nosniff");
-                    response.setHeader("X-Frame-Options", "sameorigin");
+                    setSecurityHeaders(props, (WebResponse) cycle.getResponse());
                 }
             }
         });
diff --git a/client/idrepo/console/src/main/resources/console.properties b/client/idrepo/console/src/main/resources/console.properties
index 1929f4f..c68e3ba 100644
--- a/client/idrepo/console/src/main/resources/console.properties
+++ b/client/idrepo/console/src/main/resources/console.properties
@@ -25,8 +25,6 @@ maxUploadFileSizeMB=5
 # Max wait time on apply changes from modals/wizards (given in seconds)
 maxWaitTimeOnApplyChanges=30
 
-csrf=true
-
 reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
 
 page.dashboard=org.apache.syncope.client.console.pages.Dashboard
@@ -41,6 +39,14 @@ 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
 
-executor.corePoolSize=10
-executor.maxPoolSize=20
-executor.queueCapacity=50
+topology.corePoolSize=10
+topology.maxPoolSize=20
+topology.queueCapacity=50
+
+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/client/idrepo/enduser/pom.xml b/client/idrepo/enduser/pom.xml
index 2dad2c1..4291275 100644
--- a/client/idrepo/enduser/pom.xml
+++ b/client/idrepo/enduser/pom.xml
@@ -100,21 +100,17 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-api</artifactId>
+      <artifactId>log4j-core</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core</artifactId>
+      <artifactId>log4j-slf4j-impl</artifactId>
     </dependency>
     <dependency>
       <groupId>com.lmax</groupId>
       <artifactId>disruptor</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-slf4j-impl</artifactId>
-    </dependency>
-    <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
       <scope>provided</scope>
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 65a8ff0..63b6654 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
@@ -27,6 +27,7 @@ import de.agilecoders.wicket.core.settings.IBootstrapSettings;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -68,6 +69,9 @@ import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.request.Request;
 import org.apache.wicket.request.Response;
 import org.apache.wicket.request.component.IRequestablePage;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.WebResponse;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.AbstractResource;
 import org.apache.wicket.request.resource.IResource;
@@ -90,7 +94,7 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
     private static final String CUSTOM_FORM_ATTRIBUTES_FILE = "customFormAttributes.json";
 
     public static final List<Locale> SUPPORTED_LOCALES = List.of(
-        Locale.ENGLISH, Locale.ITALIAN, new Locale("pt", "BR"), new Locale("ru"), Locale.JAPANESE);
+            Locale.ENGLISH, Locale.ITALIAN, new Locale("pt", "BR"), new Locale("ru"), Locale.JAPANESE);
 
     private static final ObjectMapper MAPPER = new ObjectMapper();
 
@@ -129,6 +133,17 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
 
     private Map<String, CustomAttributesInfo> customFormAttributes;
 
+    protected 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));
+            }
+        }
+    }
+
     private NetworkService getNetworkService() {
         NetworkService ns = new NetworkService();
         ns.setType(NetworkService.Type.ENDUSER);
@@ -153,9 +168,6 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
         captchaEnabled = Boolean.parseBoolean(props.getProperty("captcha"));
         Args.notNull(captchaEnabled, "<captcha>");
 
-        boolean xsrf = Boolean.parseBoolean(props.getProperty("xsrf"));
-        Args.notNull(xsrf, "<xsrf>");
-
         useGZIPCompression = BooleanUtils.toBoolean(props.getProperty("useGZIPCompression"));
         Args.notNull(useGZIPCompression, "<useGZIPCompression>");
         maxUploadFileSizeMB = props.getProperty("maxUploadFileSizeMB") == null
@@ -268,10 +280,6 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
             }
         });
 
-        if (xsrf) {
-            getRequestCycleListeners().add(new CsrfPreventionRequestCycleListener());
-        }
-
         getRequestCycleListeners().add(new SyncopeUIRequestCycleListener() {
 
             @Override
@@ -291,6 +299,19 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
 
         });
 
+        if (BooleanUtils.toBoolean(props.getProperty("csrf"))) {
+            getRequestCycleListeners().add(new CsrfPreventionRequestCycleListener());
+        }
+        getRequestCycleListeners().add(new IRequestCycleListener() {
+
+            @Override
+            public void onEndRequest(final RequestCycle cycle) {
+                if (cycle.getResponse() instanceof WebResponse) {
+                    setSecurityHeaders(props, (WebResponse) cycle.getResponse());
+                }
+            }
+        });
+
         // Confirm password reset page
         mountPage("/confirmpasswordreset", SelfConfirmPasswordReset.class);
 
@@ -414,5 +435,4 @@ public class SyncopeWebApplication extends WicketBootStandardWebApplication {
             LOG.error("While extracting ext attributes", e);
         }
     }
-
 }
diff --git a/client/idrepo/enduser/src/main/resources/enduser.properties b/client/idrepo/enduser/src/main/resources/enduser.properties
index 5adb55a..27e3053 100644
--- a/client/idrepo/enduser/src/main/resources/enduser.properties
+++ b/client/idrepo/enduser/src/main/resources/enduser.properties
@@ -23,4 +23,10 @@ useGZIPCompression=true
 maxUploadFileSizeMB=5
 
 captcha=true
-xsrf=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/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/Login_it.properties b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/Login_it.properties
index e54db3c..d2fb118 100644
--- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/Login_it.properties
+++ b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/Login_it.properties
@@ -18,5 +18,5 @@ username=Username
 password=Password
 submit=Login
 login-error=Username e/o password errati
-self-registration=Registratione
+self-registration=Registrazione
 self-pwd-reset=Reset Password
diff --git a/core/starter/pom.xml b/core/starter/pom.xml
index f492545..ad2baf5 100644
--- a/core/starter/pom.xml
+++ b/core/starter/pom.xml
@@ -79,21 +79,17 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-api</artifactId>
+      <artifactId>log4j-core</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core</artifactId>
+      <artifactId>log4j-slf4j-impl</artifactId>
     </dependency>
     <dependency>
       <groupId>com.lmax</groupId>
       <artifactId>disruptor</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-slf4j-impl</artifactId>
-    </dependency>
-    <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
       <scope>provided</scope>
diff --git a/docker/enduser/src/main/resources/enduser.properties b/docker/enduser/src/main/resources/enduser.properties
deleted file mode 100644
index 5adb55a..0000000
--- a/docker/enduser/src/main/resources/enduser.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.
-enduser.directory=${conf.directory}
-
-anonymousUser=${anonymousUser}
-anonymousKey=${anonymousKey}
-adminUser=${adminUser}
-useGZIPCompression=true
-maxUploadFileSizeMB=5
-
-captcha=true
-xsrf=true
diff --git a/fit/build-tools/pom.xml b/fit/build-tools/pom.xml
index 1646a64..18158c5 100644
--- a/fit/build-tools/pom.xml
+++ b/fit/build-tools/pom.xml
@@ -201,21 +201,17 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-api</artifactId>
+      <artifactId>log4j-core</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core</artifactId>
+      <artifactId>log4j-slf4j-impl</artifactId>
     </dependency>
     <dependency>
       <groupId>com.lmax</groupId>
       <artifactId>disruptor</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-slf4j-impl</artifactId>
-    </dependency>
-    <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
       <scope>provided</scope>
diff --git a/fit/console-reference/src/main/resources/console.properties b/fit/console-reference/src/main/resources/console.properties
index a2b3bad..0b51e78 100644
--- a/fit/console-reference/src/main/resources/console.properties
+++ b/fit/console-reference/src/main/resources/console.properties
@@ -25,8 +25,6 @@ maxUploadFileSizeMB=5
 # Max wait time on apply changes from modals/wizards (given in seconds)
 maxWaitTimeOnApplyChanges=30
 
-csrf=true
-
 reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
 
 page.dashboard=org.apache.syncope.client.console.pages.Dashboard
@@ -41,6 +39,14 @@ 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
 
-executor.corePoolSize=50
-executor.maxPoolSize=100
-executor.queueCapacity=10
+topology.corePoolSize=50
+topology.maxPoolSize=100
+topology.queueCapacity=10
+
+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/core-reference/src/test/resources/console.properties b/fit/core-reference/src/test/resources/console.properties
index 10e2d4f..0c6250c 100644
--- a/fit/core-reference/src/test/resources/console.properties
+++ b/fit/core-reference/src/test/resources/console.properties
@@ -25,8 +25,6 @@ maxUploadFileSizeMB=5
 # Max wait time on apply changes from modals/wizards (given in seconds)
 maxWaitTimeOnApplyChanges=30
 
-csrf=false
-
 reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
 
 page.dashboard=org.apache.syncope.client.console.pages.Dashboard
@@ -45,3 +43,10 @@ executor.corePoolSize=50
 executor.maxPoolSize=100
 executor.queueCapacity=10
 
+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/test/resources/enduser.properties b/fit/core-reference/src/test/resources/enduser.properties
index 97a84fc..fc07dfa 100644
--- a/fit/core-reference/src/test/resources/enduser.properties
+++ b/fit/core-reference/src/test/resources/enduser.properties
@@ -22,4 +22,10 @@ adminUser=${adminUser}
 useGZIPCompression=true
 
 captcha=false
-xsrf=false
+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/enduser-reference/src/main/resources/enduser.properties b/fit/enduser-reference/src/main/resources/enduser.properties
index 5adb55a..27e3053 100644
--- a/fit/enduser-reference/src/main/resources/enduser.properties
+++ b/fit/enduser-reference/src/main/resources/enduser.properties
@@ -23,4 +23,10 @@ useGZIPCompression=true
 maxUploadFileSizeMB=5
 
 captcha=true
-xsrf=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/enduser-reference/src/test/resources/enduser.properties b/fit/enduser-reference/src/test/resources/enduser.properties
deleted file mode 100644
index 97a84fc..0000000
--- a/fit/enduser-reference/src/test/resources/enduser.properties
+++ /dev/null
@@ -1,25 +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
-
-captcha=false
-xsrf=false
diff --git a/pom.xml b/pom.xml
index 17685e1..ec14e66 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1500,16 +1500,16 @@ under the License.
         <version>${log4j.version}</version>
       </dependency>
       <dependency>
-        <groupId>com.lmax</groupId>
-        <artifactId>disruptor</artifactId>
-        <version>${disruptor.version}</version>
-      </dependency>
-      <dependency>
         <groupId>org.apache.logging.log4j</groupId>
         <artifactId>log4j-slf4j-impl</artifactId>
         <version>${log4j.version}</version>
       </dependency>
       <dependency>
+        <groupId>com.lmax</groupId>
+        <artifactId>disruptor</artifactId>
+        <version>${disruptor.version}</version>
+      </dependency>
+      <dependency>
         <groupId>commons-logging</groupId>
         <artifactId>commons-logging</artifactId>
         <version>${commons-logging.version}</version>
@@ -1788,6 +1788,12 @@ under the License.
         <scope>test</scope>
       </dependency>
       <dependency>
+        <groupId>org.mockito</groupId>
+        <artifactId>mockito-core</artifactId>
+        <version>3.1.0</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
         <groupId>org.junit.jupiter</groupId>
         <artifactId>junit-jupiter</artifactId>
         <version>${junit.version}</version>
diff --git a/sra/pom.xml b/sra/pom.xml
index adfe9e6..db03aae 100644
--- a/sra/pom.xml
+++ b/sra/pom.xml
@@ -94,21 +94,17 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-api</artifactId>
+      <artifactId>log4j-core</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core</artifactId>
+      <artifactId>log4j-slf4j-impl</artifactId>
     </dependency>
     <dependency>
       <groupId>com.lmax</groupId>
       <artifactId>disruptor</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-slf4j-impl</artifactId>
-    </dependency>
-    <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
       <scope>provided</scope>