hsheinblatt opened a new pull request, #1398:
URL: https://github.com/apache/knox/pull/1398

   KNOX-3441 - Add delegated token exchange (actor-token and headless) with 
policy enforcement and audit
   
   ## What changes were proposed in this pull request?
   
   Implements RFC 8693 delegated token exchange in TokenExchangeHandler:
   a request is classified as same-subject (dispatched unchanged) or a
   delegation attempt, expressed either interactively via actor_token or,
   headlessly, via a requested_subject that differs from the subject_token's
   own subject. All delegation behavior is gated behind five new topology
   flags on JWTFederationFilter (delegation.server.enabled,
   delegation.requested.subject.enabled, 
delegation.enforce.requested.audience.required,
   delegation.enforce.requested.audience.exactly.one, 
delegation.requested.scope.enabled),
   each defaulting to false and resolved once at init() alongside the filter's
   existing DelegationPolicyService resolution, exposed to TokenExchangeHandler
   through a new evaluateDelegationPolicy() callback so the handler retains no
   direct GatewayServices/GatewayConfig access.
   
   Delegation requests are rejected uniformly when delegation.server.enabled is
   off, and the invalid combination of actor_token present with a differing
   requested_subject is rejected once the gate is on. For headless delegation,
   createSubjectForHeadlessDelegation() treats the subject_token's identity as
   the actor (PrimaryPrincipal) and requested_subject as the impersonated
   party, carried via TokenExchangePrincipal with a null subjectIssuer; no
   ActorChainPrincipal is added, since subject_token here is the actor's own
   token, not the impersonated user's. requested_subject is format-validated
   (length bound, no control characters) and, per flag, audience/resource
   values can be required (at least one) and/or capped (at most one distinct
   combined audience/resource value).
   
   DelegationPolicyService.evaluate() is called exactly once for an otherwise
   valid delegated exchange, for both the actor_token and requested_subject
   forms. The actor identity passed to the policy check is derived from the
   actor's validated JWT: a Kubernetes service-account subject
   (system:serviceaccount:<namespace>:<sa-name>) is tagged K8S_SA with actorId
   "<issuer>:<namespace>:<sa-name>" (avoiding collisions across namespaces/
   clusters); every other subject is tagged USER with the subject value itself
   as actorId. A policy denial maps to HTTP 400 invalid_request with a generic
   description that does not identify which value or scope failed; the
   pre-existing canActFor.groups UnsupportedOperationException still maps to
   HTTP 501, unaffected. Requested scopes are passed through as empty for now;
   scope parsing/validation is deferred. Actor-chain depth is parsed but not
   yet capped, and PolicyDecision's effectiveTtlSec is computed but not yet
   used to override the issued token's TTL -- both deferred to later work.
   
   PolicyCheckRequest.requestedResource (String) became requestedResources
   (Set<String>, same null-safe defensive-copy pattern as requestedScopes), and
   JdbcDelegationPolicyService.evaluate() now checks membership and scope
   compliance independently per requested resource. A request with scopes but
   no resources is denied with "scope_not_allowed" (a scope can only be
   validated against a specific resource's allowed set); a request with
   neither resources nor scopes imposes no resource/scope restriction.
   
   Added a new Action.TOKEN_EXCHANGE audit action, emitted for both outcomes at
   the point evaluate() returns, with a ResourceType.PRINCIPAL resourceName of
   "<actorAuthority>/<actorId>" and a message carrying the deny reason (when
   denied), the actor identity, the subject token's issuer/subject, the
   requested_subject (when present), and the requested resources. The minted
   token's jti/expiry/subject are not included (unavailable at the
   policy-decision point) but the audit record can be correlated with the
   pre-existing token-minting log line via the request's trace_id/reqID.
   
   Also fixes a stale PolicyDecision.getDenyReason() javadoc reference to
   invalid_grant (should read invalid_request).
   
   
   ## How was this patch tested?
   
   - JWTFederationFilterDelegationConfigTest (new): each of the five delegation
     flags defaults to false, is independently configurable, and all five can
     be enabled together; evaluateDelegationPolicy() delegates to the resolved
     DelegationPolicyService. TestFilterConfig extended with a
     DelegationPolicyService constructor overload for this.
   - TokenExchangeHandlerTest: extensive new coverage for the delegation gate
     (on/off, actor_token vs requested_subject, blank/whitespace
     requested_subject treated as absent), the actor_token/requested_subject
     conflict rejection, headless Subject construction (principal shape, act
     claim on subject_token not carried forward, requested_issuer boundary
     values), requested_subject format validation (control character,
     overlong, at-the-bound), audience required/max-one enforcement (including
     blank-value and same-value-dedupe cases) for both delegation forms and all
     combined-flag cells, actor-identity derivation for both K8S_SA and USER
     outcomes, policy-check routing and requested-resources population for
     both delegation forms (and confirmation same-subject exchanges never
     invoke policy evaluation), HTTP 400/501 error mapping, and audit emission
     end-to-end (FAILURE per delegation form, SUCCESS per actor-authority
     value).
   - JdbcDelegationPolicyServiceTest: updated all pre-existing evaluate() tests
     to the multi-resource constructor shape and added cases for multi-resource
     authorization/denial, per-resource scope enforcement, and the
     empty-requestedResources/requestedScopes interaction cases.
   - PolicyCheckRequestTest: updated to the new constructor shape and added
     coverage for the requestedResources field's defensive copy and
     post-construction immutability.
   - DelegationPolicyServiceFactoryTest: updated to the new constructor shape.
   - Pre-existing TokenExchangeHandlerTest and 
JWTFederationFilterTokenExchangeTest
     tests that send an actor_token now set delegation.server.enabled=true so
     they keep passing with the gate defaulting to reject delegation.
   
   ## Integration Tests
   Integration tests will be added as a follow on
   
   ## UI changes
   N/A
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to