This is an automated email from the ASF dual-hosted git repository.
smolnar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new 5a069b970 KNOX-3148: Make SameSite configurable for pac4j session
cookies (#1042)
5a069b970 is described below
commit 5a069b97012eadfc42f493b938cc24b3e9da6055
Author: hanicz <[email protected]>
AuthorDate: Mon May 12 10:10:08 2025 +0200
KNOX-3148: Make SameSite configurable for pac4j session cookies (#1042)
---
.../pac4j/filter/Pac4jDispatcherFilter.java | 5 +
.../gateway/pac4j/session/KnoxSessionStore.java | 30 ++--
.../gateway/pac4j/MockHttpServletResponse.java | 13 +-
.../knox/gateway/pac4j/Pac4jProviderTest.java | 173 ++++++++++-----------
.../pac4j/filter/Pac4jDispatcherFilterTest.java | 45 +++++-
.../pac4j/session/KnoxSessionStoreTest.java | 7 +-
.../gateway/service/knoxsso/WebSSOResource.java | 18 +--
.../apache/knox/gateway/util/SetCookieHeader.java | 134 ++++++++++++++++
.../knox/gateway/util/SetCookieHeaderTest.java | 110 +++++++++++++
9 files changed, 416 insertions(+), 119 deletions(-)
diff --git
a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/filter/Pac4jDispatcherFilter.java
b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/filter/Pac4jDispatcherFilter.java
index d0b7521d9..0fc6241f0 100644
---
a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/filter/Pac4jDispatcherFilter.java
+++
b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/filter/Pac4jDispatcherFilter.java
@@ -130,6 +130,9 @@ public class Pac4jDispatcherFilter implements Filter,
SessionInvalidator {
/* default value is same is KNOXSSO token ttl default */
private static final String PAC4J_COOKIE_MAX_AGE_DEFAULT = "-1";
+ public static final String PAC4J_COOKIE_SAMESITE = "pac4j.cookie.samesite";
+ private static final String PAC4J_COOKIE_SAMESITE_DEFAULT = "Strict";
+
private static final String PAC4J_CSRF_TOKEN = "pac4jCsrfToken";
private static boolean SSL_ENABLED = true;
@@ -229,6 +232,8 @@ public class Pac4jDispatcherFilter implements Filter,
SessionInvalidator {
setSessionStoreConfig(filterConfig,
PAC4J_SESSION_STORE_EXCLUDE_CUSTOM_ATTRIBUTES,
PAC4J_SESSION_STORE_EXCLUDE_CUSTOM_ATTRIBUTES_DEFAULT);
/* add cookie expiry */
setSessionStoreConfig(filterConfig, PAC4J_COOKIE_MAX_AGE,
PAC4J_COOKIE_MAX_AGE_DEFAULT);
+ /* add cookie samesite */
+ setSessionStoreConfig(filterConfig, PAC4J_COOKIE_SAMESITE,
PAC4J_COOKIE_SAMESITE_DEFAULT);
//decorating client configuration (if needed)
PAC4J_CLIENT_CONFIGURATION_DECORATOR.decorateClients(clients,
properties);
}
diff --git
a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/session/KnoxSessionStore.java
b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/session/KnoxSessionStore.java
index 94007954d..0ac14ac68 100644
---
a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/session/KnoxSessionStore.java
+++
b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/session/KnoxSessionStore.java
@@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter;
import org.apache.knox.gateway.services.security.CryptoService;
import org.apache.knox.gateway.services.security.EncryptionResult;
+import org.apache.knox.gateway.util.SetCookieHeader;
import org.apache.knox.gateway.util.Urls;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -48,6 +49,7 @@ import java.util.Optional;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
+import static
org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_COOKIE_SAMESITE;
import static
org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_EXCLUDE_CUSTOM_ATTRIBUTES;
import static
org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_EXCLUDE_CUSTOM_ATTRIBUTES_DEFAULT;
import static
org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_EXCLUDE_GROUPS;
@@ -161,18 +163,17 @@ public class KnoxSessionStore<C extends WebContext>
implements SessionStore<C> {
@Override
public void set(WebContext context, String key, Object value) {
Object profile = value;
- Cookie cookie;
+ SetCookieHeader setCookieHeader;
if (value == null) {
- cookie = new Cookie(PAC4J_SESSION_PREFIX + key, null);
+ setCookieHeader = new SetCookieHeader(PAC4J_SESSION_PREFIX + key,
null);
} else {
if (key.contentEquals(Pac4jConstants.USER_PROFILES)) {
/* trim the profile object */
profile = clearUserProfile(value);
}
logger.debug("Save in session: {} = {}", key, profile);
- cookie = new Cookie(PAC4J_SESSION_PREFIX + key,
- compressEncryptBase64(profile));
+ setCookieHeader = new SetCookieHeader(PAC4J_SESSION_PREFIX + key,
compressEncryptBase64(profile));
}
try {
String domain = Urls
@@ -180,12 +181,14 @@ public class KnoxSessionStore<C extends WebContext>
implements SessionStore<C> {
if (domain == null) {
domain = context.getServerName();
}
- cookie.setDomain(domain);
+ setCookieHeader.setDomain(domain);
} catch (final Exception e) {
throw new TechnicalException(e);
}
- cookie.setHttpOnly(true);
- cookie.setSecure(ContextHelper.isHttpsOrSecure(context));
+ setCookieHeader.setSecure(true);
+ if(ContextHelper.isHttpsOrSecure(context)) {
+ setCookieHeader.setHttpOnly(true);
+ }
/*
* set the correct path for setting pac4j profile cookie.
@@ -198,16 +201,19 @@ public class KnoxSessionStore<C extends WebContext>
implements SessionStore<C> {
final String[] parts = ((JEEContext)
context).getNativeRequest().getRequestURI()
.split(
"websso"+ Pac4jDispatcherFilter.URL_PATH_SEPARATOR +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER);
-
- cookie.setPath(parts[0]);
-
+ setCookieHeader.setPath(parts[0]);
}
/* Set cookie max age */
if(sessionStoreConfigs != null &&
sessionStoreConfigs.containsKey(PAC4J_COOKIE_MAX_AGE)) {
-
cookie.setMaxAge(Integer.parseInt(sessionStoreConfigs.get(PAC4J_COOKIE_MAX_AGE)));
+
setCookieHeader.setMaxAge(Integer.parseInt(sessionStoreConfigs.get(PAC4J_COOKIE_MAX_AGE)));
}
- context.addResponseCookie(cookie);
+
+ if(sessionStoreConfigs != null &&
sessionStoreConfigs.containsKey(PAC4J_COOKIE_SAMESITE)) {
+
setCookieHeader.setSameSite(sessionStoreConfigs.get(PAC4J_COOKIE_SAMESITE));
+ }
+
+ ((JEEContext) context).getNativeResponse().addHeader("Set-Cookie",
setCookieHeader.toString());
}
/**
diff --git
a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/MockHttpServletResponse.java
b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/MockHttpServletResponse.java
index 688465d55..2e7dc6ff0 100644
---
a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/MockHttpServletResponse.java
+++
b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/MockHttpServletResponse.java
@@ -25,15 +25,16 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MockHttpServletResponse extends HttpServletResponseWrapper {
- private List<Cookie> cookies = new ArrayList<>();
+ private final List<Cookie> cookies = new ArrayList<>();
private int status;
- private Map<String, String> headers = new HashMap<>();
+ private final Map<String, List<String>> headers = new HashMap<>();
public MockHttpServletResponse() {
super(setupMockResponse());
@@ -47,15 +48,17 @@ public class MockHttpServletResponse extends
HttpServletResponseWrapper {
@Override
public void setHeader(String name, String value) {
- headers.put(name, value);
+ headers.put(name, new ArrayList<>(Collections.singletonList(value)));
}
@Override
public void addHeader(String name, String value) {
- headers.put(name, value);
+ List<String> values = headers.getOrDefault(name, new ArrayList<>());
+ values.add(value);
+ headers.put(name, values);
}
- public Map<String, String> getHeaders() {
+ public Map<String, List<String>> getHeaders() {
return headers;
}
diff --git
a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/Pac4jProviderTest.java
b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/Pac4jProviderTest.java
index de4b7a5c9..c4bc8d817 100644
---
a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/Pac4jProviderTest.java
+++
b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/Pac4jProviderTest.java
@@ -24,8 +24,8 @@ import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter;
import org.apache.knox.gateway.pac4j.filter.Pac4jIdentityAdapter;
import org.apache.knox.gateway.pac4j.session.KnoxSessionStore;
-import org.apache.knox.gateway.services.ServiceType;
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.impl.DefaultCryptoService;
import org.easymock.EasyMock;
@@ -38,15 +38,12 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import java.util.Enumeration;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.Optional;
import java.util.Properties;
-import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
/**
* This class simulates a full authentication process using pac4j.
@@ -113,18 +110,21 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
// it should be a redirection to the idp topology
assertEquals(302, response.getStatus());
- assertEquals(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS,
response.getHeaders().get("Location"));
- // we should have one cookie for the saved requested url
+ assertEquals(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS,
response.getHeaders().get("Location").get(0));
+
List<Cookie> cookies = response.getCookies();
- assertEquals(3, cookies.size());
- final Cookie requestedUrlCookie = cookies.stream()
- .filter(c -> (KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL).equals(c.getName()))
- .collect(Collectors.toList()).get(0);
- assertEquals(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL, requestedUrlCookie.getName());
+ assertEquals(1, cookies.size());
+ Optional<String> requestedUrlSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL))
+ .findFirst();
+ assertTrue(requestedUrlSetCookie.isPresent());
+
+ assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.CSRF_TOKEN)).count());
// step 2: send credentials to the callback url (callback from the
identity provider)
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{requestedUrlCookie});
+ request.setCookies(new
Cookie[]{this.setCookieParser(requestedUrlSetCookie.get())});
request.setRequestURL(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS);
request.addParameter(Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER,
"true");
request.addParameter(Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER,
CLIENT_CLASS);
@@ -135,20 +135,20 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
// it should be a redirection to the original url
assertEquals(302, response.getStatus());
- assertEquals(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL, response.getHeaders().get("Location"));
- // we should have 2 cookies among with the user profile
- cookies = response.getCookies();
- Map<String, String> mapCookies = new HashMap<>();
- assertEquals(2, cookies.size());
- for (final Cookie cookie : cookies) {
- mapCookies.put(cookie.getName(), cookie.getValue());
- }
- assertNotNull(mapCookies.get(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES));
- assertNull(mapCookies.get(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL));
+ assertEquals(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL, response.getHeaders().get("Location").get(0));
+
+ Optional<String> userProfilesSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES))
+ .findFirst();
+ requestedUrlSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL + "=null;"))
+ .findFirst();
+ assertTrue(userProfilesSetCookie.isPresent());
+ assertTrue(requestedUrlSetCookie.isPresent());
// step 3: turn pac4j identity into KnoxSSO identity
request = new MockHttpServletRequest();
- request.setCookies(cookies.toArray(new Cookie[0]));
+ request.setCookies(new
Cookie[]{this.setCookieParser(requestedUrlSetCookie.get()),
this.setCookieParser(userProfilesSetCookie.get())});
request.setRequestURL(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL);
request.setServerName(LOCALHOST);
response = new MockHttpServletResponse();
@@ -156,14 +156,9 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
assertEquals(0, response.getStatus());
adapter.doFilter(request, response, filterChain);
- cookies = response.getCookies();
- assertEquals(3, cookies.size());
- final Cookie userProfileCookie = cookies.stream()
- .filter(c -> (KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES).equals(c.getName()))
- .collect(Collectors.toList()).get(0);
- // the user profile has been cleaned
- assertEquals(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES, userProfileCookie.getName());
- assertNull(userProfileCookie.getValue());
+
+ assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES + "=null;")).count());
assertEquals(USERNAME, adapter.getTestIdentifier());
}
@@ -218,18 +213,21 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
// it should be a redirection to the idp topology
assertEquals(302, response.getStatus());
- assertEquals(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS,
response.getHeaders().get("Location"));
- // we should have one cookie for the saved requested url
+ assertEquals(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS,
response.getHeaders().get("Location").get(0));
+
List<Cookie> cookies = response.getCookies();
- assertEquals(3, cookies.size());
- final Cookie requestedUrlCookie = cookies.stream()
- .filter(c -> (KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL).equals(c.getName()))
- .collect(Collectors.toList()).get(0);
- assertEquals(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL, requestedUrlCookie.getName());
+ assertEquals(1, cookies.size());
+ Optional<String> requestedUrlSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL))
+ .findFirst();
+ assertTrue(requestedUrlSetCookie.isPresent());
+
+ assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.CSRF_TOKEN)).count());
// step 2: send credentials to the callback url (callback from the
identity provider)
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{requestedUrlCookie});
+ request.setCookies(new
Cookie[]{this.setCookieParser(requestedUrlSetCookie.get())});
request.setRequestURL(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS);
request.addParameter(Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER,
"true");
request.addParameter(Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER,
CLIENT_CLASS);
@@ -240,20 +238,20 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
// it should be a redirection to the original url
assertEquals(302, response.getStatus());
- assertEquals(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL, response.getHeaders().get("Location"));
- // we should have 2 cookies among with the user profile
- cookies = response.getCookies();
- Map<String, String> mapCookies = new HashMap<>();
- assertEquals(2, cookies.size());
- for (final Cookie cookie : cookies) {
- mapCookies.put(cookie.getName(), cookie.getValue());
- }
- assertNotNull(mapCookies.get(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES));
- assertNull(mapCookies.get(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL));
+ assertEquals(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL, response.getHeaders().get("Location").get(0));
+
+ Optional<String> userProfilesSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES))
+ .findFirst();
+ requestedUrlSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL + "=null;"))
+ .findFirst();
+ assertTrue(userProfilesSetCookie.isPresent());
+ assertTrue(requestedUrlSetCookie.isPresent());
// step 3: turn pac4j identity into KnoxSSO identity
request = new MockHttpServletRequest();
- request.setCookies(cookies.toArray(new Cookie[0]));
+ request.setCookies(new
Cookie[]{this.setCookieParser(requestedUrlSetCookie.get()),
this.setCookieParser(userProfilesSetCookie.get())});
request.setRequestURL(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL);
request.setServerName(LOCALHOST);
response = new MockHttpServletResponse();
@@ -261,16 +259,12 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
assertEquals(0, response.getStatus());
adapter.doFilter(request, response, filterChain);
- cookies = response.getCookies();
- assertEquals(3, cookies.size());
- final Cookie userProfileCookie = cookies.stream()
- .filter(c -> (KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES).equals(c.getName()))
- .collect(Collectors.toList()).get(0);
- // the user profile has been cleaned
- assertEquals(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES, userProfileCookie.getName());
- assertNull(userProfileCookie.getValue());
+
+ assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES + "=null;")).count());
assertEquals(USERNAME, adapter.getTestIdentifier());
}
+
@Test
public void testInvalidIdAttribute() throws Exception {
final AliasService aliasService =
EasyMock.createNiceMock(AliasService.class);
@@ -322,18 +316,21 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
// it should be a redirection to the idp topology
assertEquals(302, response.getStatus());
- assertEquals(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS,
response.getHeaders().get("Location"));
- // we should have one cookie for the saved requested url
+ assertEquals(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS,
response.getHeaders().get("Location").get(0));
+
List<Cookie> cookies = response.getCookies();
- assertEquals(3, cookies.size());
- final Cookie requestedUrlCookie = cookies.stream()
- .filter(c -> (KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL).equals(c.getName()))
- .collect(Collectors.toList()).get(0);
- assertEquals(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL, requestedUrlCookie.getName());
+ assertEquals(1, cookies.size());
+ Optional<String> requestedUrlSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL))
+ .findFirst();
+ assertTrue(requestedUrlSetCookie.isPresent());
+
+ assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.CSRF_TOKEN)).count());
// step 2: send credentials to the callback url (callback from the
identity provider)
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{requestedUrlCookie});
+ request.setCookies(new
Cookie[]{this.setCookieParser(requestedUrlSetCookie.get())});
request.setRequestURL(PAC4J_CALLBACK_URL + "?" +
Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER + "=true&" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" +
Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER + "=" + CLIENT_CLASS);
request.addParameter(Pac4jDispatcherFilter.PAC4J_CALLBACK_PARAMETER,
"true");
request.addParameter(Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER,
CLIENT_CLASS);
@@ -344,20 +341,20 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
// it should be a redirection to the original url
assertEquals(302, response.getStatus());
- assertEquals(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL, response.getHeaders().get("Location"));
- // we should have 2 cookies among with the user profile
- cookies = response.getCookies();
- Map<String, String> mapCookies = new HashMap<>();
- assertEquals(2, cookies.size());
- for (final Cookie cookie : cookies) {
- mapCookies.put(cookie.getName(), cookie.getValue());
- }
- assertNotNull(mapCookies.get(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES));
- assertNull(mapCookies.get(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL));
+ assertEquals(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL, response.getHeaders().get("Location").get(0));
+
+ Optional<String> userProfilesSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES))
+ .findFirst();
+ requestedUrlSetCookie =
response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.REQUESTED_URL + "=null;"))
+ .findFirst();
+ assertTrue(userProfilesSetCookie.isPresent());
+ assertTrue(requestedUrlSetCookie.isPresent());
// step 3: turn pac4j identity into KnoxSSO identity
request = new MockHttpServletRequest();
- request.setCookies(cookies.toArray(new Cookie[0]));
+ request.setCookies(new
Cookie[]{this.setCookieParser(requestedUrlSetCookie.get()),
this.setCookieParser(userProfilesSetCookie.get())});
request.setRequestURL(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" +
HADOOP_SERVICE_URL);
request.setServerName(LOCALHOST);
response = new MockHttpServletResponse();
@@ -365,14 +362,9 @@ public class Pac4jProviderTest {
dispatcher.doFilter(request, response, filterChain);
assertEquals(0, response.getStatus());
adapter.doFilter(request, response, filterChain);
- cookies = response.getCookies();
- assertEquals(3, cookies.size());
- final Cookie userProfileCookie = cookies.stream()
- .filter(c -> (KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES).equals(c.getName()))
- .collect(Collectors.toList()).get(0);
- // the user profile has been cleaned
- assertEquals(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES, userProfileCookie.getName());
- assertNull(userProfileCookie.getValue());
+
+ assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
+ .filter(c ->
c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX +
Pac4jConstants.USER_PROFILES + "=null;")).count());
assertEquals(USERNAME, adapter.getTestIdentifier());
}
@@ -409,6 +401,13 @@ public class Pac4jProviderTest {
EasyMock.verify(aliasService);
}
+ private Cookie setCookieParser(String setCookie) {
+ String[] cookieParts = setCookie.split(";");
+ String[] nameValuePairs = cookieParts[0].trim().split("=", 2);
+
+ return new Cookie(nameValuePairs[0].trim(), nameValuePairs[1].trim());
+ }
+
private class FilterConfigStub implements FilterConfig {
private ServletContext context;
private Properties properties = new Properties();
diff --git
a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/filter/Pac4jDispatcherFilterTest.java
b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/filter/Pac4jDispatcherFilterTest.java
index c93d969be..b965cf827 100644
---
a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/filter/Pac4jDispatcherFilterTest.java
+++
b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/filter/Pac4jDispatcherFilterTest.java
@@ -104,14 +104,14 @@ public class Pac4jDispatcherFilterTest {
EasyMock.expect(mocks.context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(mocks.gatewayConfig).anyTimes();
}
- private void verifyCookiemaxAge(FilterConfig filterConfig, String
expectedCookieMaxAge) throws Exception {
+ private void verifyCookieConfig(FilterConfig filterConfig, String key,
String expected) throws Exception {
Pac4jDispatcherFilter filter = new Pac4jDispatcherFilter();
filter.init(filterConfig);
java.lang.reflect.Field configField =
filter.getClass().getDeclaredField("sessionStoreConfigs");
configField.setAccessible(true);
Map<String, String> sessionStoreConfigs = (Map<String, String>)
configField.get(filter);
- Assert.assertEquals(expectedCookieMaxAge,
sessionStoreConfigs.get(Pac4jDispatcherFilter.PAC4J_COOKIE_MAX_AGE));
+ Assert.assertEquals(expected, sessionStoreConfigs.get(key));
}
@@ -129,7 +129,7 @@ public class Pac4jDispatcherFilterTest {
EasyMock.replay(mocks.context, mocks.services, mocks.cryptoService,
mocks.aliasService, mocks.keystoreService, mocks.masterService,
mocks.filterConfig, mocks.gatewayConfig);
- verifyCookiemaxAge(mocks.filterConfig, expectedCookieMaxAge);
+ verifyCookieConfig(mocks.filterConfig,
Pac4jDispatcherFilter.PAC4J_COOKIE_MAX_AGE, expectedCookieMaxAge);
// Verify all mock interactions
EasyMock.verify(mocks.context, mocks.services, mocks.cryptoService,
mocks.aliasService, mocks.keystoreService, mocks.masterService,
mocks.filterConfig, mocks.gatewayConfig);
@@ -152,7 +152,44 @@ public class Pac4jDispatcherFilterTest {
EasyMock.replay(mocks.context, mocks.services, mocks.cryptoService,
mocks.aliasService, mocks.keystoreService, mocks.masterService,
mocks.filterConfig, mocks.gatewayConfig);
- verifyCookiemaxAge(mocks.filterConfig, expectedCookieMaxAge);
+ verifyCookieConfig(mocks.filterConfig,
Pac4jDispatcherFilter.PAC4J_COOKIE_MAX_AGE, expectedCookieMaxAge);
+
+ // Verify all mock interactions
+ EasyMock.verify(mocks.context, mocks.services, mocks.cryptoService,
mocks.aliasService, mocks.keystoreService, mocks.masterService,
mocks.filterConfig, mocks.gatewayConfig);
+ }
+
+ @Test
+ public void testDefaultCookieSameSite() throws Exception {
+ final String expectedSameSite = "Strict";
+ List<String> params = new ArrayList<>();
+ params.add(Pac4jDispatcherFilter.PAC4J_CALLBACK_URL);
+ params.add("clientName");
+ params.add(SAML_KEYSTORE_PATH);
+ params.add(SAML_IDENTITY_PROVIDER_METADATA_PATH);
+
+ TestMocks mocks = createMocks();
+ setupCommonExpectations(mocks, Collections.EMPTY_LIST);
+
+ EasyMock.replay(mocks.context, mocks.services, mocks.cryptoService,
mocks.aliasService, mocks.keystoreService, mocks.masterService,
mocks.filterConfig, mocks.gatewayConfig);
+
+ verifyCookieConfig(mocks.filterConfig,
Pac4jDispatcherFilter.PAC4J_COOKIE_SAMESITE, expectedSameSite);
+
+ // Verify all mock interactions
+ EasyMock.verify(mocks.context, mocks.services, mocks.cryptoService,
mocks.aliasService, mocks.keystoreService, mocks.masterService,
mocks.filterConfig, mocks.gatewayConfig);
+ }
+
+ @Test
+ public void testCustomCookieSameSite() throws Exception {
+ final String expectedSameSite = "None";
+ List<String> additionalParams = new ArrayList<>();
+ additionalParams.add(Pac4jDispatcherFilter.PAC4J_COOKIE_SAMESITE);
+ TestMocks mocks = createMocks();
+ setupCommonExpectations(mocks, additionalParams);
+
EasyMock.expect(mocks.filterConfig.getInitParameter(Pac4jDispatcherFilter.PAC4J_COOKIE_SAMESITE)).andReturn(expectedSameSite).anyTimes();
+
+ EasyMock.replay(mocks.context, mocks.services, mocks.cryptoService,
mocks.aliasService, mocks.keystoreService, mocks.masterService,
mocks.filterConfig, mocks.gatewayConfig);
+
+ verifyCookieConfig(mocks.filterConfig,
Pac4jDispatcherFilter.PAC4J_COOKIE_SAMESITE, expectedSameSite);
// Verify all mock interactions
EasyMock.verify(mocks.context, mocks.services, mocks.cryptoService,
mocks.aliasService, mocks.keystoreService, mocks.masterService,
mocks.filterConfig, mocks.gatewayConfig);
diff --git
a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/session/KnoxSessionStoreTest.java
b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/session/KnoxSessionStoreTest.java
index 586a46a88..32cb5e405 100644
---
a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/session/KnoxSessionStoreTest.java
+++
b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/session/KnoxSessionStoreTest.java
@@ -24,11 +24,12 @@ import org.easymock.Capture;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
-import org.pac4j.core.context.WebContext;
+import org.pac4j.core.context.JEEContext;
import org.pac4j.core.profile.CommonProfile;
import org.pac4j.core.util.Pac4jConstants;
import org.pac4j.saml.profile.SAML2Profile;
+import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -68,9 +69,11 @@ public class KnoxSessionStoreTest {
final Map<String, String> sessionStoreConfigs = new HashMap();
final Capture<org.pac4j.core.context.Cookie> captureCookieValue =
EasyMock.newCapture();
- final WebContext mockContext = EasyMock.createNiceMock(WebContext.class);
+ final JEEContext mockContext = EasyMock.createNiceMock(JEEContext.class);
+ final HttpServletResponse response =
EasyMock.createNiceMock(HttpServletResponse.class);
EasyMock.expect(mockContext.getFullRequestURL()).andReturn("https://local.com/gateway/knoxsso/").anyTimes();
mockContext.addResponseCookie(EasyMock.capture(captureCookieValue));
+
EasyMock.expect(mockContext.getNativeResponse()).andReturn(response).anyTimes();
EasyMock.replay(mockContext);
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 641e15a7d..6856120a2 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
@@ -65,6 +65,7 @@ import
org.apache.knox.gateway.services.security.token.impl.JWT;
import org.apache.knox.gateway.session.control.ConcurrentSessionVerifier;
import org.apache.knox.gateway.util.CookieUtils;
import org.apache.knox.gateway.util.RegExUtils;
+import org.apache.knox.gateway.util.SetCookieHeader;
import org.apache.knox.gateway.util.Tokens;
import org.apache.knox.gateway.util.Urls;
import org.apache.knox.gateway.util.WhitelistUtils;
@@ -409,23 +410,22 @@ public class WebSSOResource {
* 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 {
- setCookie.append(cookieName).append('=').append(token.toString());
- setCookie.append("; Path=/");
+ SetCookieHeader setCookieHeader = new SetCookieHeader(cookieName,
token.toString());
+ setCookieHeader.setPath("/");
final String domain = Urls.getDomainName(original, domainSuffix);
if (domain != null) {
- setCookie.append("; Domain=").append(domain);
+ setCookieHeader.setDomain(domain);
}
- setCookie.append("; HttpOnly");
+ setCookieHeader.setHttpOnly(true);
if (secureOnly) {
- setCookie.append("; Secure");
+ setCookieHeader.setSecure(true);
}
if (maxAge != -1) {
- setCookie.append("; Max-Age=").append(maxAge);
+ setCookieHeader.setMaxAge(maxAge);
}
- setCookie.append("; SameSite=").append(this.sameSiteValue);
- response.setHeader("Set-Cookie", setCookie.toString());
+ setCookieHeader.setSameSite(sameSiteValue);
+ response.setHeader("Set-Cookie", setCookieHeader.toString());
LOGGER.addedJWTCookie(logSafeToken);
} catch (Exception e) {
LOGGER.unableAddCookieToResponse(e.getMessage(),
diff --git
a/gateway-util-common/src/main/java/org/apache/knox/gateway/util/SetCookieHeader.java
b/gateway-util-common/src/main/java/org/apache/knox/gateway/util/SetCookieHeader.java
new file mode 100644
index 000000000..2a56d9048
--- /dev/null
+++
b/gateway-util-common/src/main/java/org/apache/knox/gateway/util/SetCookieHeader.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.knox.gateway.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class SetCookieHeader {
+ private final String name;
+ private final String value;
+ private final Map<String, String> attributes = new HashMap<>();
+
+ private String path;
+ private String domain;
+ private int maxAge = -1;
+ private boolean httpOnly;
+ private boolean secure;
+ private String sameSite;
+
+ public SetCookieHeader(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public int getMaxAge() {
+ return maxAge;
+ }
+
+ public boolean isHttpOnly() {
+ return httpOnly;
+ }
+
+ public boolean isSecure() {
+ return secure;
+ }
+
+ public String getSameSite() {
+ return sameSite;
+ }
+
+ public String getAttribute(String name) {
+ return attributes.get(name);
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public void setDomain(String domain) {
+ this.domain = domain;
+ }
+
+ public void setMaxAge(int maxAge) {
+ this.maxAge = maxAge;
+ }
+
+ public void setHttpOnly(boolean httpOnly) {
+ this.httpOnly = httpOnly;
+ }
+
+ public void setSecure(boolean secure) {
+ this.secure = secure;
+ }
+
+ public void setSameSite(String sameSite) {
+ this.sameSite = sameSite;
+ }
+
+ public void setAttribute(String name, String value) {
+ attributes.put(name, value);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(name);
+ sb.append('=').append(value);
+
+ if(path != null) {
+ sb.append("; Path=").append(path);
+ }
+ if(domain != null) {
+ sb.append("; Domain=").append(domain);
+ }
+ if(maxAge != -1) {
+ sb.append("; Max-Age=").append(maxAge);
+ }
+ if(httpOnly) {
+ sb.append("; HttpOnly");
+ }
+ if(secure) {
+ sb.append("; Secure");
+ }
+ if(sameSite != null) {
+ sb.append("; SameSite=").append(sameSite);
+ }
+
+ for(Map.Entry<String, String> entry : attributes.entrySet()) {
+ sb.append(";
").append(entry.getKey()).append('=').append(entry.getValue());
+ }
+
+ return sb.toString();
+ }
+}
diff --git
a/gateway-util-common/src/test/java/org/apache/knox/gateway/util/SetCookieHeaderTest.java
b/gateway-util-common/src/test/java/org/apache/knox/gateway/util/SetCookieHeaderTest.java
new file mode 100644
index 000000000..58db606b4
--- /dev/null
+++
b/gateway-util-common/src/test/java/org/apache/knox/gateway/util/SetCookieHeaderTest.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.knox.gateway.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class SetCookieHeaderTest {
+
+ @Test
+ public void testBasicSetCookie() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ assertEquals("name=value", setCookieHeader.toString());
+ }
+
+ @Test
+ public void testEmptyValueSetCookie() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", null);
+ assertEquals("name=null", setCookieHeader.toString());
+ }
+
+ @Test
+ public void testSetCookieWithPath() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ setCookieHeader.setPath("/");
+ assertEquals("name=value; Path=/", setCookieHeader.toString());
+ }
+
+ @Test
+ public void testSetCookieWithDomain() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ setCookieHeader.setDomain("example.com");
+ assertEquals("name=value; Domain=example.com",
setCookieHeader.toString());
+ }
+
+ @Test
+ public void testSetCookieWithMaxAge() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ setCookieHeader.setMaxAge(231);
+ assertEquals("name=value; Max-Age=231", setCookieHeader.toString());
+ }
+
+ @Test
+ public void testSetCookieWithHttpOnly() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ setCookieHeader.setHttpOnly(true);
+ assertEquals("name=value; HttpOnly", setCookieHeader.toString());
+ }
+
+ @Test
+ public void testSetCookieWithSecure() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ setCookieHeader.setSecure(true);
+ assertEquals("name=value; Secure", setCookieHeader.toString());
+ }
+
+ @Test
+ public void testSetCookieWithSameSite() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ setCookieHeader.setSameSite("Strict");
+ assertEquals("name=value; SameSite=Strict",
setCookieHeader.toString());
+ }
+
+ @Test
+ public void testSetCookieWithAdditionalAttribute() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ setCookieHeader.setAttribute("Attribute", "value");
+ assertEquals("name=value; Attribute=value",
setCookieHeader.toString());
+ }
+
+ @Test
+ public void testSetCookieWithAll() {
+ SetCookieHeader setCookieHeader = new SetCookieHeader("name", "value");
+ setCookieHeader.setDomain("example.com");
+ setCookieHeader.setMaxAge(231);
+ setCookieHeader.setHttpOnly(true);
+ setCookieHeader.setSecure(true);
+ setCookieHeader.setPath("/path");
+ setCookieHeader.setSameSite("Lax");
+ setCookieHeader.setAttribute("Attribute", "attrValue");
+
+ String setCookieHeaderString = setCookieHeader.toString();
+
+ assertTrue("Name/value pair is incorrect",
setCookieHeaderString.contains("name=value"));
+ assertTrue("Domain is incorrect", setCookieHeaderString.contains(";
Domain=example.com"));
+ assertTrue("Max-Age is incorrect", setCookieHeaderString.contains(";
Max-Age=231"));
+ assertTrue("HttpOnly is incorrect", setCookieHeaderString.contains(";
HttpOnly"));
+ assertTrue("Secure is incorrect", setCookieHeaderString.contains(";
Secure"));
+ assertTrue("Path is incorrect", setCookieHeaderString.contains(";
Path=/path"));
+ assertTrue("SameSite is incorrect", setCookieHeaderString.contains(";
SameSite=Lax"));
+ assertTrue("Additional attribute is incorrect",
setCookieHeaderString.contains("; Attribute=attrValue"));
+ }
+}
\ No newline at end of file