[
https://issues.apache.org/jira/browse/KNOX-3438?focusedWorklogId=1039840&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-1039840
]
ASF GitHub Bot logged work on KNOX-3438:
----------------------------------------
Author: ASF GitHub Bot
Created on: 06/Sep/26 17:41
Start Date: 06/Sep/26 17:41
Worklog Time Spent: 10m
Work Description: hsheinblatt opened a new pull request, #1379:
URL: https://github.com/apache/knox/pull/1379
KNOX-3438: Add delegation policy admin API with upsert support for RFC 8693
token exchange
## What changes were proposed in this pull request?
Adds a REST admin API for managing RFC 8693 delegation policies on the
KNOXIDF_ADMIN service
under /knoxidf/admin/v1/delegation-policies. The new
DelegationPolicyResource (gateway-service-
knoxidf) exposes POST (register), GET (list or get-by-id), PUT
/{registrationId} (full-replace
update), PUT to the bare collection path (create-or-update / upsert), and
DELETE. Three DTO
classes accompany it - DelegationPolicyRequest, DelegationPolicyResponse, and
DelegationPolicyListResponse - along with a
DelegationPolicyDtoImmutabilityHelper that ensures
defensive copies of Set and Map fields on both construction and getter
access.
Changes to common code and other services:
gateway-util-common: adds ResourceType.DELEGATION_POLICY, used by audit
records emitted from
all three mutating endpoints (register, update, delete) in the same
Action.DELEGATION_LIFECYCLE
/ try-finally pattern already used by TrustedOidcIssuersResource.
gateway-server: extracts the private
isUniqueConstraintViolation(SQLException) method from
JdbcFederatedIdentityService into a new public static on JDBCUtils (the
existing JDBC-utility
class in org.apache.knox.gateway.database). JdbcFederatedIdentityService
now delegates to the
shared copy; its own private method is deleted. The extracted helper
classifies a SQLException
as a unique-constraint violation portably across Derby, MySQL, PostgreSQL,
and Oracle via
SQLState "23"-prefix matching rather than vendor-specific syntax.
gateway-spi: adds two new exception types in the delegation package -
DelegationPolicyAlreadyExistsException (thrown by register() on a
duplicate actorAuthority /
actorId pair) and DelegationPolicyNotFoundException (thrown by update()
and delete() when the
supplied registrationId does not exist). The DelegationPolicyService
interface gains a new
registerOrUpdate(DelegationPolicy) method returning RegisterOrUpdateResult
(a small value type
carrying the persisted policy and a boolean indicating whether the call
created or replaced),
and update() now returns DelegationPolicy instead of void. Two new config
keys and getters are
added to GatewayConfig / GatewayConfigImpl for configurable token TTL
bounds:
gateway.delegation.service.min.token.ttl.sec (default 60 s) and
gateway.delegation.service.max.token.ttl.sec (default 86400 s).
Service and database layer (gateway-server):
DelegationPolicyDatabase.updatePolicy() and deletePolicy() now inspect the
affected-row count
returned by the core UPDATE and DELETE statements. A count of zero causes
the transaction to be
rolled back and the call to return false, which
JdbcDelegationPolicyService converts into a
DelegationPolicyNotFoundException. This replaces the previous silent no-op
on a nonexistent
registrationId.
JdbcDelegationPolicyService.register() now wraps the database call in a
specific SQLException
catch block before the generic Exception catch. Inside it,
JDBCUtils.isUniqueConstraintViolation
is used to throw DelegationPolicyAlreadyExistsException on a genuine
duplicate rather than a
plain RuntimeException; non-duplicate SQLExceptions and all other
exceptions still produce a
plain RuntimeException.
JdbcDelegationPolicyService.registerOrUpdate() composes the existing
findByActor(),
register(), and update() methods with exactly one bounded retry per race
branch (concurrent
delete between findByActor and update, or concurrent insert between
findByActor and register).
A second race on the same actor within the same call propagates the
resulting
DelegationPolicyNotFoundException or
DelegationPolicyAlreadyExistsException to the caller
unchanged. This design adds no new SQL and no new transaction boundary. A
database-level
savepoint-based upsert was considered and rejected: savepoint semantics
differ across the
databases supported in knox in ways that are non-trivial to handle
portably.
Path ACLs and coexistence with TrustedOidcIssuersResource:
DelegationPolicyResource is a sibling of TrustedOidcIssuersResource inside
the same KNOXIDF_ADMIN
role. Both are served by KnoxIDFAdminServiceDeploymentContributor's
existing Jersey package
scan and "knoxidf/admin/**" URL pattern - no deployment-contributor
changes are required. The
two resources share the same base path prefix (knoxidf/admin/v1/) but have
distinct final
segments (trusted-oidc-issuers vs. delegation-policies), which lets
operators assign independent
PathAclsAuthz rules to each, for example:
```
<param>
<name>KNOXIDF_ADMIN.rule_issuers.path.acl</name>
<value>https://*:*/**/knoxidf/admin/v1/trusted-oidc-issuers**;awc-svc;issuer-admins;*</value>
</param>
<param>
<name>KNOXIDF_ADMIN.rule_delegation.path.acl</name>
<value>https://*:*/**/knoxidf/admin/v1/delegation-policies**;knox-operator;delegation-admins;*</value>
</param>
```
This means the two admin APIs can be administered by separate groups (or the
same group) with
no change to PathAclsAuthorizationFilter or PathAclParser. PathAclsAuthz
is what actually gates
access to both resources; the resource layer itself does not reject a null
principal but does
audit the operator as "ANONYMOUS" in that case, matching
TrustedOidcIssuersResource's precedent.
## How was this patch tested?
DelegationPolicyResourceTest (gateway-service-knoxidf, EasyMock): covers
every HTTP status
code enumerated in the error-handling contract for all five endpoint
variants. POST coverage
includes happy paths (all-defaults, all-fields round-trip, unknown-field
ignored, duplicate-
array-entry collapse via Set), boundary value tests for
actorAuthority/actorId
(missing/null/empty/whitespace), status (omitted, empty, valid, invalid,
wrong case), tokenTtlSec
(absent, at-min, at-max, below-min, above-max, negative, zero, wrong JSON
type), allowHeadless-
Exchange (omitted defaults to false, explicit true),
canActForUsers/canActForGroups combinations (both
empty rejected, each individually sufficient, both non-empty), and
resourcePolicy shapes (omitted,
empty object, empty-scope-array entry meaning all-scopes,
non-empty-scope-array, mixed). Error
paths cover 409 actor_exists, 500 storage_error, and null-principal
auditing ANONYMOUS. PUT
(upsert) tests mirror the POST validation suite and additionally cover 201
(create branch), 200
(update branch), 409/404 from the retry's second-race propagation, and
500. GET (list) covers
no-filter, actorAuthority filter, empty-string filter treated as no
filter, empty result, and
hasMore=true surfaced verbatim. GET (one) covers found, not-found (404),
and storage failure
(500). PUT /{id} covers full-replace semantics including fields reset to
defaults on omission,
server-managed fields (registrationId, createdAt, createdBy, updatedAt)
ignored when present in
the body, 404, and 500. DELETE covers 204, 404 (not the idempotent 204
that TrustedOidcIssuers-
Resource returns - a deliberate divergence since the storage layer now
surfaces not-found
explicitly), and 500. Cross-cutting tests assert ISO-8601 serialization of
Instant fields (guards
against an ObjectMapper without JavaTimeModule), and that @PostConstruct
wires both the service
and the TTL bounds from GatewayConfig.
DelegationPolicyDtoImmutabilityTest (gateway-service-knoxidf): confirms
DelegationPolicyRequest
and DelegationPolicyResponse defensively copy Set and Map fields on both
construction and getter
access, and that null inputs to DelegationPolicyDtoImmutabilityHelper are
handled cleanly.
JdbcDelegationPolicyServiceTest (gateway-server, real Derby in-memory):
adds tests for the
duplicate-detection upgrade (register() twice on the same
actorAuthority/actorId throws the
typed DelegationPolicyAlreadyExistsException with the original
SQLException in its cause chain)
and a negative counterpart (a non-duplicate storage failure - VARCHAR
overflow, SQLState 22001
- still throws plain RuntimeException, not the typed exception, confirming
isUniqueConstraintViolation does not over-match). Not-found tests for
update() and delete() on
a nonexistent registrationId, and identity-immutability tests rejecting an
actorAuthority or
actorId change on update(), verify the database-layer not-found detection.
Two real Derby-backed
tests cover registerOrUpdate()'s create branch (new actor, isCreated()
true) and update branch
(same actor called twice, same registrationId both times, isCreated()
false on the second call,
mutable fields updated, createdAt unchanged, updatedAt advances).
## Integration Tests
Integration tests will follow once the rest of the delegation flow has been
implemented.
## UI changes
N/A
Issue Time Tracking
-------------------
Worklog Id: (was: 1039840)
Remaining Estimate: 0h
Time Spent: 10m
> Delegation policy admin API for Knox IDF
> ----------------------------------------
>
> Key: KNOX-3438
> URL: https://issues.apache.org/jira/browse/KNOX-3438
> Project: Apache Knox
> Issue Type: Task
> Components: JWT
> Reporter: Harrison Sheinblatt
> Priority: Major
> Time Spent: 10m
> Remaining Estimate: 0h
>
> Implement the Admin API for managing delegation policies for knoxidf
--
This message was sent by Atlassian Jira
(v8.20.10#820010)