[
https://issues.apache.org/jira/browse/KNOX-3347?focusedWorklogId=1025416&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1025416
]
ASF GitHub Bot logged work on KNOX-3347:
----------------------------------------
Author: ASF GitHub Bot
Created on: 16/Jun/26 14:18
Start Date: 16/Jun/26 14:18
Worklog Time Spent: 10m
Work Description: lmccay commented on code in PR #1262:
URL: https://github.com/apache/knox/pull/1262#discussion_r3421469656
##########
gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java:
##########
@@ -405,6 +415,132 @@ private boolean
authenticateWithCookies(HttpServletRequest request, HttpServletR
return false;
}
+ /**
+ * Handle RFC 8693 token exchange flow.
+ *
+ * <p>This method validates both the subject_token and actor_token
parameters,
+ * creates a TokenExchangePrincipal with the identity information from both
tokens,
+ * and establishes a Subject with the actor as the PrimaryPrincipal.</p>
+ *
+ * <p>The TokenExchangePrincipal signals to the identity assertion layer that
+ * impersonation should be established with the subject as the
ImpersonatedPrincipal.</p>
+ *
+ * @param request the HTTP request containing subject_token and actor_token
parameters
+ * @param response the HTTP response
+ * @param chain the filter chain
+ * @throws IOException if an I/O error occurs
+ * @throws ServletException if a servlet error occurs
+ */
+ private void handleTokenExchange(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ // Extract subject_token (required)
+ String subjectTokenValue = request.getParameter(SUBJECT_TOKEN);
+ if (subjectTokenValue == null || subjectTokenValue.isEmpty()) {
+ handleValidationError(request, response,
HttpServletResponse.SC_BAD_REQUEST,
+ "RFC 8693 token exchange requires subject_token parameter");
+ return;
+ }
+
+ // Extract actor_token (required for proper token exchange)
+ String actorTokenValue = request.getParameter(ACTOR_TOKEN);
+ if (actorTokenValue == null || actorTokenValue.isEmpty()) {
+ handleValidationError(request, response,
HttpServletResponse.SC_BAD_REQUEST,
+ "RFC 8693 token exchange requires actor_token parameter");
+ return;
+ }
+
+ try {
+ // Parse and validate subject_token
+ JWT subjectToken = parseAndValidateJWT(request, response, chain,
subjectTokenValue);
+ if (subjectToken == null) {
+ // Validation failed, error response already sent
+ return;
+ }
+
+ // Parse and validate actor_token
+ JWT actorToken = parseAndValidateJWT(request, response, chain,
actorTokenValue);
+ if (actorToken == null) {
+ // Validation failed, error response already sent
+ return;
+ }
+
+ // Create Subject with actor as PrimaryPrincipal and
TokenExchangePrincipal
+ Subject subject = createSubjectForTokenExchange(subjectToken,
actorToken);
+
+ continueWithEstablishedSecurityContext(subject, request, response,
chain);
+
+ } catch (ParseException e) {
+ LOGGER.failedToParsePasscodeToken(e);
+ handleValidationError(request, response,
HttpServletResponse.SC_UNAUTHORIZED,
+ "Failed to parse token in token exchange: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Parse and validate a JWT token.
+ *
+ * @param request the HTTP request
+ * @param response the HTTP response
+ * @param chain the filter chain
+ * @param tokenValue the JWT string to parse
+ * @return the parsed and validated JWT, or null if validation failed
+ * @throws ParseException if the JWT cannot be parsed
+ * @throws IOException if an I/O error occurs during validation
+ * @throws ServletException if a servlet error occurs during validation
+ */
+ private JWT parseAndValidateJWT(HttpServletRequest request,
HttpServletResponse response,
+ FilterChain chain, String tokenValue)
+ throws ParseException, IOException, ServletException {
+ JWT token = new JWTToken(tokenValue);
+ if (validateToken(request, response, chain, token)) {
+ return token;
+ }
+ // Validation failed - error response already sent by validateToken
+ return null;
+ }
+
+ /**
+ * Create a Subject for RFC 8693 token exchange with proper principal setup.
+ *
+ * @param subjectToken the validated subject token
+ * @param actorToken the validated actor token
+ * @return a Subject configured for token exchange
+ */
+ private Subject createSubjectForTokenExchange(JWT subjectToken, JWT
actorToken) {
+ // Extract identities from the tokens
+ String subjectPrincipalName = subjectToken.getSubject();
+ String subjectIssuer = subjectToken.getIssuer();
+ String actorPrincipalName = actorToken.getSubject();
+ String actorIssuer = actorToken.getIssuer();
+ // Create principals for the Subject
+ // PrimaryPrincipal is the ACTOR (the authenticated party)
+ org.apache.knox.gateway.security.PrimaryPrincipal primaryPrincipal =
+ new
org.apache.knox.gateway.security.PrimaryPrincipal(actorPrincipalName);
Review Comment:
got it
##########
gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java:
##########
@@ -405,6 +415,132 @@ private boolean
authenticateWithCookies(HttpServletRequest request, HttpServletR
return false;
}
+ /**
+ * Handle RFC 8693 token exchange flow.
+ *
+ * <p>This method validates both the subject_token and actor_token
parameters,
+ * creates a TokenExchangePrincipal with the identity information from both
tokens,
+ * and establishes a Subject with the actor as the PrimaryPrincipal.</p>
+ *
+ * <p>The TokenExchangePrincipal signals to the identity assertion layer that
+ * impersonation should be established with the subject as the
ImpersonatedPrincipal.</p>
+ *
+ * @param request the HTTP request containing subject_token and actor_token
parameters
+ * @param response the HTTP response
+ * @param chain the filter chain
+ * @throws IOException if an I/O error occurs
+ * @throws ServletException if a servlet error occurs
+ */
+ private void handleTokenExchange(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ // Extract subject_token (required)
+ String subjectTokenValue = request.getParameter(SUBJECT_TOKEN);
+ if (subjectTokenValue == null || subjectTokenValue.isEmpty()) {
+ handleValidationError(request, response,
HttpServletResponse.SC_BAD_REQUEST,
+ "RFC 8693 token exchange requires subject_token parameter");
+ return;
+ }
+
+ // Extract actor_token (required for proper token exchange)
+ String actorTokenValue = request.getParameter(ACTOR_TOKEN);
+ if (actorTokenValue == null || actorTokenValue.isEmpty()) {
+ handleValidationError(request, response,
HttpServletResponse.SC_BAD_REQUEST,
+ "RFC 8693 token exchange requires actor_token parameter");
+ return;
+ }
+
+ try {
+ // Parse and validate subject_token
+ JWT subjectToken = parseAndValidateJWT(request, response, chain,
subjectTokenValue);
+ if (subjectToken == null) {
+ // Validation failed, error response already sent
+ return;
+ }
+
+ // Parse and validate actor_token
+ JWT actorToken = parseAndValidateJWT(request, response, chain,
actorTokenValue);
+ if (actorToken == null) {
+ // Validation failed, error response already sent
+ return;
+ }
+
+ // Create Subject with actor as PrimaryPrincipal and
TokenExchangePrincipal
+ Subject subject = createSubjectForTokenExchange(subjectToken,
actorToken);
+
+ continueWithEstablishedSecurityContext(subject, request, response,
chain);
+
+ } catch (ParseException e) {
+ LOGGER.failedToParsePasscodeToken(e);
+ handleValidationError(request, response,
HttpServletResponse.SC_UNAUTHORIZED,
+ "Failed to parse token in token exchange: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Parse and validate a JWT token.
+ *
+ * @param request the HTTP request
+ * @param response the HTTP response
+ * @param chain the filter chain
+ * @param tokenValue the JWT string to parse
+ * @return the parsed and validated JWT, or null if validation failed
+ * @throws ParseException if the JWT cannot be parsed
+ * @throws IOException if an I/O error occurs during validation
+ * @throws ServletException if a servlet error occurs during validation
+ */
+ private JWT parseAndValidateJWT(HttpServletRequest request,
HttpServletResponse response,
+ FilterChain chain, String tokenValue)
+ throws ParseException, IOException, ServletException {
+ JWT token = new JWTToken(tokenValue);
+ if (validateToken(request, response, chain, token)) {
+ return token;
+ }
+ // Validation failed - error response already sent by validateToken
+ return null;
+ }
+
+ /**
+ * Create a Subject for RFC 8693 token exchange with proper principal setup.
+ *
+ * @param subjectToken the validated subject token
+ * @param actorToken the validated actor token
+ * @return a Subject configured for token exchange
+ */
+ private Subject createSubjectForTokenExchange(JWT subjectToken, JWT
actorToken) {
+ // Extract identities from the tokens
+ String subjectPrincipalName = subjectToken.getSubject();
+ String subjectIssuer = subjectToken.getIssuer();
+ String actorPrincipalName = actorToken.getSubject();
+ String actorIssuer = actorToken.getIssuer();
+ // Create principals for the Subject
+ // PrimaryPrincipal is the ACTOR (the authenticated party)
+ org.apache.knox.gateway.security.PrimaryPrincipal primaryPrincipal =
+ new
org.apache.knox.gateway.security.PrimaryPrincipal(actorPrincipalName);
+
+ // TokenExchangePrincipal carries metadata for identity assertion layer
+ org.apache.knox.gateway.security.TokenExchangePrincipal
tokenExchangePrincipal =
+ new org.apache.knox.gateway.security.TokenExchangePrincipalImpl(
+ subjectPrincipalName, subjectIssuer, actorPrincipalName,
actorIssuer);
+
+ // Extract actor chain from subject_token (if present) using existing logic
+ java.util.List<java.util.Map<String, Object>> actorChain =
+
org.apache.knox.gateway.services.security.token.TokenUtils.extractActorChain(subjectToken);
Review Comment:
got it
Issue Time Tracking
-------------------
Worklog Id: (was: 1025416)
Time Spent: 40m (was: 0.5h)
> Introduce TokenExchangePrincipal for extending Act claim for token_exchange
> ---------------------------------------------------------------------------
>
> Key: KNOX-3347
> URL: https://issues.apache.org/jira/browse/KNOX-3347
> Project: Apache Knox
> Issue Type: Improvement
> Components: JWT
> Reporter: Larry McCay
> Priority: Major
> Fix For: 3.0.0
>
> Time Spent: 40m
> Remaining Estimate: 0h
>
> Currently, the ActorChainPrincipal includes whatever act chain was in the
> Subject token from the token_exchange. The presence of the
> ImpersonatedPrincipal is currently only added by the identity assertion
> provider based on a doAs and proxyuser based impersonation. This is required
> for the new actor to be added to the nested 'act' claim.
> Let's add not the use of the TokenExchangePrincipal to the identity assertion
> logic that sets the ImpersonatedPrincipal in addition to the doAs pattern.
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)