You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by sm...@apache.org on 2026/02/20 11:32:44 UTC

(knox) branch master updated: KNOX-3254 - Allow control the Secure flag in pac4j session cookies (#1148)

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 79f0deb93 KNOX-3254 - Allow control the Secure flag in pac4j session cookies (#1148)
79f0deb93 is described below

commit 79f0deb935d45eb8d8302ce0eed1ad08aa00a5c8
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Fri Feb 20 12:32:39 2026 +0100

    KNOX-3254 - Allow control the Secure flag in pac4j session cookies (#1148)
---
 .../pac4j/filter/Pac4jDispatcherFilter.java        |  6 +++
 .../gateway/pac4j/session/KnoxSessionStore.java    |  5 +-
 .../pac4j/session/KnoxSessionStoreTest.java        | 60 ++++++++++++++++++++++
 3 files changed, 70 insertions(+), 1 deletion(-)

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 51727733d..1f90c1568 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
@@ -113,6 +113,8 @@ public class Pac4jDispatcherFilter implements Filter, SessionInvalidator {
   /* A comma seperated list of attributes that needed to be excluded */
   public static final String PAC4J_SESSION_STORE_EXCLUDE_CUSTOM_ATTRIBUTES = "pac4j.session.store.exclude.custom.attributes";
 
+  public static final String PAC4J_SESSION_STORE_SECURE_COOKIE = "pac4j.session.store.secure.cookie";
+
   public static final String PAC4J_SESSION_STORE_EXCLUDE_CUSTOM_ATTRIBUTES_DEFAULT = "";
 
   public static final String PAC4J_SESSION_STORE_EXCLUDE_GROUPS_DEFAULT = "true";
@@ -236,6 +238,10 @@ public class Pac4jDispatcherFilter implements Filter, SessionInvalidator {
       if(StringUtils.isNotBlank(filterConfig.getInitParameter(PAC4J_COOKIE_SAMESITE))) {
         setSessionStoreConfig(filterConfig, PAC4J_COOKIE_SAMESITE, null);
       }
+      /* control the Secure flag */
+      if (StringUtils.isNotBlank(filterConfig.getInitParameter(PAC4J_SESSION_STORE_SECURE_COOKIE))) {
+        setSessionStoreConfig(filterConfig, PAC4J_SESSION_STORE_SECURE_COOKIE, null);
+      }
       //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 4b9c365b3..700ceaf65 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
@@ -58,6 +58,7 @@ import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_S
 import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_EXCLUDE_ROLES;
 import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_EXCLUDE_ROLES_DEFAULT;
 import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_COOKIE_MAX_AGE;
+import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_SECURE_COOKIE;
 
 /**
  * Specific session store where data are saved into cookies (and not in memory).
@@ -184,7 +185,9 @@ public class KnoxSessionStore implements SessionStore {
         } catch (final Exception e) {
             throw new TechnicalException(e);
         }
-        setCookieHeader.setSecure(true);
+        final String secureCookieSettings = sessionStoreConfigs.get(PAC4J_SESSION_STORE_SECURE_COOKIE);
+        final boolean secureFlag = secureCookieSettings == null ? WebContextHelper.isHttpsOrSecure(context) : Boolean.parseBoolean(secureCookieSettings);
+        setCookieHeader.setSecure(secureFlag);
         if (WebContextHelper.isHttpsOrSecure(context)) {
             setCookieHeader.setHttpOnly(true);
         }
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 d48a4261d..1ad63b150 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
@@ -46,6 +46,7 @@ import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_S
 import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_EXCLUDE_PERMISSIONS_DEFAULT;
 import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_EXCLUDE_ROLES;
 import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_EXCLUDE_ROLES_DEFAULT;
+import static org.apache.knox.gateway.pac4j.filter.Pac4jDispatcherFilter.PAC4J_SESSION_STORE_SECURE_COOKIE;
 import static org.apache.knox.gateway.pac4j.session.KnoxSessionStore.PAC4J_PASSWORD;
 import static org.apache.knox.gateway.pac4j.session.KnoxSessionStore.PAC4J_SESSION_PREFIX;
 
@@ -163,6 +164,65 @@ public class KnoxSessionStoreTest {
     Assert.assertNotNull(samlProfile.getAttribute("https://knox.apache.org/SAML/Attributes/groups2"));
   }
 
+  @Test
+  public void testSecureCookieSettings() throws Exception {
+    final AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+    EasyMock.expect(aliasService.getPasswordFromAliasForCluster(CLUSTER_NAME, PAC4J_PASSWORD)).andReturn(PAC4J_PASSWORD.toCharArray()).anyTimes();
+    EasyMock.replay(aliasService);
+
+    final DefaultCryptoService cryptoService = new DefaultCryptoService();
+    cryptoService.setAliasService(aliasService);
+
+    final Map<String, CommonProfile> profile = new HashMap<>();
+    profile.put("SAML2Client", new SAML2Profile());
+
+    // 1. Test when pac4j.session.store.secure.cookie is explicitly set to true
+    runSecureCookieTest(cryptoService, profile, "true", "http://local.com/gateway/knoxsso/", true, "Secure flag should be true when explicitly set");
+
+    // 2. Test when pac4j.session.store.secure.cookie is explicitly set to false
+    runSecureCookieTest(cryptoService, profile, "false", "https://local.com/gateway/knoxsso/", false, "Secure flag should be false when explicitly set");
+
+    // 3. Test default behavior for HTTPS request
+    runSecureCookieTest(cryptoService, profile, null, "https://local.com/gateway/knoxsso/", true, "Secure flag should be true for HTTPS request by default");
+
+    // 4. Test default behavior for HTTP request
+    runSecureCookieTest(cryptoService, profile, null, "http://local.com/gateway/knoxsso/", false, "Secure flag should be false for HTTP request by default");
+  }
+
+  private void runSecureCookieTest(CryptoService cryptoService, Map<String, CommonProfile> profile,
+      String secureCookieValue, String requestUrl, boolean expectSecureFlag, String assertMessage) {
+    final Map<String, String> sessionStoreConfigs = new HashMap<>();
+    if (secureCookieValue != null) {
+      sessionStoreConfigs.put(PAC4J_SESSION_STORE_SECURE_COOKIE, secureCookieValue);
+    }
+
+    final Capture<String> capturedHeader = EasyMock.newCapture();
+    final HttpServletResponse mockResponse = EasyMock.createNiceMock(HttpServletResponse.class);
+    mockResponse.addHeader(EasyMock.eq("Set-Cookie"), EasyMock.capture(capturedHeader));
+    EasyMock.replay(mockResponse);
+
+    final JEEContext mockContext = EasyMock.createNiceMock(JEEContext.class);
+    EasyMock.expect(mockContext.getNativeResponse()).andReturn(mockResponse).anyTimes();
+    EasyMock.expect(mockContext.getFullRequestURL()).andReturn(requestUrl).anyTimes();
+    if (requestUrl.startsWith("https")) {
+      EasyMock.expect(mockContext.getScheme()).andReturn("https").anyTimes();
+      EasyMock.expect(mockContext.isSecure()).andReturn(true).anyTimes();
+    } else {
+      EasyMock.expect(mockContext.getScheme()).andReturn("http").anyTimes();
+      EasyMock.expect(mockContext.isSecure()).andReturn(false).anyTimes();
+    }
+    EasyMock.replay(mockContext);
+
+    final KnoxSessionStore sessionStore = new KnoxSessionStore(cryptoService, CLUSTER_NAME, null, sessionStoreConfigs);
+    sessionStore.set(mockContext, Pac4jConstants.USER_PROFILES, profile);
+
+    if (expectSecureFlag) {
+      Assert.assertTrue(assertMessage, capturedHeader.getValue().contains("Secure"));
+    } else {
+      Assert.assertFalse(assertMessage, capturedHeader.getValue().contains("Secure"));
+    }
+  }
+
   @Test
   public void testNullCookieValue() throws AliasServiceException {
     final CryptoService cryptoService = EasyMock.createNiceMock(CryptoService.class);