fix(springboot): add session-cookie-path-enforcement flag to isolate …#2609
Conversation
…Operaton session cookie (LF insted of crlf) - introduces operaton.bpm.webapp.session-cookie-path-enforcement property (default false) - when enabled, applies Path=/operaton to session cookie via filter to prevent JSESSIONID conflicts - resolves conflict with backend/Keycloak session when running in single Spring Boot app related to operaton#1632
There was a problem hiding this comment.
Pull request overview
This PR extends the Spring Boot webapp integration to better isolate Operaton’s session cookie in “single Spring Boot app” setups (e.g., backend + Keycloak + Operaton) by enabling optional session-cookie path enforcement and by enhancing existing cookie-related filters/config.
Changes:
- Add optional
operaton.bpm.webapp.session-cookie-path-enforcementthat registers a new response-wrapping filter to enforce aPathattribute on the session cookie. - Extend cookie configuration to support
cookiePathand improve session-cookie header detection/configuration inSessionCookieFilter. - Introduce
operaton.bpm.session.cookie.name(via newSessionProperties) and wire it into filter init params as an alias for session cookie name handling.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| webapps/assembly/src/main/java/org/operaton/bpm/webapp/impl/security/filter/SessionCookieFilter.java | Enhances Set-Cookie rewriting to also enforce a configured cookie Path. |
| webapps/assembly/src/main/java/org/operaton/bpm/webapp/impl/security/filter/CsrfPreventionFilter.java | Decouples CSRF cookie naming from session-cookie configuration by introducing a dedicated csrfCookieName. |
| webapps/assembly/src/main/java/org/operaton/bpm/webapp/impl/security/filter/CookieConfigurator.java | Adds support for parsing and exposing a cookiePath init param, and allows explicit cookieName init param override. |
| spring-boot-starter/starter/src/main/java/org/operaton/bpm/spring/boot/starter/property/SessionProperties.java | Adds new nested config object for operaton.bpm.session.cookie.name. |
| spring-boot-starter/starter/src/main/java/org/operaton/bpm/spring/boot/starter/property/SessionCookieProperties.java | Adds cookiePath property and passes it through to filter init params. |
| spring-boot-starter/starter/src/main/java/org/operaton/bpm/spring/boot/starter/property/OperatonBpmProperties.java | Wires new SessionProperties into the root configuration properties. |
| spring-boot-starter/starter-webapp-core/src/main/java/org/operaton/bpm/spring/boot/starter/webapp/filter/SessionCookiePathFilter.java | New filter that rewrites the session cookie Path via addCookie and Set-Cookie header rewriting. |
| spring-boot-starter/starter-webapp-core/src/main/java/org/operaton/bpm/spring/boot/starter/webapp/OperatonBpmWebappInitializer.java | Adds alias wiring: operaton.bpm.session.cookie.name → SessionCookieFilter init params. |
| spring-boot-starter/starter-webapp-core/src/main/java/org/operaton/bpm/spring/boot/starter/webapp/OperatonBpmWebappAutoConfiguration.java | Registers the new SessionCookiePathFilter behind the session-cookie-path-enforcement flag. |
| spring-boot-starter/starter-webapp-core/src/test/java/org/operaton/bpm/spring/boot/starter/webapp/filter/util/HttpClientExtension.java | Updates the default expected session-cookie Path segment in test regex helpers. |
Comments suppressed due to low confidence (1)
webapps/assembly/src/main/java/org/operaton/bpm/webapp/impl/security/filter/SessionCookieFilter.java:53
doFilteronly invokesfilterChain.doFilter(...)when both request/response are HTTP. If this filter is ever applied to non-HTTP requests/responses, the chain will not proceed and the request will effectively be dropped. Consider adding anelsebranch that delegates tofilterChain.doFilter(servletRequest, servletResponse)to preserve the Filter contract.
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
if ((servletRequest instanceof HttpServletRequest httpServletRequest) && (servletResponse instanceof HttpServletResponse httpServletResponse)) {
// create a session if none exists yet
httpServletRequest.getSession();
// execute filter chain with a response wrapper that handles sameSite attributes
filterChain.doFilter(httpServletRequest, new SameSiteResponseProxy(httpServletResponse));
}
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
webapps/assembly/src/main/java/org/operaton/bpm/webapp/impl/security/filter/SessionCookieFilter.java:53
- SessionCookieFilter.doFilter only invokes filterChain.doFilter() inside the HttpServletRequest/HttpServletResponse instanceof check; if the filter is ever applied to a non-HTTP request/response, the chain will not continue and the request will effectively be dropped. Consider always calling filterChain.doFilter(...), wrapping only when the request/response are HTTP.
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
if ((servletRequest instanceof HttpServletRequest httpServletRequest) && (servletResponse instanceof HttpServletResponse httpServletResponse)) {
// create a session if none exists yet
httpServletRequest.getSession();
// execute filter chain with a response wrapper that handles sameSite attributes
filterChain.doFilter(httpServletRequest, new SameSiteResponseProxy(httpServletResponse));
}
}
| public String getSessionCookieRegex(String sameSite) { | ||
| return getSessionCookieRegex(null, null, sameSite, false); | ||
| return getSessionCookieRegex(DEFAULT_SESSION_COOKIE_PATH, null, sameSite, false); | ||
| } | ||
|
|
||
| public String getSessionCookieRegex(String cookieName, String sameSite) { |
There was a problem hiding this comment.
HttpClientExtension now assumes the default expected session cookie Path is '/operaton' (via DEFAULT_SESSION_COOKIE_PATH). Existing session-cookie tests that don't enable path enforcement historically expected Path='/' and will likely fail. Consider keeping the default regex path as '/' (or leaving it unset) and only passing an explicit path in the new path-enforcement tests.
| httpClientExtension.performRequest("http://localhost:" + port + "/operaton/app/tasklist/default"); | ||
|
|
||
| assertThat(httpClientExtension.getCookie("JSESSIONID")) | ||
| .as("JSESSIONID should not exist when operaton.bpm.session.cookie.name is set alias wiring may be broken") |
There was a problem hiding this comment.
The assertion message references operaton.bpm.session.cookie.name, but this test configures the cookie name via server.servlet.session.cookie.name. Updating the message to match the actual configuration will make failures easier to understand.
| .as("JSESSIONID should not exist when operaton.bpm.session.cookie.name is set alias wiring may be broken") | |
| .as("JSESSIONID should not exist when server.servlet.session.cookie.name is set; alias wiring may be broken") |
| if (StringUtils.isNotBlank(cookiePath)) { | ||
| initParams.put("cookiePath", cookiePath); | ||
| } |
There was a problem hiding this comment.
cookiePath is user-configurable and later inserted into Set-Cookie headers. To prevent invalid cookies / header injection, validate/normalize it (e.g., ensure it starts with '/', and reject CR/LF and ';') before putting it into initParams.
…path against invalid chars - path is set automatically (in bean) based of operaton.bpm.webapp.application-path and server.servlet.context-path related to operaton#1632
…t state - Renames SessionCookiePathEnforcementDefaultIT to SessionCookiePathEnforcementEnabledDefaultIT - Renames SessionCookiePathEnforcementCustomIT to SessionCookiePathEnforcementEnabledCustomIT - SessionCookiePathEnforcementDisabledIT to test the actual default behavior (disabled) without explicitly setting the property related to operaton#1632
|
|
||
| @NestedConfigurationProperty | ||
| private FilterProperty filter = new FilterProperty(); | ||
|
|
There was a problem hiding this comment.
A new operaton.bpm.session.* configuration subtree is introduced (SessionProperties), but there is already an existing operaton.bpm.webapp.session-cookie.cookie-name (via SessionCookieProperties) that maps to the same cookieName init parameter. Adding a second way to configure the same behavior is confusing for users and complicates the public configuration surface; consider reusing the existing webapp.session-cookie.cookie-name property (or clearly documenting and differentiating responsibilities if both are required).
| /** | |
| * Deprecated configuration subtree for HTTP session settings. | |
| * <p> | |
| * This property exposes the {@code operaton.bpm.session.*} configuration | |
| * subtree, which configures the same underlying behavior (including the | |
| * {@code cookieName} init parameter) as | |
| * {@code operaton.bpm.webapp.session-cookie.cookie-name}. | |
| * <p> | |
| * New configurations should use | |
| * {@code operaton.bpm.webapp.session-cookie.cookie-name} instead of this | |
| * property. The {@code operaton.bpm.session.*} subtree is kept only for | |
| * backward compatibility and may be removed in a future release. | |
| */ | |
| @Deprecated |
…to csrf cookie related to operaton#1632
…istration test related to operaton#1632
|
|
||
| /** | ||
| * Initializes the filter by reading the configuration parameters. | ||
| * * <p>If the {@value #PARAM_SESSION_COOKIE_NAME} or {@value #PARAM_COOKIE_PATH} |
There was a problem hiding this comment.
There is a stray extra * in the Javadoc ("* *
...") which will render oddly in generated docs. Please remove the redundant asterisk so the Javadoc formatting stays correct.
| * * <p>If the {@value #PARAM_SESSION_COOKIE_NAME} or {@value #PARAM_COOKIE_PATH} | |
| * <p>If the {@value #PARAM_SESSION_COOKIE_NAME} or {@value #PARAM_COOKIE_PATH} |
- Retrieve session cookie name from ServletContext as a reliable fallback - Delegate cookie path formatting to SessionCookiePathFilter.normalizeCookiePath - Remove unused StringUtils - Remove asterisk in SessionCookiePathFilter Javadoc related to operaton#1632
|
@Fejbien Sorry for the long delay. I had finally the rest to look at your PR in detail. I have added some changes and think it is now in a good shape. I will have a final look, but planning to merge for release 2.1. |
|
Thank you for your patience and contributions, @Fejbien ! |
…Operaton session cookie (LF insted of crlf)
related to #1632