Skip to content

Conversation

@strantalis
Copy link
Member

Proposed Changes

Checklist

  • I have added or updated unit tests
  • I have added or updated integration tests (if appropriate)
  • I have added or updated documentation

Testing Instructions

@strantalis strantalis requested a review from a team as a code owner June 4, 2025 13:07
@github-actions github-actions bot added the size/m label Jun 4, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Hello @strantalis, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

Summary of Changes

Hello team,

Gemini here to provide a summary of this pull request. The primary goal of this PR, as indicated by the title, is to optimize the Keycloak integration within the Entity Resolution Service (ERS). Previously, the service would perform a client login to Keycloak to obtain an access token on potentially every request that required interaction with Keycloak. This PR changes that behavior to only request a new token when the currently cached token is nil, expired, or nearing its expiration.

This is achieved by adding state to the ERS service struct to hold the Keycloak connector and its expiration time, protected by a mutex for concurrent access. A new internal method getConnector is introduced to manage the token lifecycle, fetching a new token only when necessary. The existing handler functions (ResolveEntities, CreateEntityChainFromJwt, CreateEntityChainsFromTokens) and helper functions now utilize this cached connector instance.

Highlights

  • Keycloak Token Caching: The Keycloak connector and its access token are now cached within the ERS service instance. A mutex is used to ensure thread-safe access to the connector.
  • Token Refresh Logic: A new getConnector method handles retrieving the connector. It checks if the current token is valid and not nearing expiration (with a 60-second buffer). If the token is missing or expiring, it performs a client login to fetch a new one.
  • Refactored Keycloak Interaction: The functions that interact with Keycloak (EntityResolution, CreateEntityChainFromJwt, CreateEntityChainsFromTokens, getEntitiesFromToken, getServiceAccountClient) have been updated to accept the cached connector instance as a parameter, removing the need for them to repeatedly call the old getKCClient function.
  • Removed Redundant Login Logic: The getKCClient function, which previously performed a Keycloak client login on every call, has been removed as its functionality is now integrated into the getConnector method.
  • Test Updates: Unit tests have been updated to reflect the changes in function signatures, passing a mock or initialized connector where required. The test package name was also changed from keycloak_test to keycloak to allow access to internal types for testing.

Changelog

Click here to see the changelog
  • service/entityresolution/keycloak/entity_resolution.go
    • Added sync and time imports (lines 11-12).
    • Added connector and connectorMu fields to KeycloakEntityResolutionService struct (lines 49-50).
    • Changed receiver for ResolveEntities and CreateEntityChainFromJwt methods to pointer (*KeycloakEntityResolutionService) (lines 73, 86).
    • Added connector field to KeyCloakConnector struct (line 124).
    • Added connector parameter to CreateEntityChainFromJwt function (line 131).
    • Added connector parameter to EntityResolution function (line 148).
    • Removed the getKCClient function (lines 337-364).
    • Added connector parameter to getEntitiesFromToken function (line 373).
    • Added connector parameter to getServiceAccountClient function (line 441).
    • Added getConnector method to KeycloakEntityResolutionService for managing the cached Keycloak connection and token refresh (lines 478-513).
  • service/entityresolution/keycloak/entity_resolution_test.go
    • Changed package name from keycloak_test to keycloak (line 1).
    • Removed explicit import alias for keycloak package (line 15).
    • Updated return types of testKeycloakConfig and testKeycloakConfigInferID to use local types (KeycloakConfig, InferredIdentityConfig, EntityImpliedFrom) (lines 78, 89, 96).
    • Initialized KeyCloakConnector instances in various test functions (Test_KCEntityResolutionByClientId, Test_KCEntityResolutionByEmail, Test_KCEntityResolutionByUsername, Test_KCEntityResolutionByGroupEmail, Test_KCEntityResolutionNotFoundError, Test_JwtClientAndUsernameClientCredentials, Test_JwtClientAndUsernamePasswordPub, Test_JwtClientAndUsernamePasswordPriv, Test_JwtClientAndUsernameAuthPub, Test_JwtClientAndUsernameAuthPriv, Test_JwtClientAndUsernameImplicitPub, Test_JwtClientAndUsernameImplicitPriv, Test_JwtClientAndClientTokenExchange, Test_JwtMultiple, Test_KCEntityResolutionNotFoundInferEmail, Test_KCEntityResolutionNotFoundInferClientId, Test_KCEntityResolutionNotFoundNotInferUsername) (e.g., lines 159-162).
    • Passed the initialized connector to calls to EntityResolution and CreateEntityChainFromJwt (e.g., lines 164, 341).
    • Updated error message comparison in Test_KCEntityResolutionNotFoundError and Test_KCEntityResolutionNotFoundNotInferUsername to use local ErrGetRetrievalFailed (lines 321, 322, 652, 653).
  • service/entityresolution/keycloak/v2/entity_resolution.go
    • Added sync and time imports (lines 11-12).
    • Added connector and connectorMu fields to EntityResolutionServiceV2 struct (lines 48-49).
    • Changed receiver for ResolveEntities and CreateEntityChainsFromTokens methods to pointer (*EntityResolutionServiceV2) (lines 72, 83).
    • Added expiresAt field to Connector struct (line 120).
    • Added connector parameter to CreateEntityChainsFromTokens function (line 127).
    • Added connector parameter to EntityResolution function (line 144).
    • Removed the getKCClient function (lines 337-364).
    • Added connector parameter to getEntitiesFromToken function (line 369).
    • Added connector parameter to getServiceAccountClient function (line 437).
    • Added getConnector method to EntityResolutionServiceV2 for managing the cached Keycloak connection and token refresh (lines 474-509).
  • service/entityresolution/keycloak/v2/entity_resolution_test.go
    • Changed package name from keycloak_test to keycloak (line 1).
    • Removed explicit import alias for keycloak package (line 15).
    • Updated return types of testConfig and testConfigInferID to use local types (Config, InferredIdentityConfig, EntityImpliedFrom) (lines 78, 89, 96).
    • Initialized Connector instances in various test functions (Test_KCEntityResolutionByClientId, Test_KCEntityResolutionByEmail, Test_KCEntityResolutionByUsername, Test_KCEntityResolutionByGroupEmail, Test_KCEntityResolutionNotFoundError, Test_JwtClientAndUsernameClientCredentials, Test_JwtClientAndUsernamePasswordPub, Test_JwtClientAndUsernamePasswordPriv, Test_JwtClientAndUsernameAuthPub, Test_JwtClientAndUsernameAuthPriv, Test_JwtClientAndUsernameImplicitPub, Test_JwtClientAndUsernameImplicitPriv, Test_JwtClientAndClientTokenExchange, Test_JwtMultiple, Test_KCEntityResolutionNotFoundInferEmail, Test_KCEntityResolutionNotFoundInferClientId, Test_KCEntityResolutionNotFoundNotInferUsername) (e.g., lines 160-163).
    • Passed the initialized connector to calls to EntityResolution and CreateEntityChainsFromTokens (e.g., lines 164, 340).
    • Updated error message comparison in Test_KCEntityResolutionNotFoundError and Test_KCEntityResolutionNotFoundNotInferUsername to use local ErrGetRetrievalFailed (lines 321, 322, 643, 644).
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.


