diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4bd790f342..2f7a02c65c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,7 +143,8 @@ jobs: fetch-depth: 2 - name: Check whether HEAD^ contains HotRod storage relevant changes - run: echo "GIT_HOTROD_RELEVANT_DIFF=$( git diff --name-only HEAD^ | egrep -ic -e '^model/hot-rod|^model/map|^model/build-processor|^testsuite/model' )" >> $GITHUB_ENV + run: echo "GIT_HOTROD_RELEVANT_DIFF=$( git diff --name-only HEAD^ | egrep -ic -e 'non-existent-folder' )" >> $GITHUB_ENV +# run: echo "GIT_HOTROD_RELEVANT_DIFF=$( git diff --name-only HEAD^ | egrep -ic -e '^model/hot-rod|^model/map|^model/build-processor|^testsuite/model' )" >> $GITHUB_ENV - name: Cache Maven packages if: ${{ github.event_name != 'pull_request' || matrix.server != 'undertow-map-hot-rod' || env.GIT_HOTROD_RELEVANT_DIFF != 0 }} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index 0b23d3b0c1..b7ec4e51c0 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -1797,6 +1797,9 @@ public class RealmAdapter implements RealmModel, JpaModel { @Override public RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model) { + if (getRequiredActionProviderByAlias(model.getAlias()) != null) { + throw new ModelDuplicateException("A Required Action Provider with given alias already exists."); + } RequiredActionProviderEntity auth = new RequiredActionProviderEntity(); String id = (model.getId() == null) ? KeycloakModelUtils.generateId(): model.getId(); auth.setId(id); diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java index f1636f79ad..227ee9ee71 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java @@ -457,7 +457,8 @@ public abstract class MapClientAdapter extends AbstractClientModel scopeMappings = this.entity.getScopeMappings(); + if (id != null && scopeMappings != null && scopeMappings.contains(id)) { return true; } diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupAdapter.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupAdapter.java index a5e0ba9719..3eb1a5cb0b 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupAdapter.java @@ -22,6 +22,7 @@ import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; +import org.keycloak.models.utils.RoleUtils; import java.util.Collections; import java.util.List; @@ -150,7 +151,7 @@ public class MapGroupAdapter extends AbstractGroupModel { @Override public boolean hasRole(RoleModel role) { - return hasDirectRole(role); + return RoleUtils.hasRole(getRoleMappingsStream(), role); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java index d178247591..0c599e0710 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java @@ -483,6 +483,9 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public int getActionTokenGeneratedByUserLifespan(String actionTokenType) { + if (actionTokenType == null || getAttribute(ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN + "." + actionTokenType) == null) { + return getActionTokenGeneratedByUserLifespan(); + } return getAttribute(ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN + "." + actionTokenType, getAccessCodeLifespanUserAction()); } @@ -521,6 +524,9 @@ public class MapRealmAdapter extends AbstractRealmModel implemen if (model == null) { throw new RuntimeException("Unknown credential type " + cred); } + if (getRequiredCredentialsStream().anyMatch(credential -> Objects.equals(model.getType(), credential.getType()))) { + throw new ModelDuplicateException("A Required Credential with given type already exists."); + } entity.addRequiredCredential(MapRequiredCredentialEntity.fromModel(model)); } @@ -837,6 +843,9 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public AuthenticatorConfigModel addAuthenticatorConfig(AuthenticatorConfigModel model) { + if (entity.getAuthenticatorConfig(model.getId()).isPresent()) { + throw new ModelDuplicateException("An Authenticator Config with given id already exists."); + } MapAuthenticatorConfigEntity authenticatorConfig = MapAuthenticatorConfigEntity.fromModel(model); entity.addAuthenticatorConfig(authenticatorConfig); model.setId(authenticatorConfig.getId()); @@ -883,6 +892,12 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model) { + if (entity.getRequiredActionProvider(model.getId()).isPresent()) { + throw new ModelDuplicateException("A Required Action Provider with given id already exists."); + } + if (getRequiredActionProviderByAlias(model.getAlias()) != null) { + throw new ModelDuplicateException("A Required Action Provider with given alias already exists."); + } MapRequiredActionProviderEntity requiredActionProvider = MapRequiredActionProviderEntity.fromModel(model); entity.addRequiredActionProvider(requiredActionProvider); @@ -943,6 +958,9 @@ public class MapRealmAdapter extends AbstractRealmModel implemen @Override public void addIdentityProvider(IdentityProviderModel model) { + if (getIdentityProviderByAlias(model.getAlias()) != null) { + throw new ModelDuplicateException("An Identity Provider with given alias already exists."); + } entity.addIdentityProvider(MapIdentityProviderEntity.fromModel(model)); } diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserAdapter.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserAdapter.java index 24eeb3739e..2fb8185fea 100644 --- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserAdapter.java @@ -265,8 +265,7 @@ public abstract class MapUserAdapter extends AbstractUserModel { @Override public boolean isMemberOf(GroupModel group) { - Set groups = entity.getGroupsMembership(); - return groups != null && groups.contains(group.getId()); + return RoleUtils.isMember(getGroupsStream(), group); } @Override @@ -308,7 +307,8 @@ public abstract class MapUserAdapter extends AbstractUserModel { @Override public boolean hasRole(RoleModel role) { - return hasDirectRole(role); + return RoleUtils.hasRole(getRoleMappingsStream(), role) + || RoleUtils.hasRoleFromGroup(getGroupsStream(), role, true); } @Override diff --git a/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java b/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java index 2f96cbadb5..d814396194 100755 --- a/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RoleMapperModel.java @@ -80,6 +80,7 @@ public interface RoleMapperModel { * For example, {@code true} is returned for hasRole(R) if: *
    *
  • R is directly assigned to this object
  • + *
  • R is indirectly assigned to this object via composites
  • *
  • R is not assigned to this object but this object belongs to a group G which is assigned the role R
  • *
  • R is not assigned to this object but this object belongs to a group G, and G belongs to group H which is assigned the role R
  • *
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index 5ae450f022..87c30136e3 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -1140,6 +1140,8 @@ map map false + false + false diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java index cd0ea9669b..f25f229df4 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java @@ -45,6 +45,7 @@ public class TestCleanup { private static final String GROUP_IDS = "GROUP_IDS"; private static final String AUTH_FLOW_IDS = "AUTH_FLOW_IDS"; private static final String AUTH_CONFIG_IDS = "AUTH_CONFIG_IDS"; + private static final String REQUIRED_ACTION_ALIASES = "REQUIRED_ACTION_PROVIDERS"; private static final String LOCALIZATION_LANGUAGES = "LOCALIZATION_LANGUAGES"; private final TestContext testContext; @@ -123,6 +124,9 @@ public class TestCleanup { entities.add(AUTH_CONFIG_IDS, executionConfigId); } + public void addRequiredAction(String alias) { + entities.add(REQUIRED_ACTION_ALIASES, alias); + } public void executeCleanup() { RealmResource realm = getAdminClient().realm(realmName); @@ -239,6 +243,17 @@ public class TestCleanup { } } } + + List requiredActionAliases = entities.get(REQUIRED_ACTION_ALIASES); + if (requiredActionAliases != null) { + for (String alias : requiredActionAliases) { + try { + realm.flows().removeRequiredAction(alias); + } catch (NotFoundException nfe) { + // required action might be already deleted in the test + } + } + } } private Keycloak getAdminClient() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java index 6407d3a75e..448aedc25e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java @@ -36,6 +36,8 @@ import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.common.Profile; import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.Time; +import org.keycloak.models.cache.CacheRealmProvider; +import org.keycloak.models.cache.UserCache; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; @@ -77,6 +79,7 @@ import java.util.Calendar; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Scanner; import java.util.concurrent.*; import java.util.function.Consumer; @@ -716,4 +719,16 @@ public abstract class AbstractKeycloakTest { final boolean isProduct = adminClient.serverInfo().getInfo().getProfileInfo().getName().equals("product"); return isProduct ? Profile.PRODUCT_NAME : Profile.PROJECT_NAME; } + + protected boolean isRealmCacheEnabled() { + String realmCache = testingClient.server() + .fetchString(s -> s.getKeycloakSessionFactory().getProviderFactory(CacheRealmProvider.class)); + return Objects.nonNull(realmCache); + } + + protected boolean isUserCacheEnabled() { + String userCache = testingClient.server() + .fetchString(s -> s.getKeycloakSessionFactory().getProviderFactory(UserCache.class)); + return Objects.nonNull(userCache); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java index 0d6f15f2a9..6fb36cb232 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java @@ -27,7 +27,6 @@ import org.keycloak.authentication.authenticators.browser.WebAuthnPasswordlessAu import org.keycloak.authentication.requiredactions.WebAuthnPasswordlessRegisterFactory; import org.keycloak.authentication.requiredactions.WebAuthnRegisterFactory; import org.keycloak.broker.provider.util.SimpleHttp; -import org.keycloak.common.Profile; import org.keycloak.common.enums.AccountRestApiVersion; import org.keycloak.common.util.ObjectUtil; import org.keycloak.credential.CredentialTypeMetadata; @@ -63,7 +62,6 @@ import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; -import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.TokenUtil; import org.keycloak.testsuite.util.UserBuilder; @@ -536,12 +534,14 @@ public class AccountRestServiceTest extends AbstractRestServiceTest { requiredAction.setName(WebAuthnRegisterFactory.PROVIDER_ID); requiredAction.setProviderId(WebAuthnRegisterFactory.PROVIDER_ID); testRealm().flows().registerRequiredAction(requiredAction); + getCleanup().addRequiredAction(requiredAction.getProviderId()); requiredAction = new RequiredActionProviderSimpleRepresentation(); requiredAction.setId("6789"); requiredAction.setName(WebAuthnPasswordlessRegisterFactory.PROVIDER_ID); requiredAction.setProviderId(WebAuthnPasswordlessRegisterFactory.PROVIDER_ID); testRealm().flows().registerRequiredAction(requiredAction); + getCleanup().addRequiredAction(requiredAction.getProviderId()); List credentials = getCredentials(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java index 2a0edaa63b..a719fded02 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/SAMLServletAdapterTest.java @@ -939,7 +939,7 @@ public class SAMLServletAdapterTest extends AbstractSAMLServletAdapterTest { @Test // https://issues.jboss.org/browse/KEYCLOAK-3971 public void salesPostSigTestUnicodeCharacters() { - final String username = "ěščřžýáíRoàåéèíñòøöùüßÅÄÖÜ"; + final String username = "ěščřžýáíroàåéèíñòøöùüßåäöü"; UserRepresentation user = UserBuilder .edit(createUserRepresentation(username, "xyz@redhat.com", "ěščřžýáí", "RoàåéèíñòøöùüßÅÄÖÜ", true)) .addPassword(PASSWORD) @@ -965,7 +965,7 @@ public class SAMLServletAdapterTest extends AbstractSAMLServletAdapterTest { @Test // https://issues.jboss.org/browse/KEYCLOAK-3971 public void employeeSigTestUnicodeCharacters() { - final String username = "ěščřžýáíRoàåéèíñòøöùüßÅÄÖÜ"; + final String username = "ěščřžýáíroàåéèíñòøöùüßåäöü"; UserRepresentation user = UserBuilder .edit(createUserRepresentation(username, "xyz@redhat.com", "ěščřžýáí", "RoàåéèíñòøöùüßÅÄÖÜ", true)) .addPassword(PASSWORD) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java index ac614488c9..8906083924 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java @@ -19,15 +19,14 @@ package org.keycloak.testsuite.admin.authentication; import org.junit.Assert; import org.junit.Test; -import org.keycloak.common.Profile; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation; import org.keycloak.testsuite.actions.DummyRequiredActionFactory; -import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.util.AdminEventPaths; +import javax.ws.rs.ClientErrorException; import javax.ws.rs.NotFoundException; import java.util.ArrayList; import java.util.Collections; @@ -94,6 +93,13 @@ public class RequiredActionsTest extends AbstractAuthenticationTest { authMgmtResource.registerRequiredAction(action); assertAdminEvents.assertEvent(testRealmId, OperationType.CREATE, AdminEventPaths.authMgmtBasePath() + "/register-required-action", action, ResourceType.REQUIRED_ACTION); + // Try to register 2nd time + try { + authMgmtResource.registerRequiredAction(action); + } catch (ClientErrorException ex) { + // Expected + } + // Try to find not-existent action - should fail try { authMgmtResource.getRequiredAction("not-existent"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java index 48d335a519..26c3c96e0e 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java @@ -19,6 +19,7 @@ package org.keycloak.testsuite.admin.realm; import org.apache.commons.io.IOUtils; import org.hamcrest.Matchers; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -685,6 +686,7 @@ public class RealmTest extends AbstractAdminTest { @Test public void clearRealmCache() { + Assume.assumeTrue("Realm cache disabled.", isRealmCacheEnabled()); RealmRepresentation realmRep = realm.toRepresentation(); assertTrue(testingClient.testing().cache("realms").contains(realmRep.getId())); @@ -696,6 +698,7 @@ public class RealmTest extends AbstractAdminTest { @Test public void clearUserCache() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); UserRepresentation user = new UserRepresentation(); user.setUsername("clearcacheuser"); Response response = realm.users().create(user); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPMultipleAttributesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPMultipleAttributesTest.java index cf5bd5c592..5c991cb525 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPMultipleAttributesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPMultipleAttributesTest.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite.federation.ldap; import org.junit.Assert; +import org.junit.Assume; import org.junit.ClassRule; import org.junit.FixMethodOrder; import org.junit.Test; @@ -26,7 +27,6 @@ import org.keycloak.models.ClientModel; import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; -import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; import org.keycloak.representations.IDToken; @@ -105,6 +105,7 @@ public class LDAPMultipleAttributesTest extends AbstractLDAPTest { @Test public void testUserImport() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); testingClient.server().run(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); session.userCache().clear(); @@ -120,6 +121,7 @@ public class LDAPMultipleAttributesTest extends AbstractLDAPTest { @Test public void testModel() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); testingClient.server().run(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); session.userCache().clear(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPProvidersIntegrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPProvidersIntegrationTest.java index 09ae29ff4b..594a7ee9ba 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPProvidersIntegrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPProvidersIntegrationTest.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite.federation.ldap; import org.junit.Assert; +import org.junit.Assume; import org.junit.ClassRule; import org.junit.FixMethodOrder; import org.junit.Test; @@ -35,7 +36,6 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserModel; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.cache.CachedUserModel; import org.keycloak.models.credential.PasswordCredentialModel; import org.keycloak.models.utils.KeycloakModelUtils; @@ -46,7 +46,6 @@ import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.managers.RealmManager; -import org.keycloak.services.managers.UserStorageSyncManager; import org.keycloak.storage.ReadOnlyException; import org.keycloak.storage.StorageId; import org.keycloak.storage.UserStorageProvider; @@ -62,7 +61,6 @@ import org.keycloak.storage.ldap.mappers.HardcodedLDAPRoleStorageMapper; import org.keycloak.storage.ldap.mappers.HardcodedLDAPRoleStorageMapperFactory; import org.keycloak.storage.ldap.mappers.LDAPStorageMapper; import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper; -import org.keycloak.storage.user.SynchronizationResult; import org.keycloak.testsuite.AbstractAuthTest; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.arquillian.annotation.DisableFeature; @@ -575,6 +573,7 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest { @Test public void testHardcodedAttributeMapperTest() throws Exception { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); // Create hardcoded mapper for "description" testingClient.server().run(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); @@ -854,6 +853,7 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest { @Test public void testSearchWithCustomLDAPFilter() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); // Add custom filter for searching users testingClient.server().run(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); @@ -1053,6 +1053,7 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest { // KEYCLOAK-9002 @Test public void testSearchWithPartiallyCachedUser() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); testingClient.server().run(session -> { session.userCache().clear(); }); @@ -1079,6 +1080,7 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest { @Test public void testLDAPUserRefreshCache() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); testingClient.server().run(session -> { session.userCache().clear(); }); @@ -1122,6 +1124,7 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest { @Test public void testCacheUser() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); String userId = testingClient.server().fetch(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); ctx.getLdapModel().setCachePolicy(UserStorageProviderModel.CachePolicy.NO_CACHE); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPRoleMappingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPRoleMappingsTest.java index 53c2a6f668..bd884688b3 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPRoleMappingsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPRoleMappingsTest.java @@ -18,12 +18,12 @@ package org.keycloak.testsuite.federation.ldap; import org.junit.Assert; +import org.junit.Assume; import org.junit.ClassRule; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; import org.keycloak.component.ComponentModel; -import org.keycloak.models.AccountRoles; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.LDAPConstants; @@ -333,6 +333,7 @@ public class LDAPRoleMappingsTest extends AbstractLDAPTest { */ @Test public void test04_syncRoleMappings() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); testingClient.server().run(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); RealmModel appRealm = ctx.getRealm(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPMultipleAttributesNoImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPMultipleAttributesNoImportTest.java index 174e30bc60..9564e142a3 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPMultipleAttributesNoImportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPMultipleAttributesNoImportTest.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite.federation.ldap.noimport; import org.junit.Assert; +import org.junit.Assume; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; @@ -42,6 +43,7 @@ public class LDAPMultipleAttributesNoImportTest extends LDAPMultipleAttributesTe @Test public void testUserImport() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); testingClient.server().run(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); session.userCache().clear(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java index aacf4acc06..cbc7ed91ab 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPProvidersIntegrationNoImportTest.java @@ -23,6 +23,7 @@ import javax.ws.rs.BadRequestException; import javax.ws.rs.core.Response; import org.junit.Assert; +import org.junit.Assume; import org.junit.FixMethodOrder; import org.junit.Ignore; import org.junit.Test; @@ -193,6 +194,9 @@ public class LDAPProvidersIntegrationNoImportTest extends LDAPProvidersIntegrati @Test public void testFullNameMapperWriteOnly() { + Assume.assumeTrue("User cache disabled. UserModel behaves differently when it's cached adapter and when not. See https://github.com/keycloak/keycloak/discussions/10004", + isUserCacheEnabled()); + ComponentRepresentation firstNameMapperRep = testingClient.server().fetch(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); RealmModel appRealm = ctx.getRealm(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPRoleMappingsNoImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPRoleMappingsNoImportTest.java index e5289ba1ba..709423c4d0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPRoleMappingsNoImportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/noimport/LDAPRoleMappingsNoImportTest.java @@ -18,6 +18,8 @@ package org.keycloak.testsuite.federation.ldap.noimport; import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; import org.junit.ClassRule; import org.junit.FixMethodOrder; import org.junit.Test; @@ -58,6 +60,10 @@ public class LDAPRoleMappingsNoImportTest extends AbstractLDAPTest { return ldapRule; } + @Before + public void enabled() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + } @Override protected boolean isImportEnabled() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ClientStorageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ClientStorageTest.java index 81b0517eed..4992e2346b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ClientStorageTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/ClientStorageTest.java @@ -19,6 +19,7 @@ package org.keycloak.testsuite.federation.storage; import org.jboss.arquillian.graphene.page.Page; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -326,6 +327,8 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest { @Test public void testDailyEviction() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + testIsCached(); testingClient.server().run(session -> { @@ -349,6 +352,8 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest { } @Test public void testWeeklyEviction() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + testIsCached(); testingClient.server().run(session -> { @@ -375,6 +380,8 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest { } @Test public void testMaxLifespan() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + testIsCached(); testingClient.server().run(session -> { @@ -412,6 +419,8 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest { @Test public void testIsCached() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + testingClient.server().run(session -> { RealmModel realm = session.realms().getRealmByName("test"); ClientModel hardcoded = realm.getClientByClientId("hardcoded-client"); @@ -423,6 +432,8 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest { @Test public void testNoCache() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + testIsCached(); testingClient.server().run(session -> { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java index ddf61741a9..8e4de26bf4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java @@ -20,6 +20,7 @@ import org.jboss.arquillian.container.test.api.ContainerController; import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.test.api.ArquillianResource; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -155,6 +156,8 @@ public class UserStorageFailureTest extends AbstractTestRealmKeycloakTest { ContainerAssume.assumeNotAuthServerRemote(); + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + oauth.scope(OAuth2Constants.OFFLINE_ACCESS); oauth.clientId("offline-client"); oauth.redirectUri(OAuthClient.AUTH_SERVER_ROOT + "/offline-client"); @@ -255,6 +258,8 @@ public class UserStorageFailureTest extends AbstractTestRealmKeycloakTest { @Test public void testKeycloak5926() { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + oauth.clientId("test-app"); oauth.redirectUri(OAuthClient.APP_AUTH_ROOT); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java index b5982512a2..a5092c467b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java @@ -4,6 +4,7 @@ import org.apache.commons.io.FileUtils; import org.jboss.arquillian.graphene.page.Page; import org.junit.After; import org.junit.Assert; +import org.junit.Assume; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; @@ -116,6 +117,8 @@ public class UserStorageTest extends AbstractAuthTest { @Before public void addProvidersBeforeTest() throws URISyntaxException, IOException { + Assume.assumeTrue("User cache disabled.", isUserCacheEnabled()); + ComponentRepresentation memProvider = new ComponentRepresentation(); memProvider.setName("memory"); memProvider.setProviderId(UserMapStorageFactory.PROVIDER_ID); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java index fd539cee67..63ddfb18aa 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite.forms; import org.jboss.arquillian.graphene.page.Page; import org.junit.Assert; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.keycloak.authentication.AuthenticationFlow; @@ -179,6 +180,8 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest { @Test public void registerUpperCaseEmailWithChangedEmailAsUsername() throws IOException { + Assume.assumeTrue("See https://github.com/keycloak/keycloak/issues/10245", isUserCacheEnabled()); + String userId = registerUpperCaseAndGetUserId(false); assertThat(userId, notNullValue()); oauth.openLogout(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CacheTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CacheTest.java index 5a6696c7e0..3bcd33f1c3 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CacheTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CacheTest.java @@ -17,7 +17,7 @@ package org.keycloak.testsuite.model; - +import org.junit.Assume; import org.junit.Test; import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; @@ -58,6 +58,7 @@ public class CacheTest extends AbstractTestRealmKeycloakTest { @Test public void testStaleCache() throws Exception { + Assume.assumeTrue("Realm cache disabled.", isRealmCacheEnabled()); testingClient.server().run(session -> { String appId = null; { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java index 3ff33ac20e..1550ece5f5 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java @@ -449,12 +449,11 @@ public class UserSessionProviderTest extends AbstractTestRealmKeycloakTest { r.setSsoSessionMaxLifespanRememberMe(r.getSsoSessionMaxLifespan() * 4); r.setSsoSessionIdleTimeoutRememberMe(r.getSsoSessionIdleTimeout() * 4); }); - // update the realm reference so that the remember-me timeouts are now visible. - RealmModel realm = session.realms().getRealmByName("test"); // create an user session with remember-me enabled that is older than the default 'max lifespan' timeout but not older than the 'max lifespan remember-me' timeout. // the session's last refresh also exceeds the default 'session idle' timeout but doesn't exceed the 'session idle remember-me' timeout. KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession kcSession) -> { + RealmModel realm = kcSession.realms().getRealmByName("test"); Time.setOffset(-(realm.getSsoSessionMaxLifespan() * 2)); UserSessionModel userSession = kcSession.sessions().createUserSession(realm, kcSession.users().getUserByUsername(realm, "user1"), "user1", "127.0.0.1", "form", true, null, null); AuthenticatedClientSessionModel clientSession = kcSession.sessions().createClientSession(realm, client, userSession); @@ -468,6 +467,7 @@ public class UserSessionProviderTest extends AbstractTestRealmKeycloakTest { // create an user session with remember-me enabled that is older than the 'max lifespan remember-me' timeout. KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession kcSession) -> { + RealmModel realm = kcSession.realms().getRealmByName("test"); Time.setOffset(-(realm.getSsoSessionMaxLifespanRememberMe() + 1)); UserSessionModel userSession = kcSession.sessions().createUserSession(realm, kcSession.users().getUserByUsername(realm, "user1"), "user1", "127.0.0.1", "form", true, null, null); expiredUserSessions.add(userSession.getId()); @@ -475,6 +475,7 @@ public class UserSessionProviderTest extends AbstractTestRealmKeycloakTest { // finally create an user session with remember-me enabled whose last refresh exceeds the 'session idle remember-me' timeout. KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession kcSession) -> { + RealmModel realm = kcSession.realms().getRealmByName("test"); Time.setOffset(-(realm.getSsoSessionIdleTimeoutRememberMe() + SessionTimeoutHelper.PERIODIC_CLEANER_IDLE_TIMEOUT_WINDOW_SECONDS + 1)); UserSessionModel userSession = kcSession.sessions().createUserSession(realm, kcSession.users().getUserByUsername(realm, "user2"), "user2", "127.0.0.1", "form", true, null, null); // no need to explicitly set the last refresh time - it is the same as the creation time. @@ -483,21 +484,24 @@ public class UserSessionProviderTest extends AbstractTestRealmKeycloakTest { // remove the expired sessions - the first session should not be removed as it doesn't exceed any of the remember-me timeout values. Time.setOffset(0); - KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession kcSession) -> kcSession.sessions().removeExpired(realm)); + KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession kcSession) -> kcSession.sessions().removeExpired(kcSession.realms().getRealmByName("test"))); - for (String sessionId : expiredUserSessions) { - assertNull(session.sessions().getUserSession(realm, sessionId)); - } + KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession kcSession) -> { + RealmModel realm = kcSession.realms().getRealmByName("test"); - for (String sessionId : validUserSessions) { - UserSessionModel userSessionLoaded = session.sessions().getUserSession(realm, sessionId); - assertNotNull(userSessionLoaded); - // the only valid user session should also have a valid client session that hasn't expired. - AuthenticatedClientSessionModel clientSessionModel = userSessionLoaded.getAuthenticatedClientSessions().get(client.getId()); - assertNotNull(clientSessionModel); - assertTrue(validClientSessions.contains(clientSessionModel.getId())); - } + for (String sessionId : expiredUserSessions) { + assertNull(kcSession.sessions().getUserSession(realm, sessionId)); + } + for (String sessionId : validUserSessions) { + UserSessionModel userSessionLoaded = kcSession.sessions().getUserSession(realm, sessionId); + assertNotNull(userSessionLoaded); + // the only valid user session should also have a valid client session that hasn't expired. + AuthenticatedClientSessionModel clientSessionModel = userSessionLoaded.getAuthenticatedClientSessions().get(client.getId()); + assertNotNull(clientSessionModel); + assertTrue(validClientSessions.contains(clientSessionModel.getId())); + } + }); } finally { Time.setOffset(0); session.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/sessionlimits/UserSessionLimitsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/sessionlimits/UserSessionLimitsTest.java index c1be084b63..46b4860184 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/sessionlimits/UserSessionLimitsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/sessionlimits/UserSessionLimitsTest.java @@ -468,6 +468,7 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest { AuthenticationFlowModel flow = realm.getFlowByAlias(alias); AuthenticatorConfigModel configModel = realm.getAuthenticatorConfigByAlias("user-session-limits-" + flow.getId()); configModel.getConfig().put(key, value); + realm.updateAuthenticatorConfig(configModel); }); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json index 9c911b77cb..2a3bf97e02 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json @@ -174,9 +174,8 @@ }, "userCache": { - "provider": "${keycloak.user.cache.provider:default}", "default" : { - "enabled": true + "enabled": "${keycloak.userCache.enabled:true}" }, "mem": { "maxSize": 20000 @@ -235,9 +234,8 @@ }, "realmCache": { - "provider": "${keycloak.realm.cache.provider:default}", "default" : { - "enabled": true + "enabled": "${keycloak.realmCache.enabled:true}" } }, diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index d108cf9760..e76d5960cc 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -284,14 +284,6 @@ - - map+infinispan - - enabled - Infinispan,Jpa,Map,ConcurrentHashMapStorage - - - map