From 48c46bb3374bdc6b4c17d69786636291c43c3129 Mon Sep 17 00:00:00 2001 From: Grzegorz Grzybek Date: Tue, 13 Sep 2022 12:45:16 +0200 Subject: [PATCH] [Fixes #1756] Backport SameSite attribute handling from Pax Web 8 --- .../web/service/WebContainerConstants.java | 1 + .../service/jetty/internal/JettyServer.java | 2 ++ .../jetty/internal/JettyServerImpl.java | 3 ++- .../jetty/internal/JettyServerWrapper.java | 23 +++++++++++++++++-- .../jetty/internal/ServerControllerImpl.java | 1 + .../service/internal/ConfigurationImpl.java | 5 ++++ .../resources/OSGI-INF/metatype/metatype.xml | 1 + .../pax/web/service/spi/Configuration.java | 4 ++++ .../tomcat/internal/EmbeddedTomcat.java | 3 +++ .../internal/ServerControllerImpl.java | 17 ++++++++++++++ 10 files changed, 57 insertions(+), 3 deletions(-) diff --git a/pax-web-api/src/main/java/org/ops4j/pax/web/service/WebContainerConstants.java b/pax-web-api/src/main/java/org/ops4j/pax/web/service/WebContainerConstants.java index dd318a3bb1..8d7854d6d2 100644 --- a/pax-web-api/src/main/java/org/ops4j/pax/web/service/WebContainerConstants.java +++ b/pax-web-api/src/main/java/org/ops4j/pax/web/service/WebContainerConstants.java @@ -117,6 +117,7 @@ public interface WebContainerConstants { String PROPERTY_SESSION_COOKIE_HTTP_ONLY = PID + ".session.cookie.httpOnly"; String PROPERTY_SESSION_COOKIE_SECURE = PID + ".session.cookie.secure"; String PROPERTY_SESSION_COOKIE_MAX_AGE = PID + ".session.cookie.maxAge"; + String PROPERTY_SESSION_COOKIE_SAME_SITE = PID + ".session.cookie.sameSite"; String PROPERTY_SESSION_LAZY_LOAD = PID + ".session.lazyload"; String PROPERTY_SESSION_STORE_DIRECTORY = PID + ".session.storedirectory"; diff --git a/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServer.java b/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServer.java index 87932803a8..744655ff92 100644 --- a/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServer.java +++ b/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServer.java @@ -66,6 +66,7 @@ public interface JettyServer { * null or "none" no URL rewriting will be done. * @param sessionCookieHttpOnly if set, the session cookie is not available to client side * scripting. + * @param sessionCookieSameSite * @param sessionCookieSecure if set, the session cookie is only transfered over secure * transports (https). * @param sessionWorkerName name appended to session id, used to assist session affinity @@ -77,6 +78,7 @@ public interface JettyServer { void configureContext(Map attributes, Integer sessionTimeout, String sessionCookie, String sessionDomain, String sessionPath, String sessionUrl, Boolean sessionCookieHttpOnly, + String sessionCookieSameSite, Boolean sessionCookieSecure, String sessionWorkerName, Boolean lazyLoad, String storeDirectory, Integer maxAge, Boolean showStacks); diff --git a/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerImpl.java b/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerImpl.java index 68356f436e..c4e5bc4f13 100644 --- a/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerImpl.java +++ b/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerImpl.java @@ -297,11 +297,12 @@ public void configureContext(final Map attributes, final Integer sessionTimeout, final String sessionCookie, final String sessionDomain, final String sessionPath, final String sessionUrl, final Boolean sessionCookieHttpOnly, + final String sessionCookieSameSite, final Boolean sessionCookieSecure, final String workerName, final Boolean lazyLoad, final String storeDirectory, final Integer maxAge, final Boolean showStacks) { server.configureContext(attributes, sessionTimeout, sessionCookie, - sessionDomain, sessionPath, sessionUrl, sessionCookieHttpOnly, + sessionDomain, sessionPath, sessionUrl, sessionCookieHttpOnly, sessionCookieSameSite, sessionCookieSecure, workerName, lazyLoad, storeDirectory, maxAge, showStacks); } diff --git a/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerWrapper.java b/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerWrapper.java index 9a4765f4c2..b67d2516d7 100644 --- a/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerWrapper.java +++ b/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerWrapper.java @@ -31,6 +31,7 @@ import javax.servlet.ServletContainerInitializer; +import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.security.authentication.BasicAuthenticator; @@ -122,6 +123,7 @@ public HttpServiceContext getHandler() { private String defaultRealmName; private Boolean sessionCookieHttpOnly; + private String sessionCookieSameSite; private Boolean sessionCookieSecure; @@ -165,6 +167,7 @@ public HandlerCollection getRootHandlerCollection() { public void configureContext(final Map attributes, final Integer timeout, final String cookie, final String domain, final String path, final String url, final Boolean cookieHttpOnly, + final String sessionCookieSameSite, final Boolean sessionCookieSecure, final String workerName, final Boolean lazy, final String directory, Integer maxAge, final Boolean showStacks) { this.contextAttributes = attributes; @@ -174,6 +177,7 @@ public void configureContext(final Map attributes, final Integer this.sessionPath = path; this.sessionUrl = url; this.sessionCookieHttpOnly = cookieHttpOnly; + this.sessionCookieSameSite = sessionCookieSameSite; this.sessionCookieSecure = sessionCookieSecure; this.sessionWorkerName = workerName; lazyLoad = lazy; @@ -337,9 +341,20 @@ private HttpServiceContext addContext(final ContextModel model) { if (maxAge == null) { maxAge = -1; } + String sameSiteValue = sessionCookieSameSite; + HttpCookie.SameSite sameSite = null; + if (sameSiteValue != null && !"unset".equalsIgnoreCase(sameSiteValue)) { + if ("none".equalsIgnoreCase(sameSiteValue)) { + sameSite = HttpCookie.SameSite.NONE; + } else if ("lax".equalsIgnoreCase(sameSiteValue)) { + sameSite = HttpCookie.SameSite.LAX; + } else if ("strict".equalsIgnoreCase(sameSiteValue)) { + sameSite = HttpCookie.SameSite.STRICT; + } + } configureSessionManager(context, modelSessionTimeout, modelSessionCookie, modelSessionDomain, modelSessionPath, modelSessionUrl, modelSessionCookieHttpOnly, modelSessionSecure, workerName, lazyLoad, storeDirectory, - maxAge); + maxAge, sameSite); if (this.defaultAuthMethod != null && model.getAuthMethod() == null) { model.setAuthMethod(this.defaultAuthMethod); @@ -572,11 +587,12 @@ private Map getContextAttributes(final BundleContext bundleConte * @param workerName name appended to session id, used to assist session affinity * in a load balancer * @param maxAge session cookie maxAge + * @param sameSite */ private void configureSessionManager(final ServletContextHandler context, final Integer minutes, final String cookie, String domain, String path, final String url, final Boolean cookieHttpOnly, final Boolean secure, final String workerName, final Boolean lazy, final String directory, - final int maxAge) { + final int maxAge, HttpCookie.SameSite sameSite) { LOG.debug("configureSessionManager for context [" + context + "] using - timeout:" + minutes + ", cookie:" + cookie + ", url:" + url + ", cookieHttpOnly:" + cookieHttpOnly + ", workerName:" + workerName + ", lazyLoad:" + lazy + ", storeDirectory: " + directory); @@ -618,6 +634,9 @@ private void configureSessionManager(final ServletContextHandler context, final ((DefaultSessionIdManager) sessionHandler.getSessionIdManager()).setWorkerName(workerName); LOG.debug("Worker name set to {} for context [{}]", workerName, context); } + if (sameSite != null) { + sessionHandler.setSameSite(sameSite); + } } } diff --git a/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/ServerControllerImpl.java b/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/ServerControllerImpl.java index 36a3089666..32442318de 100644 --- a/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/ServerControllerImpl.java +++ b/pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/ServerControllerImpl.java @@ -470,6 +470,7 @@ public void start() { configuration.getSessionPath(), configuration.getSessionUrl(), configuration.getSessionCookieHttpOnly(), + configuration.getSessionCookieSameSite(), configuration.getSessionCookieSecure(), configuration.getWorkerName(), configuration.getSessionLazyLoad(), diff --git a/pax-web-runtime/src/main/java/org/ops4j/pax/web/service/internal/ConfigurationImpl.java b/pax-web-runtime/src/main/java/org/ops4j/pax/web/service/internal/ConfigurationImpl.java index 38f1c60fe2..6848e55c85 100644 --- a/pax-web-runtime/src/main/java/org/ops4j/pax/web/service/internal/ConfigurationImpl.java +++ b/pax-web-runtime/src/main/java/org/ops4j/pax/web/service/internal/ConfigurationImpl.java @@ -457,6 +457,11 @@ public Integer getSessionCookieMaxAge() { return getResolvedIntegerProperty(PROPERTY_SESSION_COOKIE_MAX_AGE); } + @Override + public String getSessionCookieSameSite() { + return getResolvedStringProperty(PROPERTY_SESSION_COOKIE_SAME_SITE); + } + @Override public Boolean getSessionLazyLoad() { return getResolvedBooleanProperty(PROPERTY_SESSION_LAZY_LOAD); diff --git a/pax-web-runtime/src/main/resources/OSGI-INF/metatype/metatype.xml b/pax-web-runtime/src/main/resources/OSGI-INF/metatype/metatype.xml index da0fc1dc84..75975c2835 100644 --- a/pax-web-runtime/src/main/resources/OSGI-INF/metatype/metatype.xml +++ b/pax-web-runtime/src/main/resources/OSGI-INF/metatype/metatype.xml @@ -30,6 +30,7 @@ limitations under the License. + diff --git a/pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/Configuration.java b/pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/Configuration.java index 1a6d258623..eb70664524 100644 --- a/pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/Configuration.java +++ b/pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/Configuration.java @@ -20,6 +20,8 @@ import java.net.URL; import java.util.List; +import static org.ops4j.pax.web.service.WebContainerConstants.PROPERTY_SESSION_COOKIE_SAME_SITE; + public interface Configuration { Boolean useNIO(); @@ -132,6 +134,8 @@ public interface Configuration { Integer getSessionCookieMaxAge(); + String getSessionCookieSameSite(); + String getSessionStoreDirectory(); Boolean getSessionLazyLoad(); diff --git a/pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/EmbeddedTomcat.java b/pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/EmbeddedTomcat.java index 4bf46d2a00..6c15ec6cbd 100644 --- a/pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/EmbeddedTomcat.java +++ b/pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/EmbeddedTomcat.java @@ -155,6 +155,8 @@ public class EmbeddedTomcat extends Tomcat { private Integer configurationSessionCookieMaxAge; + private String configurationSessionCookieSameSite; + private Boolean configurationSessionCookieHttpOnly; private File configurationDir; @@ -259,6 +261,7 @@ private void mergeConfiguration(Configuration configuration) { configurationSessionCookieMaxAge = configuration.getSessionCookieMaxAge(); configurationSessionCookieHttpOnly = configuration .getSessionCookieHttpOnly(); + configurationSessionCookieSameSite = configuration.getSessionCookieSameSite(); // NCSA Logger --> AccessLogValve if (configuration.isLogNCSAFormatEnabled()) { diff --git a/pax-web-undertow/src/main/java/org/ops4j/pax/web/service/undertow/internal/ServerControllerImpl.java b/pax-web-undertow/src/main/java/org/ops4j/pax/web/service/undertow/internal/ServerControllerImpl.java index 6678d33419..1db3549284 100644 --- a/pax-web-undertow/src/main/java/org/ops4j/pax/web/service/undertow/internal/ServerControllerImpl.java +++ b/pax-web-undertow/src/main/java/org/ops4j/pax/web/service/undertow/internal/ServerControllerImpl.java @@ -63,8 +63,10 @@ import io.undertow.UndertowOptions; import io.undertow.security.idm.Account; import io.undertow.security.idm.Credential; +import io.undertow.server.handlers.CookieSameSiteMode; import io.undertow.server.handlers.DisallowedMethodsHandler; import io.undertow.server.handlers.ProxyPeerAddressHandler; +import io.undertow.server.handlers.SameSiteCookieHandler; import io.undertow.server.handlers.resource.FileResourceManager; import io.undertow.server.handlers.resource.ResourceHandler; import io.undertow.servlet.api.ServletContainer; @@ -751,6 +753,21 @@ public String getProperty(String key, String defaultValue) { if (peerHostLookup) { rootHandler = new PeerNameResolvingHandler(rootHandler); } + + // https://github.com/ops4j/org.ops4j.pax.web/issues/1727 - SameSite attribute + String sameSiteValue = configuration.getSessionCookieSameSite(); + if (sameSiteValue != null && !"unset".equalsIgnoreCase(sameSiteValue)) { + String mode = null; + if ("none".equalsIgnoreCase(sameSiteValue)) { + mode = CookieSameSiteMode.NONE.toString(); + } else if ("lax".equalsIgnoreCase(sameSiteValue)) { + mode = CookieSameSiteMode.LAX.toString(); + } else if ("strict".equalsIgnoreCase(sameSiteValue)) { + mode = CookieSameSiteMode.STRICT.toString(); + } + rootHandler = new SameSiteCookieHandler(rootHandler, mode, configuration.getSessionCookie()); + } + if (!disallowedMethods.isEmpty()) { Set disallowedMethodNames = new HashSet<>(); for (String m : disallowedMethods) {