A token in the cache,
Saves a network dash.
Check time, then renew,
Efficiency is true.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an important optimization for the Keycloak Entity Resolution Service by caching the authentication token and only refreshing it when it's nil, expired, or nearing expiration. This should significantly reduce the number of login requests to Keycloak, improving performance and reducing load.

The core logic in the new getConnector method appears sound, incorporating a mutex for thread-safe access and a refresh buffer for proactive token renewal. The changes are consistently applied across both the v1 and v2 ERS implementations, and method receivers have been correctly updated to pointers to manage the shared connector state.

While the existing tests have been adapted to the new function signatures, the PR description is quite minimal. Providing more details on the changes and testing strategy would be helpful for reviewers.

Overall, this is a valuable improvement. My main suggestion revolves around enhancing test coverage for the new token management logic, as detailed in the specific comment.

Summary of Findings

  • Token Caching and Refresh Logic: The PR successfully implements token caching and refresh logic in the getConnector method, only requesting new tokens when necessary. This includes mutex protection for concurrent access and a refresh buffer.
  • Method Receiver Updates: Service methods (ResolveEntities, CreateEntityChainFromJwt, etc.) were correctly changed to use pointer receivers (*KeycloakEntityResolutionService) to allow modification of the internal connector state.
  • Test Adaptations: Existing unit tests were updated to pass the new KeyCloakConnector / Connector instance to helper functions, accommodating the refactoring.
  • Test Coverage for getConnector: The new getConnector method, which contains the core token management logic (caching, expiry checks, refresh), is not unit-tested in isolation. Adding dedicated tests for this method would improve robustness and maintainability. (Commented with 'medium' severity)
  • Test Package Renaming (Not Commented - Low Severity): Test packages were renamed (e.g., keycloak_test to keycloak), shifting from a black-box to a white-box testing style. This is an acceptable change, likely to simplify testing of internal components.

Merge Readiness

The pull request introduces a significant and valuable optimization for token handling. The core implementation is well done. However, to ensure the robustness of the new token management logic in getConnector, I recommend adding dedicated unit tests for this critical component as suggested in the review comments. Addressing this medium severity testing concern would increase confidence in the change. Therefore, I suggest these changes be considered before merging. As an AI reviewer, I am not authorized to approve pull requests.

@strantalis strantalis changed the title fix(ers): only request a token when near expiration fix: only request a token when near expiration Jun 4, 2025
jakedoublev
jakedoublev previously approved these changes Jun 4, 2025
jakedoublev
jakedoublev previously approved these changes Jun 4, 2025
Copy link
Contributor

