-
Notifications
You must be signed in to change notification settings - Fork 0
Automated Test: feature-idp-cache-implementation #307
Automated Test: feature-idp-cache-implementation #307
Conversation
Closes #32573 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
📝 WalkthroughWalkthroughThe 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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this 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
registerIDPLoginInvalidationOnUpdateproperly identifies the four quadrants (was/wasn't login-eligible × becomes/stays) and adds the org-linkage change check. ThegetLoginPredicate()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; }
| @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); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| testRealm().identityProviders().create(idpRep).close(); | ||
| getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
This pull request was automatically created by
@coderabbitai/e2e-reviewer.Batch created pull request.
Summary by CodeRabbit
Release Notes
New Features
Tests