Skip to content

fix(ers): preserve resolved token-chain context - #3808

Open
jrschumacher wants to merge 4 commits into
mainfrom
fix/3801-chain-no-rehydrate
Open

fix(ers): preserve resolved token-chain context#3808
jrschumacher wants to merge 4 commits into
mainfrom
fix/3801-chain-no-rehydrate

Conversation

@jrschumacher

@jrschumacher jrschumacher commented Jul 31, 2026

Copy link
Copy Markdown
Member

Summary

  • preserve final mapped multi-strategy token-chain payloads in v2 entity chains instead of projecting to lossy typed identities
  • consume only marked pre-resolved token-chain claims directly in authz, while keeping explicit entity-chain resolution on the normal ERS ResolveEntities path
  • add access-layer regression tests, multi-strategy integration coverage, and token-based BDD coverage for the no-rehydrate flow

Testing

  • cd service && golangci-lint run ./entityresolution/multi-strategy/... ./internal/access/v2 ./entityresolution/integration
  • cd service && go test ./entityresolution/multi-strategy/... ./internal/access/v2 ./entityresolution/integration -run "Test(ResolveEntities_ClaimsProviderUsesInlineClaimsContext|ResolveEntities_UserNameEntityDoesNotSeedClaimsContext|EntityRepresentationFromClaimsEntity_RequiresPreResolvedPrefix|ResolveEntitiesFromEntityChain_UsesPreResolvedClaimsEntitiesDirectly|MultiStrategy_ClaimsOnly|MultiStrategy_SQLOnly|MultiStrategyEntityResolutionV2|Integration_CreateEntityChainsFromTokens_PreservesLDAPAttributesInChain|Integration_ResolveEntities_ReturnsPopulatedRepresentation|Integration_TokenChainPreservesResolvedClaims|Integration_FailureIsolation_MixedBatch)" -count=1
  • go test ./tests-bdd/cukes -run "TestConvertInterfaceToAny_PlainClaimsJSON" -count=1

Notes

  • Full local Godog execution is currently blocked in this environment by existing local platform startup/port issues unrelated to these code changes.

Fixes #3801

Summary by CodeRabbit

  • New Features

    • Preserved resolved identity claims throughout entity chains, including username, email, and client identifiers.
    • Added support for applying a specifically selected resolution strategy.
    • Enabled authorization decisions directly from resolved token chains when claims are available.
  • Bug Fixes

    • Improved fallback handling when token-chain claims require additional resolution.
    • Excluded internal resolution metadata from exposed entity claims.
    • Added validation for required claims and clearer handling of invalid or missing entity data.
    • Rejects claims that cannot be serialized safely.
  • Tests

    • Added coverage for multi-strategy authorization, claim preservation, filtering, and token-based decisions.

@jrschumacher
jrschumacher requested a review from a team as a code owner July 31, 2026 07:52
@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 166.463788ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 91.241094ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 372.844193ms
Throughput 268.21 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 45.230601093s
Average Latency 450.656692ms
Throughput 110.54 requests/second

@jrschumacher
jrschumacher force-pushed the fix/3801-chain-no-rehydrate branch from 77907ff to 9febb1d Compare July 31, 2026 08:02
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c46142d4-596d-4730-a740-52399273ea3e

📥 Commits

Reviewing files that changed from the base of the PR and between 4ee44ad and 00b74da.

📒 Files selected for processing (3)
  • service/entityresolution/multi-strategy/v2/registration.go
  • service/entityresolution/multi-strategy/v2/registration_test.go
  • service/internal/access/v2/just_in_time_pdp_test.go

📝 Walkthrough

Walkthrough

The change preserves resolved claims in multi-strategy token entity chains. It adds explicit strategy execution, claims-based entity serialization, direct PDP conversion with hydration fallback, expanded contract validation, and token-based BDD authorization coverage.

Changes

Entity resolution and chain preservation