@jakedoublev jakedoublev left a comment

Choose a reason for hiding this comment

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

Not worth blocking this, but you could rely on the s.idpConfig.TokenBuffer state within s.getConnector instead of passing it as an argument to each call.

@strantalis strantalis enabled auto-merge June 4, 2025 16:55
@strantalis strantalis added this pull request to the merge queue Jun 6, 2025
Merged via the queue into opentdf:main with commit 556d95e Jun 6, 2025
28 checks passed
@strantalis strantalis deleted the ers-token-reuse branch June 6, 2025 16:27
@strantalis
Copy link
Member Author

/backport

@opentdf-automation
Copy link
Contributor

Successfully created backport PR for release/service/v0.6:

opentdf-automation bot pushed a commit that referenced this pull request Jun 6, 2025
### Proposed Changes

*

### Checklist

- [ ] I have added or updated unit tests
- [ ] I have added or updated integration tests (if appropriate)
- [ ] I have added or updated documentation

### Testing Instructions

(cherry picked from commit 556d95e)
opentdf-automation bot added a commit that referenced this pull request Jun 6, 2025
### Proposed Changes

*

### Checklist

- [ ] I have added or updated unit tests
- [ ] I have added or updated integration tests (if appropriate)
- [ ] I have added or updated documentation

### Testing Instructions

(cherry picked from commit 556d95e)
strantalis pushed a commit that referenced this pull request Jun 6, 2025
…ervice/v0.6] (#2408)

# Description
Backport of #2370 to `release/service/v0.6`.

Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com>
github-merge-queue bot pushed a commit that referenced this pull request Jun 24, 2025
🤖 I have created a release *beep* *boop*
---


##
[0.7.0](service/v0.6.0...service/v0.7.0)
(2025-06-24)


### ⚠ BREAKING CHANGES

* **policy:** disable kas grants in favor of key mappings
([#2220](#2220))

### Features

* **authz:** Add caching to keycloak ERS
([#2466](#2466))
([f5b0a06](f5b0a06))
* **authz:** auth svc registered resource GetDecision support
([#2392](#2392))
([5405674](5405674))
* **authz:** authz v2 GetBulkDecision
([#2448](#2448))
([0da3363](0da3363))
* **authz:** cache entitlement policy within authorization service
([#2457](#2457))
([c16361c](c16361c))
* **authz:** ensure logging parity between authz v2 and v1
([#2443](#2443))
([ef68586](ef68586))
* **core:** add cache manager
([#2449](#2449))
([2b062c5](2b062c5))
* **core:** consume RPC interceptor request context metadata in logging
([#2442](#2442))
([2769c48](2769c48))
* **core:** DSPX-609 - add cli-client to keycloak provisioning
([#2396](#2396))
([48e7489](48e7489))
* **core:** ERS cache setup, fix cache initialization
([#2458](#2458))
([d0c6938](d0c6938))
* inject logger and cache manager to key managers
([#2461](#2461))
([9292162](9292162))
* **kas:** expose provider config from key details.
([#2459](#2459))
([0e7d39a](0e7d39a))
* **main:** Add Close() method to cache manager
([#2465](#2465))
([32630d6](32630d6))
* **policy:** disable kas grants in favor of key mappings
([#2220](#2220))
([30f8cf5](30f8cf5))
* **policy:** Restrict deletion of pc with used key.
([#2414](#2414))
([3b40a46](3b40a46))
* **sdk:** allow Connect-Protocol-Version RPC header for cors
([#2437](#2437))
([4bf241e](4bf241e))


### Bug Fixes

* **core:** remove generics on new platform cache manager and client
([#2456](#2456))
([98c3c16](98c3c16))
* **core:** replace opentdf-public client with cli-client
([#2422](#2422))
([fb18525](fb18525))
* **deps:** bump github.com/casbin/casbin/v2 from 2.106.0 to 2.107.0 in
/service in the external group
([#2416](#2416))
([43afd48](43afd48))
* **deps:** bump github.com/opentdf/platform/protocol/go from 0.4.0 to
0.5.0 in /service
([#2470](#2470))
([3a73fc9](3a73fc9))
* **deps:** bump github.com/opentdf/platform/sdk from 0.4.7 to 0.5.0 in
/service ([#2473](#2473))
([ad37476](ad37476))
* **deps:** bump the external group across 1 directory with 2 updates
([#2450](#2450))
([9d8d1f1](9d8d1f1))
* **deps:** bump the external group across 1 directory with 2 updates
([#2472](#2472))
([d45b3c8](d45b3c8))
* only request a token when near expiration
([#2370](#2370))
([556d95e](556d95e))
* **policy:** fix casing bug and get provider config on update.
([#2403](#2403))
([a52b8f9](a52b8f9))
* **policy:** properly formatted pem in test fixtures
([#2409](#2409))
([54ffd23](54ffd23))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants