KNOX-3217: Upgrade pac4j to 6.3.0 for JDK17 (#1110)
* KNOX-3217: Upgrade pac4j for JDK17. Upgraded pac4j to 5.7.8 and javaee-pac4j to 7.1.0.
(Instead of the jee-pac4j artifact, javaee-pac4j needs to be used - then jakartaee-pac4j if we migrate to Jakarta).
Update opensaml to 4.2.0 and cryptacular to 1.2.5 (from pac4j-saml:5.7.8). Pin net.shibboleth.utilities:java-support to 8.3.1.
Fix KnoxSessionStore getSessionId and Pac4jIdentityAdapter removeProfiles call.
Corrected Pac4jProviderTest.
Pac4jSetCookieResponseWrapper.addCookie() is probably not needed anymore, pac4jcsrf is set in Set-Cookie header and is secure by default (goes through KnoxSessionStore).
* KNOX-3217: fix missing net.shibboleth.utilities:java-support:8.3.1 from Shibboleth maven repo. Updated shib-release maven repo URL.
* KNOX-3217: Update javaee-pac4j to 8.1.0, pac4j to 6.3.0 and opensaml to 5.1.6. Update cryptacular to 1.2.7 and xmlsec to 4.0.4.
org.pac4j.oidc.client.AzureAdClient was removed for AzureAd2Client; AzureAdOidcConfiguration to AzureAd2OidcConfiguration.
Pinned managed dependency versions for org.apache.httpcomponents.client5:httpclient5:5.4.3 and org.apache.httpcomponents.core5:5.3.6 should work:
org.pac4j:pac4j-saml:jar:6.3.0 would bring in org.apache.httpcomponents.client5:httpclient5:jar:5.3.1,
plus a dependency convergence error with org.apache.httpcomponents.core5:httpcore5:jar:5.2.5 and 5.2.4.
* KNOX-3217: Also remove pac4jCsrfTokenExpirationDate and pac4jPreviousCsrfToken in logout.jsp (added in pac4j 5.0).
* KNOX-3217: hibernate-core exclusion is not needed for opensaml-storage-impl.
* KNOX-3217: remove managed dependency com.nimbusds:lang-tag:1.5 (will be 1.7, no dependency convergence issues). org.pac4j.pac4j-oidc:6.3.0 needs com.nimbusds:lang-tag:1.7.
* KNOX-3217: Update nimbus-jose-jwt to 10.5 - dependency convergence - org.nimbus-jose-jwt:10.5 is needed for org.pac4j:pac4j-oidc:6.3.0. org.apereo.cas.client:cas-client-core:4.0.4 would need nimbus-jose-jwt:9.37.3 and org.apache.hadoop:hadoop-auth:3.4.1 would need nimbus-jose-jwt:9.37.2.
* KNOX-3217: review findings
diff --git a/gateway-applications/src/main/resources/applications/knoxauth/app/logout.jsp b/gateway-applications/src/main/resources/applications/knoxauth/app/logout.jsp
index debeb6e..5d85d20 100644
--- a/gateway-applications/src/main/resources/applications/knoxauth/app/logout.jsp
+++ b/gateway-applications/src/main/resources/applications/knoxauth/app/logout.jsp
@@ -126,6 +126,8 @@
// remove pac4j cookies
response.addHeader("Set-Cookie", removeCookie("pac4j.session.pac4jCsrfToken", p4j_domainName, pac4jPath));
+ response.addHeader("Set-Cookie", removeCookie("pac4j.session.pac4jCsrfTokenExpirationDate", p4j_domainName, pac4jPath));
+ response.addHeader("Set-Cookie", removeCookie("pac4j.session.pac4jPreviousCsrfToken", p4j_domainName, pac4jPath));
response.addHeader("Set-Cookie", removeCookie("pac4j.session.pac4jRequestedUrl", p4j_domainName, pac4jPath));
response.addHeader("Set-Cookie", removeCookie("pac4j.session.pac4jUserProfiles", p4j_domainName, pac4jPath));
response.addHeader("Set-Cookie", removeCookie("pac4j.session.pac4jUserProfiles", p4j_domainName, pac4jPath+"/websso"));
diff --git a/gateway-provider-security-pac4j/pom.xml b/gateway-provider-security-pac4j/pom.xml
index 5e27fa3..ee7e55c 100644
--- a/gateway-provider-security-pac4j/pom.xml
+++ b/gateway-provider-security-pac4j/pom.xml
@@ -28,6 +28,19 @@
<name>gateway-provider-security-pac4j</name>
<description>An extension of the gateway integrating pac4j as an authentication provider.</description>
+ <repositories>
+ <repository>
+ <id>shib-release</id>
+ <url>https://build.shibboleth.net/maven/releases</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ </repository>
+ </repositories>
+
<dependencies>
<dependency>
<groupId>org.apache.knox</groupId>
@@ -101,6 +114,10 @@
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
+ <artifactId>pac4j-javaee</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.pac4j</groupId>
<artifactId>pac4j-oauth</artifactId>
</dependency>
<dependency>
@@ -109,7 +126,7 @@
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
- <artifactId>pac4j-saml-opensamlv3</artifactId>
+ <artifactId>pac4j-saml</artifactId>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
@@ -135,13 +152,7 @@
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
- <artifactId>jee-pac4j</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.pac4j</groupId>
- <artifactId>pac4j-core</artifactId>
- </exclusion>
- </exclusions>
+ <artifactId>javaee-pac4j</artifactId>
</dependency>
<dependency>
<groupId>net.shibboleth.utilities</groupId>
diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/config/AzureADClientConfigurationDecorator.java b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/config/AzureADClientConfigurationDecorator.java
index 5021536..6338ce1 100644
--- a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/config/AzureADClientConfigurationDecorator.java
+++ b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/config/AzureADClientConfigurationDecorator.java
@@ -22,17 +22,17 @@
import org.pac4j.core.client.Client;
import org.pac4j.core.http.callback.PathParameterCallbackUrlResolver;
-import org.pac4j.oidc.client.AzureAdClient;
+import org.pac4j.oidc.client.AzureAd2Client;
public class AzureADClientConfigurationDecorator implements ClientConfigurationDecorator {
- private static final String AZURE_AD_CLIENT_CLASS_NAME = AzureAdClient.class.getSimpleName();
+ private static final String AZURE_AD_CLIENT_CLASS_NAME = AzureAd2Client.class.getSimpleName();
@Override
public void decorateClients(List<Client> clients, Map<String, String> properties) {
for (Client client : clients) {
if (AZURE_AD_CLIENT_CLASS_NAME.equalsIgnoreCase(client.getName())) {
// special handling for Azure AD, use path separators instead of query params
- ((AzureAdClient) client).setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
+ ((AzureAd2Client) client).setCallbackUrlResolver(new PathParameterCallbackUrlResolver());
}
}
}
diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/config/SAML2ClientConfigurationDecorator.java b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/config/SAML2ClientConfigurationDecorator.java
index b5a283d..1ce44da 100644
--- a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/config/SAML2ClientConfigurationDecorator.java
+++ b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/config/SAML2ClientConfigurationDecorator.java
@@ -83,7 +83,7 @@
private void setKeyStoreType(Map<String, String> properties, final SAML2Client saml2Client) {
final String keyStoreType = properties.get(KEYSTORE_TYPE);
if (StringUtils.isNotBlank(keyStoreType)) {
- saml2Client.getConfiguration().setKeystoreType(keyStoreType);
+ saml2Client.getConfiguration().setKeyStoreType(keyStoreType);
log.pac4jSamlKeystoreType(keyStoreType);
}
}
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 08b2a0e..5172773 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
@@ -40,14 +40,15 @@
import org.pac4j.config.client.PropertiesConstants;
import org.pac4j.core.client.Client;
import org.pac4j.core.config.Config;
-import org.pac4j.core.context.session.JEESessionStore;
+import org.pac4j.core.context.FrameworkParameters;
import org.pac4j.core.context.session.SessionStore;
import org.pac4j.core.util.CommonHelper;
import org.pac4j.http.client.indirect.IndirectBasicAuthClient;
import org.pac4j.http.credentials.authenticator.test.SimpleTestUsernamePasswordAuthenticator;
+import org.pac4j.jee.context.session.JEESessionStore;
import org.pac4j.jee.filter.CallbackFilter;
import org.pac4j.jee.filter.SecurityFilter;
-import org.pac4j.oidc.client.AzureAdClient;
+import org.pac4j.oidc.client.AzureAd2Client;
import org.pac4j.saml.client.SAML2Client;
import javax.servlet.Filter;
@@ -186,7 +187,7 @@
add the callback parameter to know it's a callback,
Azure AD does not honor query param so we add callback param as path element.
*/
- if (AzureAdClient.class.getSimpleName().equals(clientNameParameter) || (
+ if (AzureAd2Client.class.getSimpleName().equals(clientNameParameter) || (
!StringUtils.isBlank(oidcType) && PAC4J_OICD_TYPE_AZURE
.equals(oidcType))) {
pac4jCallbackUrl = pac4jCallbackUrl + URL_PATH_SEPARATOR + PAC4J_CALLBACK_PARAMETER;
@@ -252,12 +253,12 @@
if(!StringUtils.isBlank(sessionStoreVar) && JEESessionStore.class.getName().contains(sessionStoreVar) ) {
/* NOTE: this is a final variable, and will be used by all requests in Knox */
- sessionStore = JEESessionStore.INSTANCE;
+ sessionStore = new JEESessionStore();
} else {
sessionStore = new KnoxSessionStore(cryptoService, clusterName, domainSuffix, sessionStoreConfigs);
}
- config.setSessionStore(sessionStore);
+ config.setSessionStoreFactory((FrameworkParameters parameters) -> sessionStore);
SessionInvalidators.KNOX_SSO_INVALIDATOR.registerSessionInvalidator(this);
diff --git a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/filter/Pac4jIdentityAdapter.java b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/filter/Pac4jIdentityAdapter.java
index ecc0ec4..7c59c6b 100644
--- a/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/filter/Pac4jIdentityAdapter.java
+++ b/gateway-provider-security-pac4j/src/main/java/org/apache/knox/gateway/pac4j/filter/Pac4jIdentityAdapter.java
@@ -30,9 +30,13 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pac4j.core.config.Config;
-import org.pac4j.core.context.JEEContext;
-import org.pac4j.core.profile.CommonProfile;
+import org.pac4j.core.context.FrameworkParameters;
+import org.pac4j.core.context.session.SessionStore;
import org.pac4j.core.profile.ProfileManager;
+import org.pac4j.core.profile.UserProfile;
+import org.pac4j.core.util.Pac4jConstants;
+import org.pac4j.jee.context.JEEContext;
+import org.pac4j.jee.context.JEEFrameworkParameters;
import javax.security.auth.Subject;
import javax.servlet.Filter;
@@ -46,6 +50,7 @@
import java.io.IOException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
+import java.util.LinkedHashMap;
import java.util.Optional;
/**
@@ -84,13 +89,17 @@
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
- final JEEContext context = new JEEContext(request, response, ((Config)request.getAttribute(PAC4J_CONFIG)).getSessionStore());
- final ProfileManager<CommonProfile> manager = new ProfileManager<>(context);
- final Optional<CommonProfile> optional = manager.get(true);
+ final JEEContext context = new JEEContext(request, response);
+ Config pac4jConfig = ((Config)request.getAttribute(PAC4J_CONFIG));
+ FrameworkParameters frameworkParameters = new JEEFrameworkParameters(request, response);
+ SessionStore sessionStore = pac4jConfig.getSessionStoreFactory().newSessionStore(frameworkParameters);
+ final ProfileManager manager = new ProfileManager(context, sessionStore);
+ final Optional<UserProfile> optional = manager.getProfile();
if (optional.isPresent()) {
- CommonProfile profile = optional.get();
+ UserProfile profile = optional.get();
logger.debug("User authenticated as: {}", profile);
- manager.remove(true);
+ sessionStore.set(context, Pac4jConstants.USER_PROFILES, new LinkedHashMap<String, UserProfile>());
+ context.setRequestAttribute(Pac4jConstants.USER_PROFILES, new LinkedHashMap<String, UserProfile>());
String id = null;
if (idAttribute != null) {
Object attribute = profile.getAttribute(idAttribute);
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 0ac14ac..4522026 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
@@ -27,20 +27,19 @@
import org.apache.knox.gateway.util.Urls;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.pac4j.core.context.ContextHelper;
+import org.pac4j.core.context.WebContextHelper;
import org.pac4j.core.context.Cookie;
-import org.pac4j.core.context.JEEContext;
import org.pac4j.core.context.WebContext;
import org.pac4j.core.context.session.SessionStore;
import org.pac4j.core.exception.TechnicalException;
import org.pac4j.core.profile.CommonProfile;
-import org.pac4j.core.util.JavaSerializationHelper;
+import org.pac4j.core.util.serializer.JavaSerializer;
import org.pac4j.core.util.Pac4jConstants;
+import org.pac4j.jee.context.JEEContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -66,7 +65,7 @@
*
* @since 0.8.0
*/
-public class KnoxSessionStore<C extends WebContext> implements SessionStore<C> {
+public class KnoxSessionStore implements SessionStore {
private static final Logger logger = LogManager.getLogger(KnoxSessionStore.class);
@@ -74,7 +73,7 @@
public static final String PAC4J_SESSION_PREFIX = "pac4j.session.";
- private final JavaSerializationHelper javaSerializationHelper;
+ private final JavaSerializer javaSerializer;
private final CryptoService cryptoService;
@@ -85,14 +84,14 @@
final Map<String, String> sessionStoreConfigs;
public KnoxSessionStore(final CryptoService cryptoService, final String clusterName, final String domainSuffix) {
- this(cryptoService, clusterName, domainSuffix, new HashMap());
+ this(cryptoService, clusterName, domainSuffix, new HashMap<>());
}
public KnoxSessionStore(final CryptoService cryptoService,
final String clusterName,
final String domainSuffix,
final Map<String, String> sessionStoreConfigs) {
- javaSerializationHelper = new JavaSerializationHelper();
+ this.javaSerializer = new JavaSerializer();
this.cryptoService = cryptoService;
this.clusterName = clusterName;
this.domainSuffix = domainSuffix;
@@ -101,11 +100,11 @@
@Override
- public String getOrCreateSessionId(WebContext context) {
- return null;
+ public Optional<String> getSessionId(WebContext context, boolean createSession) {
+ return Optional.empty();
}
- private Serializable uncompressDecryptBase64(final String v) {
+ private Object uncompressDecryptBase64(final String v) {
if (v != null && !v.isEmpty()) {
byte[] bytes = Base64.decodeBase64(v);
EncryptionResult result = EncryptionResult.fromByteArray(bytes);
@@ -116,7 +115,7 @@
result.salt);
if (clear != null) {
try {
- return javaSerializationHelper.deserializeFromBytes(unCompress(clear));
+ return javaSerializer.deserializeFromBytes(unCompress(clear));
} catch (IOException e) {
throw new TechnicalException(e);
}
@@ -127,7 +126,7 @@
@Override
public Optional<Object> get(WebContext context, String key) {
- final Cookie cookie = ContextHelper.getCookie(context, PAC4J_SESSION_PREFIX + key);
+ final Cookie cookie = WebContextHelper.getCookie(context, PAC4J_SESSION_PREFIX + key);
Object value = null;
if (cookie != null) {
value = uncompressDecryptBase64(cookie.getValue());
@@ -141,13 +140,13 @@
|| (o instanceof Map<?,?> && ((Map<?,?>)o).isEmpty())) {
return null;
} else {
- byte[] bytes = javaSerializationHelper.serializeToBytes((Serializable) o);
+ byte[] bytes = javaSerializer.serializeToBytes(o);
/* compress the data */
try {
bytes = compress(bytes);
- if(bytes.length > 3000) {
+ if (bytes.length > 3000) {
logger.warn("Cookie too big, it might not be properly set");
}
@@ -186,7 +185,7 @@
throw new TechnicalException(e);
}
setCookieHeader.setSecure(true);
- if(ContextHelper.isHttpsOrSecure(context)) {
+ if (WebContextHelper.isHttpsOrSecure(context)) {
setCookieHeader.setHttpOnly(true);
}
@@ -293,7 +292,7 @@
}
@Override
- public Optional<SessionStore<C>> buildFromTrackableSession(WebContext arg0, Object arg1) {
+ public Optional<SessionStore> buildFromTrackableSession(WebContext arg0, Object arg1) {
return Optional.empty();
}
@@ -303,7 +302,7 @@
}
@Override
- public Optional getTrackableSession(WebContext arg0) {
+ public Optional<Object> getTrackableSession(WebContext arg0) {
return Optional.empty();
}
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 c4bc8d8..b466ef1 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
@@ -38,7 +38,6 @@
import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import java.util.Enumeration;
-import java.util.List;
import java.util.Optional;
import java.util.Properties;
@@ -112,19 +111,21 @@
assertEquals(302, response.getStatus());
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(1, cookies.size());
- Optional<String> requestedUrlSetCookie = response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.REQUESTED_URL))
- .findFirst();
+ Optional<String> pac4jCsrfToken = findFirstCookieByName(response, Pac4jConstants.CSRF_TOKEN);
+ assertTrue(pac4jCsrfToken.isPresent());
+ assertTrue(pac4jCsrfToken.get().contains("Secure;"));
+
+ assertTrue(findFirstCookieByName(response, Cookies.PREVIOUS_CSRF_TOKEN).isPresent());
+ assertTrue(findFirstCookieByName(response, Cookies.CSRF_TOKEN_EXPIRATION_DATE).isPresent());
+
+ Optional<String> requestedUrlSetCookie = findFirstCookieByName(response, Cookies.REQUESTED_URL);
assertTrue(requestedUrlSetCookie.isPresent());
- assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.CSRF_TOKEN)).count());
+ assertEquals(1, countCookiesByName(response, Cookies.CSRF_TOKEN));
// step 2: send credentials to the callback url (callback from the identity provider)
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{this.setCookieParser(requestedUrlSetCookie.get())});
+ request.setCookies(new Cookie[]{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);
@@ -137,18 +138,16 @@
assertEquals(302, response.getStatus());
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();
+ Optional<String> userProfilesSetCookie = findFirstCookieByName(response, Cookies.USER_PROFILES);
assertTrue(userProfilesSetCookie.isPresent());
+
+ requestedUrlSetCookie = findFirstCookieByName(response, Cookies.REQUESTED_URL);
assertTrue(requestedUrlSetCookie.isPresent());
+ assertTrue(cookieValueIsNull(requestedUrlSetCookie.get()));
// step 3: turn pac4j identity into KnoxSSO identity
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{this.setCookieParser(requestedUrlSetCookie.get()), this.setCookieParser(userProfilesSetCookie.get())});
+ request.setCookies(new Cookie[]{setCookieParser(requestedUrlSetCookie.get()), setCookieParser(userProfilesSetCookie.get())});
request.setRequestURL(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" + HADOOP_SERVICE_URL);
request.setServerName(LOCALHOST);
response = new MockHttpServletResponse();
@@ -157,8 +156,7 @@
assertEquals(0, response.getStatus());
adapter.doFilter(request, response, filterChain);
- assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.USER_PROFILES + "=null;")).count());
+ assertEquals(1, countCookiesWithNullValueByName(response, Cookies.USER_PROFILES));
assertEquals(USERNAME, adapter.getTestIdentifier());
}
@@ -215,19 +213,18 @@
assertEquals(302, response.getStatus());
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(1, cookies.size());
- Optional<String> requestedUrlSetCookie = response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.REQUESTED_URL))
- .findFirst();
+ Optional<String> pac4jCsrfToken = findFirstCookieByName(response, Pac4jConstants.CSRF_TOKEN);
+ assertTrue(pac4jCsrfToken.isPresent());
+ assertTrue(pac4jCsrfToken.get().contains("Secure;"));
+
+ Optional<String> requestedUrlSetCookie = findFirstCookieByName(response, Cookies.REQUESTED_URL);
assertTrue(requestedUrlSetCookie.isPresent());
- assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.CSRF_TOKEN)).count());
+ assertEquals(1, countCookiesByName(response, Cookies.CSRF_TOKEN));
// step 2: send credentials to the callback url (callback from the identity provider)
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{this.setCookieParser(requestedUrlSetCookie.get())});
+ request.setCookies(new Cookie[]{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,18 +237,16 @@
assertEquals(302, response.getStatus());
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();
+ Optional<String> userProfilesSetCookie = findFirstCookieByName(response, Cookies.USER_PROFILES);
assertTrue(userProfilesSetCookie.isPresent());
+
+ requestedUrlSetCookie = findFirstCookieByName(response, Cookies.REQUESTED_URL);
assertTrue(requestedUrlSetCookie.isPresent());
+ assertTrue(cookieValueIsNull(requestedUrlSetCookie.get()));
// step 3: turn pac4j identity into KnoxSSO identity
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{this.setCookieParser(requestedUrlSetCookie.get()), this.setCookieParser(userProfilesSetCookie.get())});
+ request.setCookies(new Cookie[]{setCookieParser(requestedUrlSetCookie.get()), setCookieParser(userProfilesSetCookie.get())});
request.setRequestURL(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" + HADOOP_SERVICE_URL);
request.setServerName(LOCALHOST);
response = new MockHttpServletResponse();
@@ -260,8 +255,7 @@
assertEquals(0, response.getStatus());
adapter.doFilter(request, response, filterChain);
- assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.USER_PROFILES + "=null;")).count());
+ assertEquals(1, countCookiesWithNullValueByName(response, Cookies.USER_PROFILES));
assertEquals(USERNAME, adapter.getTestIdentifier());
}
@@ -318,19 +312,18 @@
assertEquals(302, response.getStatus());
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(1, cookies.size());
- Optional<String> requestedUrlSetCookie = response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.REQUESTED_URL))
- .findFirst();
+ Optional<String> pac4jCsrfToken = findFirstCookieByName(response, Pac4jConstants.CSRF_TOKEN);
+ assertTrue(pac4jCsrfToken.isPresent());
+ assertTrue(pac4jCsrfToken.get().contains("Secure;"));
+
+ Optional<String> requestedUrlSetCookie = findFirstCookieByName(response, Cookies.REQUESTED_URL);
assertTrue(requestedUrlSetCookie.isPresent());
- assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.CSRF_TOKEN)).count());
+ assertEquals(1, countCookiesByName(response, Cookies.CSRF_TOKEN));
// step 2: send credentials to the callback url (callback from the identity provider)
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{this.setCookieParser(requestedUrlSetCookie.get())});
+ request.setCookies(new Cookie[]{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);
@@ -343,18 +336,16 @@
assertEquals(302, response.getStatus());
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();
+ Optional<String> userProfilesSetCookie = findFirstCookieByName(response, Cookies.USER_PROFILES);
assertTrue(userProfilesSetCookie.isPresent());
+
+ requestedUrlSetCookie = findFirstCookieByName(response, Cookies.REQUESTED_URL);
assertTrue(requestedUrlSetCookie.isPresent());
+ assertTrue(cookieValueIsNull(requestedUrlSetCookie.get()));
// step 3: turn pac4j identity into KnoxSSO identity
request = new MockHttpServletRequest();
- request.setCookies(new Cookie[]{this.setCookieParser(requestedUrlSetCookie.get()), this.setCookieParser(userProfilesSetCookie.get())});
+ request.setCookies(new Cookie[]{setCookieParser(requestedUrlSetCookie.get()), setCookieParser(userProfilesSetCookie.get())});
request.setRequestURL(KNOXSSO_SERVICE_URL + "?" + ORIGINAL_URL + "=" + HADOOP_SERVICE_URL);
request.setServerName(LOCALHOST);
response = new MockHttpServletResponse();
@@ -363,8 +354,7 @@
assertEquals(0, response.getStatus());
adapter.doFilter(request, response, filterChain);
- assertEquals(1, response.getHeaders().get("Set-Cookie").stream()
- .filter(c -> c.startsWith(KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.USER_PROFILES + "=null;")).count());
+ assertEquals(1, countCookiesWithNullValueByName(response, Cookies.USER_PROFILES));
assertEquals(USERNAME, adapter.getTestIdentifier());
}
@@ -401,16 +391,16 @@
EasyMock.verify(aliasService);
}
- private Cookie setCookieParser(String setCookie) {
+ private static 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();
+ private static class FilterConfigStub implements FilterConfig {
+ private final ServletContext context;
+ private final Properties properties = new Properties();
FilterConfigStub(ServletContext context) {
this.context = context;
@@ -441,4 +431,35 @@
return (Enumeration<String>) properties.propertyNames();
}
}
+
+ private static Optional<String> findFirstCookieByName(MockHttpServletResponse response, String name) {
+ return response.getHeaders().get("Set-Cookie").stream()
+ .filter(c -> c.startsWith(name+"="))
+ .findFirst();
+ }
+
+ private static long countCookiesByName(MockHttpServletResponse response, String name) {
+ return response.getHeaders().get("Set-Cookie").stream()
+ .filter(c -> c.startsWith(name+"="))
+ .count();
+ }
+
+ private static long countCookiesWithNullValueByName(MockHttpServletResponse response, String name) {
+ return response.getHeaders().get("Set-Cookie").stream()
+ .filter(c -> c.startsWith(name+"=null;"))
+ .count();
+ }
+
+ private static boolean cookieValueIsNull(String setCookieHeader) {
+ return setCookieParser(setCookieHeader).getValue().equals("null");
+ }
+
+ private static class Cookies {
+ static final String REQUESTED_URL = KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.REQUESTED_URL;
+ static final String CSRF_TOKEN_EXPIRATION_DATE = KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.CSRF_TOKEN_EXPIRATION_DATE;
+ static final String PREVIOUS_CSRF_TOKEN = KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.PREVIOUS_CSRF_TOKEN;
+ static final String CSRF_TOKEN = KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.CSRF_TOKEN;
+ static final String USER_PROFILES = KnoxSessionStore.PAC4J_SESSION_PREFIX + Pac4jConstants.USER_PROFILES;
+ }
+
}
diff --git a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/config/AzureADClientConfigurationDecoratorTest.java b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/config/AzureADClientConfigurationDecoratorTest.java
index 0605025..ac1b984 100644
--- a/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/config/AzureADClientConfigurationDecoratorTest.java
+++ b/gateway-provider-security-pac4j/src/test/java/org/apache/knox/gateway/pac4j/config/AzureADClientConfigurationDecoratorTest.java
@@ -23,15 +23,15 @@
import org.junit.Test;
import org.pac4j.core.http.callback.PathParameterCallbackUrlResolver;
-import org.pac4j.oidc.client.AzureAdClient;
-import org.pac4j.oidc.config.AzureAdOidcConfiguration;
+import org.pac4j.oidc.client.AzureAd2Client;
+import org.pac4j.oidc.config.AzureAd2OidcConfiguration;
public class AzureADClientConfigurationDecoratorTest {
@Test
public void testAzureADClientConfigurationDecoration() throws Exception {
- final AzureAdOidcConfiguration azureAdConfig = new AzureAdOidcConfiguration();
- final AzureAdClient client = new AzureAdClient(azureAdConfig);
+ final AzureAd2OidcConfiguration azureAdConfig = new AzureAd2OidcConfiguration();
+ final AzureAd2Client client = new AzureAd2Client(azureAdConfig);
final AzureADClientConfigurationDecorator azureConfigDecorator = new AzureADClientConfigurationDecorator();
azureConfigDecorator.decorateClients(Collections.singletonList(client), null);
assertTrue(client.getCallbackUrlResolver() instanceof PathParameterCallbackUrlResolver);
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 32cb5e4..a56f682 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,9 +24,9 @@
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
-import org.pac4j.core.context.JEEContext;
import org.pac4j.core.profile.CommonProfile;
import org.pac4j.core.util.Pac4jConstants;
+import org.pac4j.jee.context.JEEContext;
import org.pac4j.saml.profile.SAML2Profile;
import javax.servlet.http.HttpServletResponse;
diff --git a/pom.xml b/pom.xml
index f7568d6..8551319 100644
--- a/pom.xml
+++ b/pom.xml
@@ -191,7 +191,7 @@
<commons-net.version>3.9.0</commons-net.version>
<commons-text.version>1.10.0</commons-text.version>
<cors-filter.version>2.9.1</cors-filter.version>
- <cryptacular.version>1.2.4</cryptacular.version>
+ <cryptacular.version>1.2.7</cryptacular.version>
<curator.version>5.4.0</curator.version>
<dependency-check-maven.version>6.0.3</dependency-check-maven.version>
<derby.db.version>10.14.2.0</derby.db.version> <!-- 10.15.1.3 requires Java 9 -->
@@ -233,10 +233,10 @@
<javax.websocket-api.version>1.1</javax.websocket-api.version>
<jaxws-ri.version>2.3.3</jaxws-ri.version>
<jakarta.xml.bind.version>2.3.2</jakarta.xml.bind.version>
- <java-support.version>7.5.1</java-support.version>
+ <java-support.version>8.3.1</java-support.version>
<javassist.version>3.27.0-GA</javassist.version>
<jboss-logging.version>3.4.1.Final</jboss-logging.version>
- <jee-pac4j.version>5.0.0</jee-pac4j.version>
+ <jee-pac4j.version>8.0.1</jee-pac4j.version>
<jericho-html.version>3.4</jericho-html.version>
<jersey.version>2.47</jersey.version>
<jetty.version>9.4.57.v20241219</jetty.version>
@@ -248,7 +248,6 @@
<jakarta.annotation-api.version>1.3.4</jakarta.annotation-api.version>
<junit.version>4.13.2</junit.version>
<kotlin-stdlib.version>1.9.10</kotlin-stdlib.version>
- <lang-tag.version>1.5</lang-tag.version>
<libpam4j.version>1.11</libpam4j.version>
<log4j2.version>2.20.0</log4j2.version>
<maven-checkstyle-plugin.version>3.1.1</maven-checkstyle-plugin.version>
@@ -259,11 +258,11 @@
<metrics.version>4.1.16</metrics.version>
<mina.version>2.2.4</mina.version>
<netty.version>4.1.127.Final</netty.version>
- <nimbus-jose-jwt.version>10.0.2</nimbus-jose-jwt.version>
+ <nimbus-jose-jwt.version>10.5</nimbus-jose-jwt.version>
<nodejs.version>v22.20.0</nodejs.version>
<okhttp.version>4.12.0</okhttp.version>
- <opensaml.version>3.4.5</opensaml.version>
- <pac4j.version>4.5.6</pac4j.version>
+ <opensaml.version>5.1.6</opensaml.version>
+ <pac4j.version>6.3.0</pac4j.version>
<postgresql.version>42.4.4</postgresql.version>
<mysql.version>8.0.28</mysql.version>
<mariadb.connector.version>3.3.0</mariadb.connector.version>
@@ -292,7 +291,7 @@
<txw2.version>2.4.0-b180830.0438</txw2.version>
<velocity.version>2.4.1</velocity.version>
<woodstox-core.version>6.4.0</woodstox-core.version>
- <xmlsec.version>2.2.6</xmlsec.version>
+ <xmlsec.version>4.0.4</xmlsec.version>
<xmltool.version>3.3</xmltool.version>
<xml-jaxb.version>2.3.1</xml-jaxb.version>
<xml-matchers.version>0.10</xml-matchers.version>
@@ -385,7 +384,7 @@
</repository>
<repository>
<id>shib-release</id>
- <url>https://build.shibboleth.net/nexus/content/groups/public</url>
+ <url>https://build.shibboleth.net/maven/releases</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
@@ -1481,11 +1480,6 @@
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose-jwt.version}</version>
</dependency>
- <dependency>
- <groupId>com.nimbusds</groupId>
- <artifactId>lang-tag</artifactId>
- <version>${lang-tag.version}</version>
- </dependency>
<dependency>
<groupId>net.minidev</groupId>
@@ -2506,6 +2500,11 @@
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
+ <artifactId>pac4j-javaee</artifactId>
+ <version>${pac4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.pac4j</groupId>
<artifactId>pac4j-http</artifactId>
<version>${pac4j.version}</version>
</dependency>
@@ -2521,7 +2520,7 @@
</dependency>
<dependency>
<groupId>org.pac4j</groupId>
- <artifactId>pac4j-saml-opensamlv3</artifactId>
+ <artifactId>pac4j-saml</artifactId>
<version>${pac4j.version}</version>
<exclusions>
<exclusion>
@@ -2559,12 +2558,12 @@
<dependency>
<groupId>org.pac4j</groupId>
- <artifactId>jee-pac4j</artifactId>
+ <artifactId>javaee-pac4j</artifactId>
<version>${jee-pac4j.version}</version>
<exclusions>
<exclusion>
<groupId>org.pac4j</groupId>
- <artifactId>pac4j-core</artifactId>
+ <artifactId>pac4j-javaee</artifactId>
</exclusion>
</exclusions>
</dependency>
@@ -2605,16 +2604,6 @@
<groupId>org.opensaml</groupId>
<artifactId>opensaml-storage-impl</artifactId>
<version>${opensaml.version}</version>
- <exclusions>
- <exclusion>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-entitymanager</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
- </exclusion>
- </exclusions>
</dependency>
<dependency>
<groupId>org.javassist</groupId>