Commit 7ace3f3c authored by Matija Obreza's avatar Matija Obreza
Browse files

Cookies: Autodetect domain from base.url, use Lax when not on https://

parent 13103a2f
......@@ -35,10 +35,13 @@ import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.genesys2.util.UrlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.filter.OncePerRequestFilter;
import static org.eclipse.jetty.http.HttpCookie.SAME_SITE_LAX_COMMENT;
import static org.eclipse.jetty.http.HttpCookie.SAME_SITE_NONE_COMMENT;;
/**
......@@ -56,12 +59,35 @@ public class AccessTokenInCookieFilter extends OncePerRequestFilter {
private static final String ACCESS_TOKEN_COOKIE_PREFIX = "GENESYS_";
@Value("${host.name}") // we're using the API host name for cookie domain here
private String cookieDomain;
@Value("${base.url}")
private String baseUrl;
@Value("${base.cookie-secure}")
private boolean cookieSecure;
private String cookiePath;
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
try {
LOG.warn("Config base.url: {}", baseUrl);
var siteUrl = UrlUtils.toCleanUrl(baseUrl);
LOG.warn("Site URL: {}", siteUrl);
cookiePath = siteUrl.getPath() + "/api";
if (StringUtils.equalsIgnoreCase("https", siteUrl.getProtocol())) {
LOG.warn("Cookies: Forcing secure cookies since we are on https");
cookieSecure = true;
} else {
LOG.warn("Cookies: Forcing insecure cookies since we are on http");
cookieSecure = false;
}
} catch (MalformedURLException e) {
LOG.warn("Site URL: {}", baseUrl, e);
throw new ServletException(e);
}
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
......@@ -111,17 +137,25 @@ public class AccessTokenInCookieFilter extends OncePerRequestFilter {
if (!tokenCookie.isPresent() || !tokenCookie.get().getValue().equals(accessToken)) {
Cookie cookie = new Cookie(tokenCookieName, accessToken);
cookie.setComment(SAME_SITE_NONE_COMMENT);
if (cookieSecure) {
cookie.setComment(SAME_SITE_NONE_COMMENT);
} else {
cookie.setComment(SAME_SITE_LAX_COMMENT);
}
cookie.setHttpOnly(true);
cookie.setSecure(cookieSecure);
// if (StringUtils.isNotBlank(cookieDomain)) {
// cookie.setDomain(sourceUrl.getHost());
// }
// Only set cookie for /api
cookie.setPath("/api");
cookie.setPath(cookiePath);
LOG.info("Registering API cookie '{}' on {}{}", cookie.getName(), cookie.getDomain(), cookie.getPath());
response.addCookie(cookie);
try {
response.addCookie(cookie);
} catch (Throwable e) {
LOG.error("Invalid cookie: {}. Value={}", e.getMessage(), cookie.getValue());
}
}
}
......
......@@ -15,7 +15,12 @@
*/
package org.genesys2.spring.config;
import org.apache.commons.lang3.StringUtils;
import org.genesys2.spring.WhoMakesTheJSessionId;
import org.genesys2.util.UrlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -29,7 +34,8 @@ import com.hazelcast.core.HazelcastInstance;
@Configuration
@EnableHazelcastHttpSession
public class HazelcastHttpSessionConfig {
public class HazelcastHttpSessionConfig implements InitializingBean {
public static final Logger LOG = LoggerFactory.getLogger(HazelcastHttpSessionConfig.class);
/** Hazelcast instance name. */
@Value("${hazelcast.instanceName}")
......@@ -38,17 +44,46 @@ public class HazelcastHttpSessionConfig {
@Value("${base.cookie-name}")
private String cookieName;
@Value("${base.cookie-same-site}")
@Value("${base.url}")
private String baseUrl;
@Value("${base.cookie-same-site:true}")
private String sameSite;
@Value("${base.cookie-domain}")
@Value("${base.cookie-domain:localhost}")
private String cookieDomain;
@Value("${base.cookie-secure}")
private String cookieSecure;
@Value("${base.cookie-path:/}")
private String cookiePath;
private boolean cookieSecure;
@Value("${base.cookie-http-only}")
private String cookieHttpOnly;
@Value("${base.cookie-http-only:true}")
private boolean cookieHttpOnly;
@Override
public void afterPropertiesSet() throws Exception {
LOG.warn("Config base.url: {}", baseUrl);
var siteUrl = UrlUtils.toCleanUrl(baseUrl);
LOG.warn("Site URL: {}", siteUrl);
if (StringUtils.equalsIgnoreCase("https", siteUrl.getProtocol())) {
LOG.warn("Cookies: Forcing secure cookies since we are on https");
cookieSecure = true;
} else {
LOG.warn("Cookies: Forcing insecure cookies since we are on http");
cookieSecure = false;
}
if (siteUrl.getPath().length() > 0) {
LOG.warn("Cookies: Forcing Cookie-Path: {}", siteUrl.getPath());
cookiePath = siteUrl.getPath() + "/";
} else {
LOG.warn("Cookies: Using Cookie-Path: {}", cookiePath);
}
if (! siteUrl.getHost().toLowerCase().endsWith(cookieDomain.toLowerCase())) {
LOG.warn("Cookies: Forcing cookie Domain: {} (was {})", siteUrl.getHost(), cookieDomain);
cookieDomain = siteUrl.getHost();
}
}
@Bean
@SpringSessionHazelcastInstance
......@@ -66,8 +101,9 @@ public class HazelcastHttpSessionConfig {
final var serializer = new DefaultCookieSerializer();
serializer.setCookieName(cookieName);
serializer.setDomainName(cookieDomain);
serializer.setUseSecureCookie(Boolean.parseBoolean(cookieSecure));
serializer.setUseHttpOnlyCookie(Boolean.parseBoolean(cookieHttpOnly));
serializer.setCookiePath(cookiePath);
serializer.setUseSecureCookie(cookieSecure);
serializer.setUseHttpOnlyCookie(cookieHttpOnly);
serializer.setSameSite(sameSite);
return serializer;
}
......
/*
* Copyright 2022 Global Crop Diversity Trust
*
* Licensed 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.genesys2.util;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.lang3.StringUtils;
/**
* UrlUtils.
*/
public abstract class UrlUtils {
/**
* Create a clean URL from string:
*
* - port == -1, if port is specified and is defaultPort
* - path without trailing /
* - no query if blank
*
* @param url
* @return
* @throws MalformedURLException
*/
public static URL toCleanUrl(String url) throws MalformedURLException {
var u = new URL(url);
var port = u.getPort() == u.getDefaultPort() ? -1 : u.getPort();
var path = StringUtils.endsWith(u.getPath(), "/") ? StringUtils.substringBeforeLast(u.getPath(), "/") : u.getPath();
var queryPart = u.getQuery() == null || u.getQuery().length() == 0 ? "" : "?" + u.getQuery();
u = new URL(u.getProtocol(), u.getHost(), port, path + queryPart);
return u;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment