Skip to content

Add caching support for IdentityProviderStorageProvider.getForLogin operations#2

Open
ShashankFC wants to merge 1 commit into
feature-idp-cache-baselinefrom
feature-idp-cache-implementation
Open

Add caching support for IdentityProviderStorageProvider.getForLogin operations#2
ShashankFC wants to merge 1 commit into
feature-idp-cache-baselinefrom
feature-idp-cache-implementation

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Jan 5, 2026

Copy link
Copy Markdown
Collaborator

Test 2

Summary by CodeRabbit

  • New Features

    • Implemented caching mechanism for identity provider login retrieval to improve performance.
  • Bug Fixes

    • Enhanced identity provider filtering to enforce enabled status and organization visibility requirements.
    • Improved handling of organization linkage and broker public accessibility in login scenarios.
  • Tests

    • Added comprehensive test coverage for identity provider login caching behavior.

✏️ Tip: You can customize this high-level summary in your review settings.


Replicated from ai-code-review-evaluation/keycloak-coderabbit#2

Closes #32573

Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
@ShashankFC

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 9, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jan 9, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces login-aware caching for identity providers in Keycloak's Infinispan cache layer. New methods enable per-login cache key generation and retrieval with fallback logic, nuanced invalidation handling for cache consistency, and enhanced filtering to enforce enabled status and organization constraints.

Changes

Cohort / File(s) Summary
Core Login Cache Implementation
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java
Added cacheKeyForLogin() for login-specific cache key generation and getForLogin() for cache-aware retrieval with delegate fallback. Introduced registerIDPLoginInvalidation() and registerIDPLoginInvalidationOnUpdate() to handle nuanced cache invalidation based on login-eligibility and organization linkage changes. Added IDP_LOGIN_SUFFIX constant.
Provider Interface & Filtering
server-spi/src/main/java/org/keycloak/models/IdentityProviderStorageProvider.java, services/src/main/java/org/keycloak/organization/forms/login/freemarker/model/OrganizationAwareIdentityProviderBean.java
Enhanced login predicates with organization/broker-public constraints and enforced isEnabled() checks on IDPs in both ORG_ONLY and ALL fetch modes to narrow login-eligible provider list.
Test Coverage
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java
Added comprehensive test testCacheIDPForLogin() validating login cache population, invalidation scenarios (IDP creation, removal, configuration updates, organization linking), and cache integrity across FetchMode variants.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant InfinispanCache as Infinispan Cache
    participant IDPProvider as IDPStorageProvider
    participant Delegate as Delegate Storage

    Client->>IDPProvider: getForLogin(fetchMode, orgId)
    IDPProvider->>InfinispanCache: retrieve with cacheKeyForLogin()
    
    alt Cache Hit
        InfinispanCache-->>IDPProvider: cached IDPs
        IDPProvider-->>Client: return cached result
    else Cache Miss
        IDPProvider->>Delegate: getForLogin(fetchMode, orgId)
        Delegate-->>IDPProvider: IDPs from delegate
        IDPProvider->>InfinispanCache: store in login cache
        IDPProvider-->>Client: return result
    end
Loading
sequenceDiagram
    participant Admin
    participant IDPProvider as IDPStorageProvider
    participant Cache as Login Cache
    
    Admin->>IDPProvider: update IDP (login-eligibility change)
    IDPProvider->>IDPProvider: registerIDPLoginInvalidationOnUpdate()
    
    alt Login-eligibility Changed
        IDPProvider->>Cache: invalidate login caches
        Note over Cache: caches cleared for affected FetchModes
    else Only Config Changed
        Note over IDPProvider: no invalidation needed
        Cache-->>Cache: remains valid
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰✨ A login cache swift and true,
Keys by fetch and realm brand new,
Invalidation's dance so keen,
Filters crisp and enabled clean—
Hopping faster through the queue! 🏃‍♂️💨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is minimal and vague ('Test 2'), failing to explain the changes, objectives, or rationale required by the repository's contribution guidelines. Add a comprehensive description following the repository's template, including what was changed, why, and how it addresses the referenced issue #32573.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change—adding caching support for getForLogin operations on IdentityProviderStorageProvider—which is reflected in the code changes across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

@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: 2

🤖 Fix all issues with AI agents
In
@testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java:
- Around line 380-381: The cleanup registration uses a hardcoded "alias" string
so removals fail; change the cleanup to reference the actual identity provider
alias from the created representation (e.g., use idpRep.getAlias() or capture a
final String alias = idpRep.getAlias()) when calling get(...) in the lambda
added via getCleanup().addCleanup(...) so the removal targets the real provider
created by testRealm().identityProviders().create(idpRep).close().
- Around line 424-425: The cleanup currently uses a hardcoded "alias" when
registering removal: replace the literal "alias" with the actual identity
provider alias used when creating the provider (e.g., use idpRep.getAlias() or
the local alias variable) so getCleanup().addCleanup(...) calls
testRealm().identityProviders().get(actualAlias)::remove; ensure the same alias
value passed to testRealm().identityProviders().create(idpRep) is used for the
subsequent get(...) call.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ff9e91 and 3e59772.

📒 Files selected for processing (4)
  • model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java
  • server-spi/src/main/java/org/keycloak/models/IdentityProviderStorageProvider.java
  • services/src/main/java/org/keycloak/organization/forms/login/freemarker/model/OrganizationAwareIdentityProviderBean.java
  • testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java
