Skip to content

Automated Test: feature-idp-cache-implementation #307

Conversation

admin-coderabbit
Copy link
Owner

@admin-coderabbit admin-coderabbit commented Feb 4, 2026

This pull request was automatically created by @coderabbitai/e2e-reviewer.

Batch created pull request.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced identity provider filtering for login screens: only enabled identity providers are now displayed.
    • Organization-linked identity providers now require the "broker public" configuration flag to appear during login.
    • Improved caching mechanism for identity providers accessible during login to enhance performance.
  • Tests

    • Added comprehensive test coverage for identity provider login caching behavior and cache invalidation scenarios.

Closes #32573

Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
@coderabbit-eval
Copy link

coderabbit-eval bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

The changes introduce login-specific caching for identity providers with cache invalidation logic based on login eligibility and organization linkage. New public methods enable retrieval and caching of login-eligible IDPs, while filter predicates enforce organization-based gating for cached data, with comprehensive test coverage.

Changes

Cohort / File(s) Summary
Cache Infrastructure
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java
Added public methods getForLogin(FetchMode mode, String organizationId) and static helper cacheKeyForLogin(RealmModel realm, FetchMode fetchMode) for login-eligible IDP caching. Introduced private invalidation helpers registerIDPLoginInvalidation() and registerIDPLoginInvalidationOnUpdate() to maintain cache coherence during create/update/remove operations based on login eligibility transitions and organization linkage.
Login Predicate Enhancement
server-spi/src/main/java/org/keycloak/models/IdentityProviderStorageProvider.java
Added config-based gating condition to login predicate: org-associated IDPs now require the broker_public config flag set to true to be login-eligible, while realm-level IDPs bypass this check.
UI Filtering
services/src/main/java/org/keycloak/organization/forms/login/freemarker/model/OrganizationAwareIdentityProviderBean.java
Added isEnabled() filter condition across REALM_ONLY and ORG_ONLY/ALL paths to ensure only enabled identity providers are returned during login collection, maintaining existing alias exclusion logic.
Test Coverage
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java
Added comprehensive testCacheIDPForLogin() test validating login cache behavior across multiple scenarios: cache misses, population via queries, and invalidation on IDP additions, removals, updates affecting login eligibility, and organization linkage changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 The cache hops with login delight,
IDPs filtered to shine just right,
When enabled they leap, org flags gleam,
Invalidation flows like a swift stream,
Login caches dance—organized, clean! 🌟

🚥 Pre-merge checks | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is minimal and does not follow the repository template. It only states that the PR was automatically created without explaining the actual changes or their purpose. Add substantive description of the changes: explain the new caching logic for login-eligible IDPs, cache invalidation strategy, organization filtering, and the test coverage added. Reference the CONTRIBUTING.md guidelines if required.
Title check ❓ Inconclusive The title is vague and generic, using non-descriptive terminology ('Automated Test') that does not convey meaningful information about the actual changeset. Replace with a clear, specific title describing the main change, such as 'Add login-aware IDP caching with organization filtering' or 'Implement cache invalidation for login-eligible identity providers'.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature-idp-cache-implementation

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

@coderabbit-eval coderabbit-eval bot closed this Feb 4, 2026
Copy link

@coderabbit-eval coderabbit-eval bot left a comment

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
`@model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java`:
- Around line 97-113: The remove method may NPE when
idpDelegate.getByAlias(alias) returns null; before calling
registerIDPInvalidation(storedIdp) or registerIDPLoginInvalidation(storedIdp)
guard against null: fetch storedIdp via idpDelegate.getByAlias(alias) and only
call registerIDPInvalidation/stub registration if storedIdp != null (and
likewise ensure cached != null before using cached.getIdentityProvider());
update the logic in InfinispanIdentityProviderStorageProvider.remove to check
storedIdp and cached identity provider for nulls before dereferencing or passing
into registerIDPInvalidation.

In
`@testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java`:
- Around line 380-382: The cleanup registers the literal string "alias" instead
of the actual IdP alias; update the cleanup call in OrganizationCacheTest so it
uses the created IdP's alias (e.g., idpRep.getAlias() or the local variable
holding the alias) when calling testRealm().identityProviders().get(...)::remove
after testRealm().identityProviders().create(idpRep).close(); apply the same fix
in the other test method (testCacheIDPByOrg) where the literal "alias" was used.
🧹 Nitpick comments (1)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java (1)

382-418: Cache invalidation logic is well-reasoned and correctly handles all transitions.

