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);