From fea4315aa32b03bc04cc8a16ad623a2a7840a240 Mon Sep 17 00:00:00 2001 From: Carlos Afonso <50378106+carlosjepard@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:09:25 +0100 Subject: [PATCH] Changed dismiss cookie to localstorage and added headers roda 5 (#3272) Signed-off-by: carlosjepard --- docker/Dockerfile | 5 ++- .../src/main/java/org/roda/wui/RODA.java | 27 +++++++++++++ .../wui/filter/SecurityHeadersFilter.java | 38 +++++++++++++++++++ .../config/theme/cookies/cookieconsent.js | 33 ++++------------ .../src/main/webapp/META-INF/context.xml | 8 ++++ .../roda-wui/src/main/webapp/WEB-INF/web.xml | 19 ++++++++++ 6 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/filter/SecurityHeadersFilter.java create mode 100644 roda-ui/roda-wui/src/main/webapp/META-INF/context.xml diff --git a/docker/Dockerfile b/docker/Dockerfile index f10220e376..6ffacc1b9e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -50,8 +50,9 @@ COPY ./docker-files/docker-entrypoint.sh / COPY ./docker-files/docker-entrypoint.d/* /docker-entrypoint.d/ RUN set -ex; \ - groupadd -r --gid "$RODA_GID" "$RODA_GROUP"; \ - useradd -r --uid "$RODA_UID" --gid "$RODA_GID" "$RODA_USER"; \ + usermod -l roda ubuntu; \ + usermod -d /home/roda -m roda; \ + groupmod -n roda ubuntu; \ mkdir -p -m0770 "$RODA_HOME/data"; \ mkdir -p -m0770 "$RODA_HOME/config"; \ chown -R "$RODA_USER:0" "$RODA_HOME" diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java index 5baea64d51..48e1fbae8a 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java @@ -7,13 +7,18 @@ */ package org.roda.wui; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; import org.apereo.cas.client.session.SingleSignOutHttpSessionListener; import org.roda.wui.filter.OnOffFilter; +import org.roda.wui.filter.SecurityHeadersFilter; import org.roda.wui.servlets.ContextListener; import org.roda.wui.servlets.RodaWuiServlet; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; @@ -57,6 +62,8 @@ public FilterRegistrationBean internalWebAuthFilter() { return registrationBean; } + + @Bean public FilterRegistrationBean internalApiAuthFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); @@ -192,6 +199,14 @@ public ServletRegistrationBean clientLogger() { return bean; } + @Bean + public FilterRegistrationBean securityHeadersFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new SecurityHeadersFilter()); + registrationBean.addUrlPatterns("/*"); // Apply the filter to all requests + return registrationBean; + } + @Bean public ServletRegistrationBean userManagementService() { ServletRegistrationBean bean; @@ -216,6 +231,18 @@ public ServletRegistrationBean browserService() { return bean; } + @Bean + public ServletContextInitializer servletContextInitializer() { + return new ServletContextInitializer() { + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + servletContext.getSessionCookieConfig().setSecure(true); + servletContext.getSessionCookieConfig().setHttpOnly(true); + } + }; + } + @Configuration public static class DefaultView implements WebMvcConfigurer { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/filter/SecurityHeadersFilter.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/filter/SecurityHeadersFilter.java new file mode 100644 index 0000000000..7c0da060a6 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/filter/SecurityHeadersFilter.java @@ -0,0 +1,38 @@ +package org.roda.wui.filter; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class SecurityHeadersFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + httpServletResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); + httpServletResponse.setHeader("Content-Security-Policy", + "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com " + + "https://www.google-analytics.com https://www.gstatic.com; style-src 'self' 'unsafe-inline'; " + + "img-src 'self'; font-src 'self';"); + httpServletResponse.setHeader("X-XSS-Protection", "1; mode=block"); + httpServletResponse.setHeader("X-Permitted-Cross-Domain-Policies", "none"); + httpServletResponse.setHeader("Feature-Policy", "camera 'none'; fullscreen 'self'; geolocation *; " + + "microphone 'self'"); + httpServletResponse.setHeader("X-Frame-Options", "SAMEORIGIN"); + httpServletResponse.setHeader("X-Content-Type-Options", "nosniff"); + httpServletResponse.setHeader("Referrer-Policy", "no-referrer"); + httpServletResponse.setHeader("Permissions-Policy", "geolocation=(self)"); + + chain.doFilter(request, response); + } + + @Override + public void init(FilterConfig filterConfig) { + } + + @Override + public void destroy() { + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/resources/config/theme/cookies/cookieconsent.js b/roda-ui/roda-wui/src/main/resources/config/theme/cookies/cookieconsent.js index f4a3500eaa..331c80f9ab 100644 --- a/roda-ui/roda-wui/src/main/resources/config/theme/cookies/cookieconsent.js +++ b/roda-ui/roda-wui/src/main/resources/config/theme/cookies/cookieconsent.js @@ -13,8 +13,8 @@ // Change cookie consent options on the fly. var OPTIONS_UPDATER = 'update_cookieconsent_options'; - // Name of cookie to be set when dismissed - var DISMISSED_COOKIE = 'cookieconsent_dismissed'; + // Key used to store consent status in localStorage + var DISMISSED_KEY = 'cookieconsent_dismissed'; // The path to built in themes // Note: Directly linking to a version on the CDN like this is horrible, but it's less horrible than people downloading the code @@ -22,7 +22,7 @@ var THEME_BUCKET_PATH = '//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/1.0.10/'; // No point going further if they've already dismissed. - if (document.cookie.indexOf(DISMISSED_COOKIE) > -1 || (window.navigator && window.navigator.CookiesOK)) { + if (localStorage.getItem(DISMISSED_KEY) || (window.navigator && window.navigator.CookiesOK)) { return; } @@ -93,25 +93,8 @@ return null; }, - setCookie: function (name, value, expiryDays, domain, path) { - expiryDays = expiryDays || 365; - - var exdate = new Date(); - exdate.setDate(exdate.getDate() + expiryDays); - - var cookie = [ - name + '=' + value, - 'expires=' + exdate.toUTCString(), - 'path=' + path || '/' - ]; - - if (domain) { - cookie.push( - 'domain=' + domain - ); - } - - document.cookie = cookie.join(';'); + setLocalStorage: function (key, value) { + localStorage.setItem(key, value); }, addEventListener: function (el, event, eventListener) { @@ -332,12 +315,12 @@ dismiss: function (evt) { evt.preventDefault && evt.preventDefault(); evt.returnValue = false; - this.setDismissedCookie(); + this.setDismissed(); this.container.removeChild(this.element); }, - setDismissedCookie: function () { - Util.setCookie(DISMISSED_COOKIE, 'yes', this.options.expiryDays, this.options.domain, this.options.path); + setDismissed: function () { + Util.setLocalStorage(DISMISSED_KEY, 'yes'); } }; diff --git a/roda-ui/roda-wui/src/main/webapp/META-INF/context.xml b/roda-ui/roda-wui/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000000..566e38d24e --- /dev/null +++ b/roda-ui/roda-wui/src/main/webapp/META-INF/context.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml b/roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml index dee5232024..87594376c2 100644 --- a/roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml +++ b/roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml @@ -234,6 +234,15 @@ /logout + + SecurityHeadersFilter + org.roda.wui.filter.SecurityHeadersFilter + + + SecurityHeadersFilter + /* + + @@ -338,4 +347,14 @@ + + + + true + true + + + + +