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 <[email protected]>
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);

Reply via email to