The update invalidation in registerIDPLoginInvalidationOnUpdate properly identifies the four quadrants (was/wasn't login-eligible × becomes/stays) and adds the org-linkage change check. The getLoginPredicate() is called up to 4 times per update; since this only runs during mutation paths it's negligible, but you could extract it to a local variable for clarity.

♻️ Minor: extract predicate to local variable
 private void registerIDPLoginInvalidationOnUpdate(IdentityProviderModel original, IdentityProviderModel updated) {
+    Predicate<IdentityProviderModel> loginPredicate = getLoginPredicate();
     // IDP isn't currently available for login and update preserves that - no need to invalidate.
-    if (!getLoginPredicate().test(original) && !getLoginPredicate().test(updated)) {
+    if (!loginPredicate.test(original) && !loginPredicate.test(updated)) {
         return;
     }
     // IDP is currently available for login and update preserves that, including organization link - no need to invalidate.
-    if (getLoginPredicate().test(original) && getLoginPredicate().test(updated)
+    if (loginPredicate.test(original) && loginPredicate.test(updated)
             && Objects.equals(original.getOrganizationId(), updated.getOrganizationId())) {
         return;
     }

Comment on lines 97 to 113
@Override
public boolean remove(String alias) {
String cacheKey = cacheKeyIdpAlias(getRealm(), alias);
IdentityProviderModel storedIdp = idpDelegate.getByAlias(alias);
if (isInvalid(cacheKey)) {
//lookup idp by alias in cache to be able to invalidate its internalId
registerIDPInvalidation(idpDelegate.getByAlias(alias));
registerIDPInvalidation(storedIdp);
} else {
CachedIdentityProvider cached = realmCache.getCache().get(cacheKey, CachedIdentityProvider.class);
if (cached != null) {
registerIDPInvalidation(cached.getIdentityProvider());
}
}
registerCountInvalidation();
registerIDPLoginInvalidation(storedIdp);
return idpDelegate.remove(alias);
}

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential NPE if remove() is called with a non-existent alias.

storedIdp fetched at line 100 could be null if the alias doesn't exist. registerIDPLoginInvalidation (line 111) handles null safely via the Objects::nonNull guard in the login predicate, but registerIDPInvalidation (line 103) will NPE since it directly dereferences idp.getInternalId().

🛡️ Proposed null guard
     IdentityProviderModel storedIdp = idpDelegate.getByAlias(alias);
+    if (storedIdp == null) {
+        return idpDelegate.remove(alias);
+    }
     if (isInvalid(cacheKey)) {
🤖 Prompt for AI Agents
In
`@model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java`
around lines 97 - 113, The remove method may NPE when
idpDelegate.getByAlias(alias) returns null; before calling
registerIDPInvalidation(storedIdp) or registerIDPLoginInvalidation(storedIdp)
guard against null: fetch storedIdp via idpDelegate.getByAlias(alias) and only
call registerIDPInvalidation/stub registration if storedIdp != null (and
likewise ensure cached != null before using cached.getIdentityProvider());
update the logic in InfinispanIdentityProviderStorageProvider.remove to check
storedIdp and cached identity provider for nulls before dereferencing or passing
into registerIDPInvalidation.

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

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Cleanup registers "alias" literally instead of the actual IDP alias.

testRealm().identityProviders().get("alias") references a non-existent IDP rather than the one just created. This means the created IDPs won't be cleaned up after the test. The same bug exists in testCacheIDPByOrg (line 303), but since this is new test code, it should be fixed here.

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

Same fix should be applied at line 425:

         testRealm().identityProviders().create(idpRep).close();
-        getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);
+        getCleanup().addCleanup(testRealm().identityProviders().get("idp-alias-20")::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);
}
final String aliasForCleanup = "idp-alias-" + i;
testRealm().identityProviders().create(idpRep).close();
getCleanup().addCleanup(testRealm().identityProviders().get(aliasForCleanup)::remove);
}
🤖 Prompt for AI Agents
In
`@testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java`
around lines 380 - 382, The cleanup registers the literal string "alias" instead
of the actual IdP alias; update the cleanup call in OrganizationCacheTest so it
uses the created IdP's alias (e.g., idpRep.getAlias() or the local variable
holding the alias) when calling testRealm().identityProviders().get(...)::remove
after testRealm().identityProviders().create(idpRep).close(); apply the same fix
in the other test method (testCacheIDPByOrg) where the literal "alias" was used.

Sign in to join this conversation on GitHub.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant