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/07/31 06:39:45 UTC
(knox) branch v3.0.0 updated: KNOX-3403: RFC 8693 token exchange: use the impersonated subject as the token sub on non-server-managed topologies (#1336)
This is an automated email from the ASF dual-hosted git repository.
smolnar82 pushed a commit to branch v3.0.0
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/v3.0.0 by this push:
new 65935d219 KNOX-3403: RFC 8693 token exchange: use the impersonated subject as the token sub on non-server-managed topologies (#1336)
65935d219 is described below
commit 65935d2193f6ff5232303c2a1cbb4abcfd0982a6
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Fri Jul 31 07:11:35 2026 +0200
KNOX-3403: RFC 8693 token exchange: use the impersonated subject as the token sub on non-server-managed topologies (#1336)
---
.../gateway/service/knoxtoken/TokenResource.java | 25 ++++++-----
.../knoxtoken/TokenServiceResourceTest.java | 51 ++++++++++++++++++++++
2 files changed, 64 insertions(+), 12 deletions(-)
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
index 8da6f137b..6d876648b 100644
--- a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
@@ -920,18 +920,19 @@ public class TokenResource {
protected UserContext buildUserContext(HttpServletRequest request) {
String userName = request.getUserPrincipal().getName();
String createdBy = null;
- // checking the doAs user only makes sense if tokens are managed (this is where we store the userName/createdBy information)
- // and if impersonation was enabled before (on HadoopAuth or identity-assertion level) so the the current subject has at least one ImpersonatedPrincipal principal
- if (tokenStateService != null) {
- final Subject subject = SubjectUtils.getCurrentSubject();
- if (subject != null && SubjectUtils.isImpersonating(subject)) {
- String primaryPrincipalName = SubjectUtils.getPrimaryPrincipalName(subject);
- String impersonatedPrincipalName = SubjectUtils.getImpersonatedPrincipalName(subject);
- if (!primaryPrincipalName.equals(impersonatedPrincipalName)) {
- createdBy = primaryPrincipalName;
- userName = impersonatedPrincipalName;
- log.tokenImpersonationSuccess(createdBy, userName);
- }
+ // When impersonation is in effect, the issued token's subject must be the effective (impersonated)
+ // identity, not the primary principal. This applies to traditional doAs as well as RFC 8693 token
+ // exchange (where the actor is the primary principal and the subject is the impersonated one).
+ // This is independent of server-managed state: createdBy is only persisted for managed tokens (see
+ // persistTokenDetails), so computing it here is harmless when there is no token state service.
+ final Subject subject = SubjectUtils.getCurrentSubject();
+ if (subject != null && SubjectUtils.isImpersonating(subject)) {
+ String primaryPrincipalName = SubjectUtils.getPrimaryPrincipalName(subject);
+ String impersonatedPrincipalName = SubjectUtils.getImpersonatedPrincipalName(subject);
+ if (!primaryPrincipalName.equals(impersonatedPrincipalName)) {
+ createdBy = primaryPrincipalName;
+ userName = impersonatedPrincipalName;
+ log.tokenImpersonationSuccess(createdBy, userName);
}
}
return new UserContext(userName, createdBy);
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index 281ea90ea..8398e168b 100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -2220,4 +2220,55 @@ public class TokenServiceResourceTest {
EasyMock.verify(request, context);
}
+
+ /**
+ * KNOX-3403: On a NON-server-managed topology, an impersonated request (traditional doAs or RFC 8693
+ * token exchange, where the actor is the primary principal and the subject is the impersonated one)
+ * must still issue a token whose {@code sub} is the impersonated subject. Previously buildUserContext
+ * only applied the impersonated identity when {@code tokenStateService != null}, so on a non-managed
+ * topology the {@code sub} incorrectly fell back to the primary principal (the actor).
+ */
+ @Test
+ @SuppressForbidden
+ public void testImpersonatedTokenSubjectOnNonServerManagedTopology() throws Exception {
+ final String primaryUser = "admin"; // primary principal (authenticated caller / actor)
+ final String impersonatedUser = "bob"; // impersonated subject (distinct from USER_NAME)
+
+ // No serverManagedTssEnabled argument -> token state service is absent (non-server-managed).
+ configureCommonExpectations(createDelegatedAuthContextExpectations(true, true));
+ Subject subject = createSubjectWithOptionalImpersonation(primaryUser, impersonatedUser);
+ JWTToken parsedToken = getTokenWithSubject(subject);
+
+ // The token subject must be the impersonated user, not the primary.
+ assertEquals("Non-server-managed impersonated token must use the impersonated subject as sub",
+ impersonatedUser, parsedToken.getSubject());
+
+ // The 'act' claim still records the primary user (delegated auth enabled).
+ Object actClaim = parsedToken.getClaimAsObject(JWTToken.ACT_CLAIM);
+ assertNotNull("RFC 8693 'act' claim should be present", actClaim);
+ assertTrue("'act' claim should be a Map", actClaim instanceof Map);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> actClaimMap = (Map<String, Object>) actClaim;
+ assertEquals("'act' claim should contain the primary user's subject", primaryUser, actClaimMap.get("sub"));
+
+ EasyMock.verify(request, context);
+ }
+
+ /**
+ * KNOX-3403: sanity check that decoupling the impersonated-sub from server-managed does not change
+ * the non-impersonating case - the token {@code sub} remains the authenticated (primary) user on a
+ * non-server-managed topology.
+ */
+ @Test
+ @SuppressForbidden
+ public void testNonImpersonatedTokenSubjectOnNonServerManagedTopology() throws Exception {
+ configureCommonExpectations(createDelegatedAuthContextExpectations(true, false));
+ Subject subject = createSubjectWithOptionalImpersonation(USER_NAME, null);
+ JWTToken parsedToken = getTokenWithSubject(subject);
+
+ assertEquals(USER_NAME, parsedToken.getSubject());
+ assertNull("'act' claim should NOT be present without impersonation", parsedToken.getClaimAsObject(JWTToken.ACT_CLAIM));
+
+ EasyMock.verify(request, context);
+ }
}