Layer / File(s) Summary
Selected strategy claim entities
service/entityresolution/multi-strategy/service.go, service/entityresolution/multi-strategy/v2/registration.go, service/entityresolution/multi-strategy/*_test.go
Selected strategies execute directly. Token-derived entities preserve normalized claims in protobuf Struct values and exclude resolution metadata. Serialization failures return errors.
Entity-chain claims contract
service/entityresolution/integration/internal/contract_tests.go, service/entityresolution/integration/multistrategy*_test.go
Contracts and integration tests validate claims entity types, required fields, decoded claims, and preserved username and email values.
Resolved-chain PDP conversion
service/internal/access/v2/just_in_time_pdp.go, service/internal/access/v2/just_in_time_pdp_test.go
PDP converts resolved chain claims into entity representations. Missing claims trigger fallback resolution. Tests cover filtering, hydration, and typed-entity rejection.
Token authorization coverage
tests-bdd/cukes/steps_authorization.go, tests-bdd/features/multi-strategy-ers-token.feature
BDD steps obtain Keycloak tokens and submit v2 token decisions. Scenarios verify permit and deny decisions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ERS
  participant EntityChain
  participant PDP
  participant PolicyDecision
  Client->>ERS: CreateEntityChainsFromTokens
  ERS->>EntityChain: store selected-strategy claims
  Client->>PDP: submit token decision
  PDP->>EntityChain: inspect claims
  EntityChain-->>PDP: entity representations
  PDP->>PolicyDecision: evaluate authorization
  PolicyDecision-->>Client: PERMIT or DENY
Loading

Possibly related PRs

Suggested labels: comp:authorization

Suggested reviewers: dmihalcik-virtru, pflynn-virtru

Poem

A rabbit packs claims in a chain,
Struct values preserve each gain.
Strategies hold their course,
PDP uses the source,
Permit and deny run plain.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: preserving resolved token-chain context in ERS.
Linked Issues check ✅ Passed The changes preserve resolved claims, execute selected strategies directly, avoid unnecessary hydration, and add regression coverage for token-chain authorization.
Out of Scope Changes check ✅ Passed The implementation and tests are directly related to preserving ERS token-chain context and validating no-rehydrate authorization flows.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/3801-chain-no-rehydrate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 207.422309ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 109.009937ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 0
Failed Decrypts 100
Total Time 273.290013ms
Throughput 0.00 requests/second

Error Summary

Error Message Occurrences
splitKey.unable to reconstruct split key: map[{https://localhost:8080 }:tdf: rewrap request 403
kao unwrap failed for split {https://localhost:8080 }: permission_denied: request error
rpc error: code = PermissionDenied desc = forbidden: pdp-denied]
tdf: rewrap request 403
kao unwrap failed for split {https://localhost:8080 }: permission_denied: request error
rpc error: code = PermissionDenied desc = forbidden: pdp-denied 100 occurrences

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 0
Failed Requests 5000
Concurrent Requests 50
Total Time 40.88377479s
Throughput 0.00 requests/second

Error Summary:

Error Message Occurrences
read error: reader.WriteTo failed: splitKey.unable to reconstruct split key: map[{https://localhost:8080 }:tdf: rewrap request 403
kao unwrap failed for split {https://localhost:8080 }: permission_denied: request error
rpc error: code = PermissionDenied desc = forbidden: pdp-denied]
tdf: rewrap request 403
kao unwrap failed for split {https://localhost:8080 }: permission_denied: request error
rpc error: code = PermissionDenied desc = forbidden: pdp-denied
5000 occurrences

@jrschumacher jrschumacher changed the title fix(entityresolution): preserve resolved token-chain context fix(ers): preserve resolved token-chain context Jul 31, 2026
@jrschumacher
jrschumacher force-pushed the fix/3801-chain-no-rehydrate branch from 9febb1d to afd0ac4 Compare July 31, 2026 08:16
@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 210.349164ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 110.191894ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 437.175012ms
Throughput 228.74 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 54.826904906s
Average Latency 546.346572ms
Throughput 91.20 requests/second

Comment thread service/entityresolution/multi-strategy/v2/registration.go
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 204.598201ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 107.988282ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 452.990155ms
Throughput 220.76 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 51.906947226s
Average Latency 517.553887ms
Throughput 96.33 requests/second

strantalis
strantalis previously approved these changes Aug 4, 2026
@jrschumacher
jrschumacher dismissed strantalis’s stale review August 4, 2026 18:57

The merge-base changed after approval.

@jrschumacher
jrschumacher force-pushed the fix/3801-chain-no-rehydrate branch from cd00177 to d3a4357 Compare August 4, 2026 18:58
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 223.257761ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 109.981501ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 436.608908ms
Throughput 229.04 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 49.866106197s
Average Latency 496.847612ms
Throughput 100.27 requests/second

khvirtru
khvirtru previously approved these changes Aug 4, 2026

@khvirtru khvirtru left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

strantalis
strantalis previously approved these changes Aug 4, 2026
Comment thread service/entityresolution/integration/internal/contract_tests.go
elizabethhealy
elizabethhealy previously approved these changes Aug 4, 2026
Base automatically changed from fix/3790-resolve-entities-jwt-claims-context to main August 4, 2026 22:19
@jrschumacher
jrschumacher dismissed stale reviews from elizabethhealy and strantalis August 4, 2026 23:23

The merge-base changed after approval.

elizabethhealy
elizabethhealy previously approved these changes Aug 5, 2026
jrschumacher and others added 2 commits August 5, 2026 09:41
Signed-off-by: Ryan Schumacher <jschumacher@virtru.com>
Signed-off-by: Ryan Schumacher <j.r.schumacher@gmail.com>
@jrschumacher
jrschumacher force-pushed the fix/3801-chain-no-rehydrate branch from d3a4357 to 345a5aa Compare August 5, 2026 14:42
@policy-bot-opentdf
policy-bot-opentdf Bot dismissed stale reviews from khvirtru August 5, 2026 14:42

Invalidated by push of 345a5aa

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 200.151637ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 104.964118ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 440.628129ms
Throughput 226.95 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 53.690480412s
Average Latency 535.220545ms
Throughput 93.13 requests/second

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@service/entityresolution/integration/multistrategy_comprehensive_test.go`:
- Around line 922-934: Update chainEntityClaimsMap to fail the test immediately
when ent.GetClaims() returns nil, using t.Fatalf with a clear message that the
entity has no claims; remove the empty-map return while preserving the existing
claims unmarshalling and map conversion behavior.
- Around line 1049-1051: Replace the fixed time.Sleep after container startup
with a readiness wait using wait.ForExec that runs an ldapsearch against
ou=users,dc=opentdf,dc=test and retries until the LDAP bootstrap is complete.
Apply the wait before tests search the directory, preserving the existing
container-start flow while removing the unconditional five-second delay.

In `@service/entityresolution/multi-strategy/service_test.go`:
- Around line 58-60: Extend the assertions in the ResolveEntityWithStrategy test
after the strategy_name check to validate
result.Metadata["attempted_strategies"] is exactly a []interface{} containing
only "client_strategy". This must verify both the collection type and its single
selected-strategy value.

In `@service/entityresolution/multi-strategy/service.go`:
- Around line 157-165: Extract the duplicated strategy metadata population into
a shared helper, preserving the default failure strategy and the
structpb-compatible []interface{} value for attempted_strategies. Update both
ResolveEntity and ResolveEntityWithStrategy to call this helper instead of
maintaining separate metadata blocks, while retaining each method’s existing
strategy inputs.

In `@service/entityresolution/multi-strategy/v2/registration.go`:
- Around line 332-361: Update createEntityFromResultV2 to return
(*entity.Entity, error) and propagate failures from structpb.NewStruct and
anypb.New instead of substituting fallback claims or discarding errors. Update
its caller in createEntityChainFromSingleTokenV2 to handle and return the error
so CreateEntityChainsFromTokens uses its existing fail-safe path and does not
append a partial entity.

In `@service/internal/access/v2/just_in_time_pdp_test.go`:
- Around line 58-124: Extend the tests around resolveEntitiesFromToken to cover
a claims-based ERS response returned directly from the token-chain path. Assert
that the result contains the expected AdditionalProps,
CreateEntityChainsFromTokens is called exactly once, and ResolveEntities is
never called, preserving the no-rehydrate behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e46dbdde-ae94-44fb-8386-be621bd1a53a

📥 Commits

Reviewing files that changed from the base of the PR and between 4a98105 and 345a5aa.

📒 Files selected for processing (12)
  • service/entityresolution/integration/internal/contract_tests.go
  • service/entityresolution/integration/multistrategy_comprehensive_test.go
  • service/entityresolution/integration/multistrategy_test.go
  • service/entityresolution/integration/multistrategy_v2_serialization_test.go
  • service/entityresolution/multi-strategy/service.go
  • service/entityresolution/multi-strategy/service_test.go
  • service/entityresolution/multi-strategy/v2/registration.go
  • service/entityresolution/multi-strategy/v2/registration_test.go
  • service/internal/access/v2/just_in_time_pdp.go
  • service/internal/access/v2/just_in_time_pdp_test.go
  • tests-bdd/cukes/steps_authorization.go
  • tests-bdd/features/multi-strategy-ers-token.feature

Comment thread service/entityresolution/integration/multistrategy_comprehensive_test.go Outdated
Comment thread service/entityresolution/multi-strategy/service_test.go
Comment thread service/entityresolution/multi-strategy/service.go Outdated
Comment thread service/entityresolution/multi-strategy/v2/registration.go
Comment thread service/internal/access/v2/just_in_time_pdp_test.go
strantalis
strantalis previously approved these changes Aug 5, 2026
Signed-off-by: Ryan Schumacher <jschumacher@virtru.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 196.459277ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 106.058265ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 449.201564ms
Throughput 222.62 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 48.967959616s
Average Latency 486.383009ms
Throughput 102.11 requests/second

Signed-off-by: Ryan Schumacher <jschumacher@virtru.com>
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 162.173505ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 74.779431ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 342.001173ms
Throughput 292.40 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 38.655782869s
Average Latency 385.111037ms
Throughput 129.35 requests/second

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • examples
  • otdfctl
  • sdk
  • service
  • lib/fixtures
  • tests-bdd

See the workflow run for details.

@jrschumacher
jrschumacher added this pull request to the merge queue Aug 6, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ers): preserve multi-strategy routing context across v2 entity-chain resolution

4 participants