Skip to content

Commit

Permalink
Changed dismiss cookie to localstorage and added headers
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosjepard committed Aug 5, 2024
1 parent 78d156d commit 311395b
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 27 deletions.
5 changes: 3 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
27 changes: 27 additions & 0 deletions roda-ui/roda-wui/src/main/java/org/roda/wui/RODA.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,6 +62,8 @@ public FilterRegistrationBean<OnOffFilter> internalWebAuthFilter() {
return registrationBean;
}



@Bean
public FilterRegistrationBean<OnOffFilter> internalApiAuthFilter() {
FilterRegistrationBean<OnOffFilter> registrationBean = new FilterRegistrationBean<>();
Expand Down Expand Up @@ -192,6 +199,14 @@ public ServletRegistrationBean<HttpServlet> clientLogger() {
return bean;
}

@Bean
public FilterRegistrationBean<SecurityHeadersFilter> securityHeadersFilter() {
FilterRegistrationBean<SecurityHeadersFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new SecurityHeadersFilter());
registrationBean.addUrlPatterns("/*"); // Apply the filter to all requests
return registrationBean;
}

@Bean
public ServletRegistrationBean<HttpServlet> userManagementService() {
ServletRegistrationBean<HttpServlet> bean;
Expand All @@ -216,6 +231,18 @@ public ServletRegistrationBean<HttpServlet> 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 {

Expand Down
Original file line number Diff line number Diff line change
@@ -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() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
// 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
// then discovering that their CSS bucket disappeared
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;
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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');
}
};

Expand Down
8 changes: 8 additions & 0 deletions roda-ui/roda-wui/src/main/webapp/META-INF/context.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Context>

<!-- Add SameSite to the cookies -->
<CookieProcessor
sameSiteCookies="strict" />

</Context>
19 changes: 19 additions & 0 deletions roda-ui/roda-wui/src/main/webapp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@
<url-pattern>/logout</url-pattern>
</filter-mapping>

<filter>
<filter-name>SecurityHeadersFilter</filter-name>
<filter-class>org.roda.wui.filter.SecurityHeadersFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityHeadersFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- ************************************ -->
<!-- REST related servlets/servlet-mappings -->
<!-- ************************************ -->
Expand Down Expand Up @@ -338,4 +347,14 @@
</web-resource-collection>
<auth-constraint/>
</security-constraint>

<session-config>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>



</web-app>

0 comments on commit 311395b

Please sign in to comment.