🧰 Additional context used
🧬 Code graph analysis (1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java (1)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java (1)
  • InfinispanIdentityProviderStorageProvider (42-447)
🔇 Additional comments (11)
server-spi/src/main/java/org/keycloak/models/IdentityProviderStorageProvider.java (1)

252-256: LGTM - Login predicate properly extended for organization-aware filtering.

The added condition correctly mirrors the logic in getForLogin() (lines 173-181), ensuring consistent filtering whether applied via search options or in-memory predicate.

services/src/main/java/org/keycloak/organization/forms/login/freemarker/model/OrganizationAwareIdentityProviderBean.java (2)

74-77: LGTM - Defensive re-check for organization-aware IDP enablement.

The isEnabled() re-check correctly handles the case where the cached IDP is wrapped with organization-aware logic that checks the organization's enabled state at runtime. This ensures stale cache entries don't return disabled organization IDPs.


79-82: LGTM - Consistent defensive filtering for the ALL fetch mode.

Same rationale as the ORG_ONLY path. The comment clearly explains the wrapper consideration.

testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java (2)

369-415: Test setup and initial cache verification look correct.

The test comprehensively validates:

  • Initial empty cache state for all FetchModes
  • Cache population with correct IDP counts (5 realm-only enabled, 5 org-linked enabled, 10 total for ALL mode)

466-498: Good coverage of login-availability change triggering cache invalidation.

Setting hideOnLogin=false on an IDP correctly triggers cache invalidation and the subsequent re-query shows the updated count (6 realm-only IDPs).

model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java (6)

77-79: LGTM - Cache key method follows established patterns.

The cacheKeyForLogin method is correctly made public static for test access, and the key format is consistent with other cache key methods in this class.


213-255: LGTM - Login caching implementation follows established patterns.

The implementation correctly:

  • Checks for invalidation before using cache
  • Uses organizationId (or empty string) as a sub-key for different org contexts
  • Falls back to delegate on cache miss or stale cached IDP reference
  • Wraps returned models with createOrganizationAwareIdentityProviderModel

Note: The use of HashSet means ordering is not preserved, but callers like OrganizationAwareIdentityProviderBean apply their own sorting (IDP_COMPARATOR_INSTANCE), so this is acceptable.


382-418: LGTM - Well-designed selective cache invalidation.

The invalidation logic is correctly optimized:

  • registerIDPLoginInvalidation: Only invalidates if the IDP actually qualifies for login, avoiding unnecessary invalidation for non-login IDPs
  • registerIDPLoginInvalidationOnUpdate: Smart logic that preserves caches when:
    • A non-login IDP remains non-login after update
    • A login IDP remains login with the same organization link

This minimizes cache churn while ensuring correctness for all state transitions (enabled/disabled, org link changes, visibility changes).


98-113: LGTM - Correct invalidation handling on remove.

The change to fetch storedIdp from the delegate (line 100) before cache checks is necessary to properly determine if login caches need invalidation. The trade-off of an additional delegate call on remove is acceptable for ensuring cache consistency.


82-86: LGTM - Login cache invalidation properly integrated into create flow.


88-95: LGTM - Update correctly uses original vs updated state for invalidation decision.

Comment on lines +380 to +381
testRealm().identityProviders().create(idpRep).close();
getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Bug: Cleanup registrations use hardcoded "alias" instead of actual alias.

The cleanup lambda references "alias" instead of the actual IDP alias (e.g., "idp-alias-" + i). This will fail to clean up the created IDPs properly since no IDP with alias "alias" exists.

🐛 Proposed fix
-            testRealm().identityProviders().create(idpRep).close();
-            getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);
+            String alias = "idp-alias-" + i;
+            testRealm().identityProviders().create(idpRep).close();
+            getCleanup().addCleanup(testRealm().identityProviders().get(alias)::remove);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
testRealm().identityProviders().create(idpRep).close();
getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);
String alias = "idp-alias-" + i;
testRealm().identityProviders().create(idpRep).close();
getCleanup().addCleanup(testRealm().identityProviders().get(alias)::remove);
🤖 Prompt for AI Agents
In
@testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java
around lines 380 - 381, The cleanup registration uses a hardcoded "alias" string
so removals fail; change the cleanup to reference the actual identity provider
alias from the created representation (e.g., use idpRep.getAlias() or capture a
final String alias = idpRep.getAlias()) when calling get(...) in the lambda
added via getCleanup().addCleanup(...) so the removal targets the real provider
created by testRealm().identityProviders().create(idpRep).close().

Comment on lines +424 to +425
testRealm().identityProviders().create(idpRep).close();
getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same cleanup bug - hardcoded "alias" instead of actual alias.

🐛 Proposed fix
-        testRealm().identityProviders().create(idpRep).close();
-        getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);
+        testRealm().identityProviders().create(idpRep).close();
+        getCleanup().addCleanup(testRealm().identityProviders().get("idp-alias-20")::remove);
🤖 Prompt for AI Agents
In
@testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java
around lines 424 - 425, The cleanup currently uses a hardcoded "alias" when
registering removal: replace the literal "alias" with the actual identity
provider alias used when creating the provider (e.g., use idpRep.getAlias() or
the local alias variable) so getCleanup().addCleanup(...) calls
testRealm().identityProviders().get(actualAlias)::remove; ensure the same alias
value passed to testRealm().identityProviders().create(idpRep) is used for the
subsequent get(...) call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants