KNOX-2387 - SameSite fix for hadoop-jwt cookie (#347)
diff --git a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
index 1fb8aa5..ba52287 100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
@@ -350,26 +350,37 @@
private void addJWTHadoopCookie(String original, JWT token) {
LOGGER.addingJWTCookie(token.toString());
- Cookie c = new Cookie(cookieName, token.toString());
- c.setPath("/");
+ /*
+ * In order to account for google chrome changing default value
+ * of SameSite from None to Lax we need to craft Set-Cookie
+ * header to prevent issues with hadoop-jwt cookie.
+ * NOTE: this would have been easier if javax.servlet.http.Cookie supported
+ * SameSite param. Change this back to Cookie impl. after
+ * SameSite header is supported by javax.servlet.http.Cookie.
+ */
+ final StringBuilder setCookie = new StringBuilder(50);
try {
- String domain = Urls.getDomainName(original, domainSuffix);
+ setCookie.append(cookieName).append('=').append(token.toString());
+ setCookie.append("; Path=/");
+ final String domain = Urls.getDomainName(original, domainSuffix);
if (domain != null) {
- c.setDomain(domain);
+ setCookie.append("; Domain=").append(domain);
}
- c.setHttpOnly(true);
+ setCookie.append("; HttpOnly");
if (secureOnly) {
- c.setSecure(true);
+ setCookie.append("; Secure");
}
if (maxAge != -1) {
- c.setMaxAge(maxAge);
+ setCookie.append("; Max-Age=").append(maxAge);
}
- response.addCookie(c);
+ setCookie.append("; SameSite=None");
+ response.setHeader("Set-Cookie", setCookie.toString());
LOGGER.addedJWTCookie();
- }
- catch(Exception e) {
- LOGGER.unableAddCookieToResponse(e.getMessage(), Arrays.toString(e.getStackTrace()));
- throw new WebApplicationException("Unable to add JWT cookie to response.");
+ } catch (Exception e) {
+ LOGGER.unableAddCookieToResponse(e.getMessage(),
+ Arrays.toString(e.getStackTrace()));
+ throw new WebApplicationException(
+ "Unable to add JWT cookie to response.");
}
}
diff --git a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
index c87aa58..bb46351 100644
--- a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
+++ b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -17,20 +17,37 @@
*/
package org.apache.knox.gateway.service.knoxsso;
+import com.nimbusds.jose.JWSSigner;
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
import org.apache.http.HttpStatus;
import org.apache.knox.gateway.audit.log4j.audit.Log4jAuditor;
import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.GatewayServices;
import org.apache.knox.gateway.services.ServiceType;
import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
+import org.apache.knox.gateway.services.security.token.TokenServiceException;
+import org.apache.knox.gateway.services.security.token.impl.JWT;
+import org.apache.knox.gateway.services.security.token.impl.JWTToken;
import org.apache.knox.gateway.util.RegExUtils;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
-import static org.apache.knox.gateway.services.GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
+import javax.security.auth.Subject;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
import java.lang.reflect.Field;
+import java.net.HttpCookie;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
@@ -46,30 +63,11 @@
import java.util.List;
import java.util.Map;
-import javax.security.auth.Subject;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Response;
-
-import org.apache.knox.gateway.services.GatewayServices;
-import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
-import org.apache.knox.gateway.services.security.token.TokenServiceException;
-import org.apache.knox.gateway.services.security.token.impl.JWT;
-import org.apache.knox.gateway.services.security.token.impl.JWTToken;
-import org.easymock.EasyMock;
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import com.nimbusds.jose.JWSSigner;
-import com.nimbusds.jose.JWSVerifier;
-import com.nimbusds.jose.crypto.RSASSASigner;
-import com.nimbusds.jose.crypto.RSASSAVerifier;
+import static org.apache.knox.gateway.services.GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* Some tests for the Knox SSO service.
@@ -940,6 +938,7 @@
private static class CookieResponseWrapper extends HttpServletResponseWrapper {
private ServletOutputStream outputStream;
+ private Map<String, String> headers = new HashMap<>();
private Map<String, Cookie> cookies = new HashMap<>();
CookieResponseWrapper(HttpServletResponse response, ServletOutputStream outputStream) {
@@ -953,6 +952,28 @@
}
@Override
+ public void setHeader(String name, String value) {
+ headers.put(name, value);
+ /* if we have Set-Cookie header create a cookie for it */
+ if ("Set-Cookie".equalsIgnoreCase(name)) {
+ final List<HttpCookie> clientCookies = HttpCookie.parse(value);
+ clientCookies.forEach(c -> {
+ Cookie cookie = new Cookie(c.getName(), c.getValue());
+ cookie.setSecure(c.getSecure());
+ if (c.getDomain() != null) {
+ cookie.setDomain(c.getDomain());
+ }
+ if (c.getPath() != null) {
+ cookie.setPath(c.getPath());
+ }
+ cookie.setHttpOnly(c.isHttpOnly());
+ cookie.setMaxAge(Math.toIntExact(c.getMaxAge()));
+ this.addCookie(cookie);
+ });
+ }
+ }
+
+ @Override
public void addCookie(Cookie cookie) {
super.addCookie(cookie);
cookies.put(cookie.getName(), cookie);