-
Notifications
You must be signed in to change notification settings - Fork 0
Automated Test: feature-idp-cache-implementation #317
Automated Test: feature-idp-cache-implementation #317
Conversation
Closes #32573 Signed-off-by: Stefan Guilhen <sguilhen@redhat.com>
📝 WalkthroughWalkthroughThe changes introduce a login-aware caching layer for Identity Providers with new methods to generate login cache keys, retrieve login-eligible IDPs with caching and fallback to delegate, and invalidate caches on create, update, and remove operations. Supporting changes enhance login predicates with organization and enabled-status filtering. Changes
Sequence DiagramsequenceDiagram
actor Client as Login Flow
participant Storage as IDP Storage<br/>Provider
participant Cache as Infinispan<br/>Cache
participant Delegate as Delegate<br/>Storage
Client->>Storage: getForLogin(mode, orgId)
activate Storage
Storage->>Storage: cacheKeyForLogin(realm, mode)
activate Storage
deactivate Storage
Storage->>Cache: get(loginCacheKey)
activate Cache
alt Cache Hit
Cache-->>Storage: IDPs (Stream)
else Cache Miss
Cache-->>Storage: null
end
deactivate Cache
alt Cached Result Available
Storage-->>Client: return cached Stream
else Cache Miss
Storage->>Delegate: get(mode, orgId)
activate Delegate
Delegate-->>Storage: IDPs (Stream)
deactivate Delegate
Storage->>Storage: filter: enabled & org-aware<br/>& login-eligible
activate Storage
deactivate Storage
Storage->>Cache: put(loginCacheKey, IDPs)
activate Cache
Cache-->>Storage: acknowledged
deactivate Cache
Storage-->>Client: return filtered Stream
end
deactivate Storage
Note over Storage,Cache: On IDP create/update/remove:<br/>invalidate login caches via<br/>registerIDPLoginInvalidation(idp)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 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: 1
🤖 Fix all issues with AI agents
In
`@testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java`:
- Around line 369-535: The cleanup calls currently use the literal "alias" so
created IDPs aren’t removed; update the getCleanup().addCleanup(...) calls to
reference the actual aliases you create: in the creation loop use
getCleanup().addCleanup(testRealm().identityProviders().get("idp-alias-" +
i)::remove) (matching the idpRep alias), and also add explicit cleanup calls for
the later single creates/updates (e.g., "idp-alias-20"), ensuring every
test-created IdentityProvider (all "idp-alias-0" .. "idp-alias-20") is scheduled
for removal via testRealm().identityProviders().get(<alias>)::remove.
🧹 Nitpick comments (1)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/idp/InfinispanIdentityProviderStorageProvider.java (1)
213-255: Normalize REALM_ONLY searchKey to avoid per-org cache entries.If callers pass an orgId with
REALM_ONLY, results are identical but cached under multiple keys. Normalizing the key avoids extra entries.♻️ Suggested refactor
- String searchKey = organizationId != null ? organizationId : ""; + String searchKey = (mode == FetchMode.REALM_ONLY) + ? "" + : (organizationId != null ? organizationId : "");
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | ||
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | ||
| assertNotNull(identityProviderListQuery); | ||
| } | ||
| }); | ||
|
|
||
| // 2- update a couple of idps (one not available for login, one available), but don't change their login-availability status | ||
| // none of these operations should invalidate the login caches. | ||
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | ||
| idpRep.getConfig().put("somekey", "somevalue"); | ||
| idpRep.setTrustEmail(true); | ||
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); // should still be unavailable for login | ||
|
|
||
| idpRep = testRealm().identityProviders().get("idp-alias-0").toRepresentation(); | ||
| idpRep.getConfig().put("somekey", "somevalue"); | ||
| idpRep.setTrustEmail(true); | ||
| testRealm().identityProviders().get("idp-alias-0").update(idpRep); // should still be available for login | ||
|
|
||
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | ||
| RealmModel realm = session.getContext().getRealm(); | ||
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | ||
|
|
||
| // check all caches for login are still there. | ||
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | ||
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | ||
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | ||
| assertNotNull(identityProviderListQuery); | ||
| } | ||
| }); | ||
|
|
||
| // 3- update an IDP, changing the availability for login - this should invalidate the caches. | ||
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | ||
| idpRep.setHideOnLogin(false); | ||
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); | ||
|
|
||
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | ||
| RealmModel realm = session.getContext().getRealm(); | ||
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | ||
|
|
||
| // check all caches have been cleared. | ||
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | ||
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | ||
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | ||
| assertNull(identityProviderListQuery); | ||
| } | ||
|
|
||
| // re-do searches to populate the caches again and check the updated results. | ||
| session.identityProviders().getForLogin(FetchMode.REALM_ONLY, null); | ||
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.REALM_ONLY), IdentityProviderListQuery.class); | ||
| assertNotNull(identityProviderListQuery); | ||
| assertEquals(6, identityProviderListQuery.getIDPs("").size()); | ||
|
|
||
| session.identityProviders().getForLogin(FetchMode.ORG_ONLY, orgaId); | ||
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ORG_ONLY), IdentityProviderListQuery.class); | ||
| assertNotNull(identityProviderListQuery); | ||
| assertEquals(5, identityProviderListQuery.getIDPs(orgaId).size()); | ||
|
|
||
| session.identityProviders().getForLogin(FetchMode.ALL, orgaId); | ||
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ALL), IdentityProviderListQuery.class); | ||
| assertNotNull(identityProviderListQuery); | ||
| assertEquals(11, identityProviderListQuery.getIDPs(orgaId).size()); | ||
|
|
||
| }); | ||
|
|
||
| // 4- finally, change one of the realm-level login IDPs, linking it to an org - although it still qualifies for login, it is now | ||
| // linked to an org, which should invalidate all login caches. | ||
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | ||
| idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()); | ||
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); | ||
| testRealm().organizations().get(orgaId).identityProviders().addIdentityProvider("idp-alias-20"); | ||
|
|
||
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | ||
| RealmModel realm = session.getContext().getRealm(); | ||
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | ||
|
|
||
| // check all caches have been cleared. | ||
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | ||
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | ||
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | ||
| assertNull(identityProviderListQuery); | ||
| } | ||
|
|
||
| // re-do searches to populate the caches again and check the updated results. | ||
| session.identityProviders().getForLogin(FetchMode.REALM_ONLY, null); | ||
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.REALM_ONLY), IdentityProviderListQuery.class); | ||
| assertNotNull(identityProviderListQuery); | ||
| assertEquals(5, identityProviderListQuery.getIDPs("").size()); | ||
|
|
||
| session.identityProviders().getForLogin(FetchMode.ORG_ONLY, orgaId); | ||
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ORG_ONLY), IdentityProviderListQuery.class); | ||
| assertNotNull(identityProviderListQuery); | ||
| assertEquals(6, identityProviderListQuery.getIDPs(orgaId).size()); | ||
|
|
||
| session.identityProviders().getForLogin(FetchMode.ALL, orgaId); | ||
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ALL), IdentityProviderListQuery.class); | ||
| assertNotNull(identityProviderListQuery); | ||
| assertEquals(11, identityProviderListQuery.getIDPs(orgaId).size()); | ||
|
|
||
| }); | ||
| } |
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.
Fix cleanup to remove the IDPs created by this test.
Using the literal "alias" leaves the test-created IDPs behind and can affect later tests.
🧹 Proposed fix
- idpRep.setAlias("idp-alias-" + i);
+ final String alias = "idp-alias-" + i;
+ idpRep.setAlias(alias);
idpRep.setEnabled((i % 2) == 0); // half of the IDPs will be disabled and won't qualify for login.
idpRep.setDisplayName("Broker " + i);
idpRep.setProviderId("keycloak-oidc");
if (i >= 10)
idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString());
testRealm().identityProviders().create(idpRep).close();
- getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);
+ getCleanup().addCleanup(testRealm().identityProviders().get(alias)::remove);
}
@@
- idpRep.setAlias("idp-alias-" + 20);
+ String alias = "idp-alias-20";
+ idpRep.setAlias(alias);
idpRep.setEnabled(true);
idpRep.setHideOnLogin(true); // this will make the new IDP not available for login.
idpRep.setDisplayName("Broker " + 20);
idpRep.setProviderId("keycloak-oidc");
testRealm().identityProviders().create(idpRep).close();
- getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove);
+ 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.
| @Test | |
| public void testCacheIDPForLogin() { | |
| // create 20 providers, and associate 10 of them with an organization. | |
| for (int i = 0; i < 20; i++) { | |
| IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation(); | |
| idpRep.setAlias("idp-alias-" + i); | |
| idpRep.setEnabled((i % 2) == 0); // half of the IDPs will be disabled and won't qualify for login. | |
| idpRep.setDisplayName("Broker " + i); | |
| idpRep.setProviderId("keycloak-oidc"); | |
| if (i >= 10) | |
| idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()); | |
| testRealm().identityProviders().create(idpRep).close(); | |
| getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove); | |
| } | |
| String orgaId = testRealm().organizations().getAll().get(0).getId(); | |
| for (int i = 10; i < 20; i++) { | |
| testRealm().organizations().get(orgaId).identityProviders().addIdentityProvider("idp-alias-" + i); | |
| } | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches for login don't exist yet | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNull(identityProviderListQuery); | |
| } | |
| // perform some login IDP searches and ensure they are cached. | |
| session.identityProviders().getForLogin(FetchMode.REALM_ONLY, null); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.REALM_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(5, identityProviderListQuery.getIDPs("").size()); | |
| session.identityProviders().getForLogin(FetchMode.ORG_ONLY, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ORG_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(5, identityProviderListQuery.getIDPs(orgaId).size()); | |
| session.identityProviders().getForLogin(FetchMode.ALL, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ALL), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(10, identityProviderListQuery.getIDPs(orgaId).size()); | |
| }); | |
| // 1- add/remove IDPs that are not available for login - none of these operations should invalidate the login caches. | |
| IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation(); | |
| idpRep.setAlias("idp-alias-" + 20); | |
| idpRep.setEnabled(true); | |
| idpRep.setHideOnLogin(true); // this will make the new IDP not available for login. | |
| idpRep.setDisplayName("Broker " + 20); | |
| idpRep.setProviderId("keycloak-oidc"); | |
| testRealm().identityProviders().create(idpRep).close(); | |
| getCleanup().addCleanup(testRealm().identityProviders().get("alias")::remove); | |
| // remove one IDP that was not available for login. | |
| testRealm().identityProviders().get("idp-alias-1").remove(); | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches for login are still there. | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| } | |
| }); | |
| // 2- update a couple of idps (one not available for login, one available), but don't change their login-availability status | |
| // none of these operations should invalidate the login caches. | |
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | |
| idpRep.getConfig().put("somekey", "somevalue"); | |
| idpRep.setTrustEmail(true); | |
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); // should still be unavailable for login | |
| idpRep = testRealm().identityProviders().get("idp-alias-0").toRepresentation(); | |
| idpRep.getConfig().put("somekey", "somevalue"); | |
| idpRep.setTrustEmail(true); | |
| testRealm().identityProviders().get("idp-alias-0").update(idpRep); // should still be available for login | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches for login are still there. | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| } | |
| }); | |
| // 3- update an IDP, changing the availability for login - this should invalidate the caches. | |
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | |
| idpRep.setHideOnLogin(false); | |
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches have been cleared. | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNull(identityProviderListQuery); | |
| } | |
| // re-do searches to populate the caches again and check the updated results. | |
| session.identityProviders().getForLogin(FetchMode.REALM_ONLY, null); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.REALM_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(6, identityProviderListQuery.getIDPs("").size()); | |
| session.identityProviders().getForLogin(FetchMode.ORG_ONLY, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ORG_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(5, identityProviderListQuery.getIDPs(orgaId).size()); | |
| session.identityProviders().getForLogin(FetchMode.ALL, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ALL), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(11, identityProviderListQuery.getIDPs(orgaId).size()); | |
| }); | |
| // 4- finally, change one of the realm-level login IDPs, linking it to an org - although it still qualifies for login, it is now | |
| // linked to an org, which should invalidate all login caches. | |
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | |
| idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()); | |
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); | |
| testRealm().organizations().get(orgaId).identityProviders().addIdentityProvider("idp-alias-20"); | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches have been cleared. | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNull(identityProviderListQuery); | |
| } | |
| // re-do searches to populate the caches again and check the updated results. | |
| session.identityProviders().getForLogin(FetchMode.REALM_ONLY, null); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.REALM_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(5, identityProviderListQuery.getIDPs("").size()); | |
| session.identityProviders().getForLogin(FetchMode.ORG_ONLY, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ORG_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(6, identityProviderListQuery.getIDPs(orgaId).size()); | |
| session.identityProviders().getForLogin(FetchMode.ALL, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ALL), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(11, identityProviderListQuery.getIDPs(orgaId).size()); | |
| }); | |
| } | |
| `@Test` | |
| public void testCacheIDPForLogin() { | |
| // create 20 providers, and associate 10 of them with an organization. | |
| for (int i = 0; i < 20; i++) { | |
| IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation(); | |
| final String alias = "idp-alias-" + i; | |
| idpRep.setAlias(alias); | |
| idpRep.setEnabled((i % 2) == 0); // half of the IDPs will be disabled and won't qualify for login. | |
| idpRep.setDisplayName("Broker " + i); | |
| idpRep.setProviderId("keycloak-oidc"); | |
| if (i >= 10) | |
| idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()); | |
| testRealm().identityProviders().create(idpRep).close(); | |
| getCleanup().addCleanup(testRealm().identityProviders().get(alias)::remove); | |
| } | |
| String orgaId = testRealm().organizations().getAll().get(0).getId(); | |
| for (int i = 10; i < 20; i++) { | |
| testRealm().organizations().get(orgaId).identityProviders().addIdentityProvider("idp-alias-" + i); | |
| } | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches for login don't exist yet | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNull(identityProviderListQuery); | |
| } | |
| // perform some login IDP searches and ensure they are cached. | |
| session.identityProviders().getForLogin(FetchMode.REALM_ONLY, null); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.REALM_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(5, identityProviderListQuery.getIDPs("").size()); | |
| session.identityProviders().getForLogin(FetchMode.ORG_ONLY, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ORG_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(5, identityProviderListQuery.getIDPs(orgaId).size()); | |
| session.identityProviders().getForLogin(FetchMode.ALL, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ALL), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(10, identityProviderListQuery.getIDPs(orgaId).size()); | |
| }); | |
| // 1- add/remove IDPs that are not available for login - none of these operations should invalidate the login caches. | |
| IdentityProviderRepresentation idpRep = new IdentityProviderRepresentation(); | |
| String alias = "idp-alias-20"; | |
| idpRep.setAlias(alias); | |
| idpRep.setEnabled(true); | |
| idpRep.setHideOnLogin(true); // this will make the new IDP not available for login. | |
| idpRep.setDisplayName("Broker " + 20); | |
| idpRep.setProviderId("keycloak-oidc"); | |
| testRealm().identityProviders().create(idpRep).close(); | |
| getCleanup().addCleanup(testRealm().identityProviders().get(alias)::remove); | |
| // remove one IDP that was not available for login. | |
| testRealm().identityProviders().get("idp-alias-1").remove(); | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches for login are still there. | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| } | |
| }); | |
| // 2- update a couple of idps (one not available for login, one available), but don't change their login-availability status | |
| // none of these operations should invalidate the login caches. | |
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | |
| idpRep.getConfig().put("somekey", "somevalue"); | |
| idpRep.setTrustEmail(true); | |
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); // should still be unavailable for login | |
| idpRep = testRealm().identityProviders().get("idp-alias-0").toRepresentation(); | |
| idpRep.getConfig().put("somekey", "somevalue"); | |
| idpRep.setTrustEmail(true); | |
| testRealm().identityProviders().get("idp-alias-0").update(idpRep); // should still be available for login | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches for login are still there. | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| } | |
| }); | |
| // 3- update an IDP, changing the availability for login - this should invalidate the caches. | |
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | |
| idpRep.setHideOnLogin(false); | |
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches have been cleared. | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNull(identityProviderListQuery); | |
| } | |
| // re-do searches to populate the caches again and check the updated results. | |
| session.identityProviders().getForLogin(FetchMode.REALM_ONLY, null); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.REALM_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(6, identityProviderListQuery.getIDPs("").size()); | |
| session.identityProviders().getForLogin(FetchMode.ORG_ONLY, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ORG_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(5, identityProviderListQuery.getIDPs(orgaId).size()); | |
| session.identityProviders().getForLogin(FetchMode.ALL, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ALL), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(11, identityProviderListQuery.getIDPs(orgaId).size()); | |
| }); | |
| // 4- finally, change one of the realm-level login IDPs, linking it to an org - although it still qualifies for login, it is now | |
| // linked to an org, which should invalidate all login caches. | |
| idpRep = testRealm().identityProviders().get("idp-alias-20").toRepresentation(); | |
| idpRep.getConfig().put(OrganizationModel.BROKER_PUBLIC, Boolean.TRUE.toString()); | |
| testRealm().identityProviders().get("idp-alias-20").update(idpRep); | |
| testRealm().organizations().get(orgaId).identityProviders().addIdentityProvider("idp-alias-20"); | |
| getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> { | |
| RealmModel realm = session.getContext().getRealm(); | |
| RealmCacheSession realmCache = (RealmCacheSession) session.getProvider(CacheRealmProvider.class); | |
| // check all caches have been cleared. | |
| for (FetchMode fetchMode : IdentityProviderStorageProvider.FetchMode.values()) { | |
| String cachedKey = cacheKeyForLogin(realm, fetchMode); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cachedKey, IdentityProviderListQuery.class); | |
| assertNull(identityProviderListQuery); | |
| } | |
| // re-do searches to populate the caches again and check the updated results. | |
| session.identityProviders().getForLogin(FetchMode.REALM_ONLY, null); | |
| IdentityProviderListQuery identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.REALM_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(5, identityProviderListQuery.getIDPs("").size()); | |
| session.identityProviders().getForLogin(FetchMode.ORG_ONLY, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ORG_ONLY), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(6, identityProviderListQuery.getIDPs(orgaId).size()); | |
| session.identityProviders().getForLogin(FetchMode.ALL, orgaId); | |
| identityProviderListQuery = realmCache.getCache().get(cacheKeyForLogin(realm, FetchMode.ALL), IdentityProviderListQuery.class); | |
| assertNotNull(identityProviderListQuery); | |
| assertEquals(11, identityProviderListQuery.getIDPs(orgaId).size()); | |
| }); | |
| } |
🤖 Prompt for AI Agents
In
`@testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/organization/cache/OrganizationCacheTest.java`
around lines 369 - 535, The cleanup calls currently use the literal "alias" so
created IDPs aren’t removed; update the getCleanup().addCleanup(...) calls to
reference the actual aliases you create: in the creation loop use
getCleanup().addCleanup(testRealm().identityProviders().get("idp-alias-" +
i)::remove) (matching the idpRep alias), and also add explicit cleanup calls for
the later single creates/updates (e.g., "idp-alias-20"), ensuring every
test-created IdentityProvider (all "idp-alias-0" .. "idp-alias-20") is scheduled
for removal via testRealm().identityProviders().get(<alias>)::remove.
This pull request was automatically created by
@coderabbitai/e2e-reviewer.Batch created pull request.
Summary by CodeRabbit
Bug Fixes
Performance