From 493fd0ad6ab64853cbe7aacb64ce85efb636e857 Mon Sep 17 00:00:00 2001 From: mposolda Date: Thu, 3 Sep 2015 11:15:06 +0200 Subject: [PATCH 1/2] KEYCLOAK-1760 Fix DB issues when schema option defined --- .../LiquibaseJpaUpdaterProvider.java | 2 +- .../liquibase/custom/AddRealmCodeSecret.java | 3 ++- .../liquibase/custom/CustomKeycloakTask.java | 8 +++++++- .../custom/JpaUpdate1_2_0_Beta1.java | 19 ++++++++++++------- .../liquibase/custom/JpaUpdate1_2_0_CR1.java | 2 +- .../custom/JpaUpdate1_4_0_Final.java | 2 +- .../META-INF/jpa-changelog-1.5.0.xml | 5 +++++ .../DefaultJpaConnectionProviderFactory.java | 3 ++- .../connections/jpa/util/JpaUtils.java | 16 ++++++++++++++++ .../keycloak/models/jpa/ClientAdapter.java | 4 +++- .../org/keycloak/models/jpa/RealmAdapter.java | 4 +++- 11 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 connections/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java index e205de6eb5..e4565c8f9d 100644 --- a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java +++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java @@ -231,7 +231,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider { } - private String getTable(String table, String defaultSchema) { + public static String getTable(String table, String defaultSchema) { return defaultSchema != null ? defaultSchema + "." + table : table; } diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AddRealmCodeSecret.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AddRealmCodeSecret.java index 48941d3981..3190485b1e 100644 --- a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AddRealmCodeSecret.java +++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AddRealmCodeSecret.java @@ -12,6 +12,7 @@ import liquibase.statement.SqlStatement; import liquibase.statement.core.UpdateStatement; import liquibase.structure.core.Schema; import liquibase.structure.core.Table; +import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider; import org.keycloak.models.utils.KeycloakModelUtils; import java.sql.Connection; @@ -36,7 +37,7 @@ public class AddRealmCodeSecret implements CustomSqlChange { String correctedTableName = database.correctObjectName("REALM", Table.class); if (SnapshotGeneratorFactory.getInstance().has(new Table().setName(correctedTableName), database)) { - ResultSet resultSet = connection.createStatement().executeQuery("SELECT ID FROM REALM WHERE CODE_SECRET IS NULL"); + ResultSet resultSet = connection.createStatement().executeQuery("SELECT ID FROM " + LiquibaseJpaUpdaterProvider.getTable(correctedTableName, database.getDefaultSchemaName()) + " WHERE CODE_SECRET IS NULL"); while (resultSet.next()) { String id = resultSet.getString(1); diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/CustomKeycloakTask.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/CustomKeycloakTask.java index 1b63bdb281..6b0ea2cb4c 100644 --- a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/CustomKeycloakTask.java +++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/CustomKeycloakTask.java @@ -16,6 +16,7 @@ import liquibase.snapshot.SnapshotGeneratorFactory; import liquibase.statement.SqlStatement; import liquibase.structure.core.Table; import org.jboss.logging.Logger; +import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider; import org.keycloak.connections.jpa.updater.liquibase.ThreadLocalSessionContext; import org.keycloak.models.KeycloakSession; import org.keycloak.services.DefaultKeycloakSessionFactory; @@ -88,7 +89,7 @@ public abstract class CustomKeycloakTask implements CustomSqlChange { try { String correctedTableName = database.correctObjectName("REALM", Table.class); if (SnapshotGeneratorFactory.getInstance().has(new Table().setName(correctedTableName), database)) { - ResultSet resultSet = connection.createStatement().executeQuery("SELECT ID FROM REALM"); + ResultSet resultSet = connection.createStatement().executeQuery("SELECT ID FROM " + getTableName(correctedTableName)); try { return (resultSet.next()); } finally { @@ -108,4 +109,9 @@ public abstract class CustomKeycloakTask implements CustomSqlChange { protected abstract void generateStatementsImpl() throws CustomChangeException; protected abstract String getTaskId(); + + // get Table name for sql selects + protected String getTableName(String tableName) { + return LiquibaseJpaUpdaterProvider.getTable(tableName, database.getDefaultSchemaName()); + } } diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java index 895a7856a7..70c62372f6 100644 --- a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java +++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java @@ -11,6 +11,7 @@ import liquibase.statement.core.InsertStatement; import liquibase.statement.core.UpdateStatement; import liquibase.structure.core.Table; import org.keycloak.Config; +import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider; import org.keycloak.migration.MigrationProvider; import org.keycloak.models.AdminRoles; import org.keycloak.models.ClaimMask; @@ -49,7 +50,10 @@ public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask { String identityProviderTableName = database.correctObjectName("IDENTITY_PROVIDER", Table.class); String idpConfigTableName = database.correctObjectName("IDENTITY_PROVIDER_CONFIG", Table.class); - PreparedStatement statement = jdbcConnection.prepareStatement("select RSC.NAME, VALUE, REALM_ID, UPDATE_PROFILE_ON_SOC_LOGIN from REALM_SOCIAL_CONFIG RSC,REALM where RSC.REALM_ID = REALM.ID ORDER BY RSC.REALM_ID, RSC.NAME"); + String realmSocialConfigTable = getTableName("REALM_SOCIAL_CONFIG"); + String realmTableName = getTableName("REALM"); + PreparedStatement statement = jdbcConnection.prepareStatement("select RSC.NAME, VALUE, REALM_ID, UPDATE_PROFILE_ON_SOC_LOGIN from " + realmSocialConfigTable + " RSC," + realmTableName + + " REALM where RSC.REALM_ID = REALM.ID ORDER BY RSC.REALM_ID, RSC.NAME"); try { ResultSet resultSet = statement.executeQuery(); try { @@ -124,7 +128,7 @@ public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask { protected void convertSocialToIdFedUsers() throws SQLException, DatabaseException { String federatedIdentityTableName = database.correctObjectName("FEDERATED_IDENTITY", Table.class); - PreparedStatement statement = jdbcConnection.prepareStatement("select REALM_ID, USER_ID, SOCIAL_PROVIDER, SOCIAL_USER_ID, SOCIAL_USERNAME from USER_SOCIAL_LINK"); + PreparedStatement statement = jdbcConnection.prepareStatement("select REALM_ID, USER_ID, SOCIAL_PROVIDER, SOCIAL_USER_ID, SOCIAL_USERNAME from " + getTableName("USER_SOCIAL_LINK")); try { ResultSet resultSet = statement.executeQuery(); try { @@ -170,7 +174,7 @@ public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask { String adminRoleId = getAdminRoleId(); String masterRealmId = Config.getAdminRealm(); - PreparedStatement statement = jdbcConnection.prepareStatement("select NAME from REALM"); + PreparedStatement statement = jdbcConnection.prepareStatement("select NAME from " + getTableName("REALM")); try { ResultSet resultSet = statement.executeQuery(); try { @@ -178,7 +182,7 @@ public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask { String realmName = resultSet.getString("NAME"); String masterAdminAppName = realmName + "-realm"; - PreparedStatement statement2 = jdbcConnection.prepareStatement("select ID from CLIENT where REALM_ID = ? AND NAME = ?"); + PreparedStatement statement2 = jdbcConnection.prepareStatement("select ID from " + getTableName("CLIENT") + " where REALM_ID = ? AND NAME = ?"); statement2.setString(1, masterRealmId); statement2.setString(2, masterAdminAppName); @@ -209,7 +213,7 @@ public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask { } private String getAdminRoleId() throws SQLException, DatabaseException { - PreparedStatement statement = jdbcConnection.prepareStatement("select ID from KEYCLOAK_ROLE where NAME = ? AND REALM = ?"); + PreparedStatement statement = jdbcConnection.prepareStatement("select ID from " + getTableName("KEYCLOAK_ROLE") + " where NAME = ? AND REALM = ?"); statement.setString(1, AdminRoles.ADMIN); statement.setString(2, Config.getAdminRealm()); @@ -231,7 +235,8 @@ public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask { protected void addNewRealmAdminRoles() throws SQLException, DatabaseException { - PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.ID REALM_ADMIN_APP_ID, CLIENT.REALM_ID REALM_ID, KEYCLOAK_ROLE.ID ADMIN_ROLE_ID from CLIENT,KEYCLOAK_ROLE where KEYCLOAK_ROLE.APPLICATION = CLIENT.ID AND CLIENT.NAME = 'realm-management' AND KEYCLOAK_ROLE.NAME = ?"); + PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.ID REALM_ADMIN_APP_ID, CLIENT.REALM_ID REALM_ID, KEYCLOAK_ROLE.ID ADMIN_ROLE_ID from " + + getTableName("CLIENT") + " CLIENT," + getTableName("KEYCLOAK_ROLE") + " KEYCLOAK_ROLE where KEYCLOAK_ROLE.APPLICATION = CLIENT.ID AND CLIENT.NAME = 'realm-management' AND KEYCLOAK_ROLE.NAME = ?"); statement.setString(1, AdminRoles.REALM_ADMIN); try { @@ -280,7 +285,7 @@ public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask { String protocolMapperTableName = database.correctObjectName("PROTOCOL_MAPPER", Table.class); String protocolMapperCfgTableName = database.correctObjectName("PROTOCOL_MAPPER_CONFIG", Table.class); - PreparedStatement statement = jdbcConnection.prepareStatement("select ID, NAME, ALLOWED_CLAIMS_MASK from CLIENT"); + PreparedStatement statement = jdbcConnection.prepareStatement("select ID, NAME, ALLOWED_CLAIMS_MASK from " + getTableName("CLIENT")); try { ResultSet resultSet = statement.executeQuery(); diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_CR1.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_CR1.java index 00f9b0cd1e..07d6c7e14d 100644 --- a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_CR1.java +++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_CR1.java @@ -19,7 +19,7 @@ public class JpaUpdate1_2_0_CR1 extends CustomKeycloakTask { try { String trueValue = DataTypeFactory.getInstance().getTrueBooleanValue(database); - PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.REALM_ID, CLIENT.ID CLIENT_ID from CLIENT where CLIENT.CONSENT_REQUIRED = " + trueValue); + PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.REALM_ID, CLIENT.ID CLIENT_ID from " + getTableName("CLIENT") + " CLIENT where CLIENT.CONSENT_REQUIRED = " + trueValue); try { ResultSet resultSet = statement.executeQuery(); diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_4_0_Final.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_4_0_Final.java index 1f60d6bdcd..579b108cdf 100644 --- a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_4_0_Final.java +++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_4_0_Final.java @@ -20,7 +20,7 @@ public class JpaUpdate1_4_0_Final extends CustomKeycloakTask { String userAttributeTableName = database.correctObjectName("USER_ATTRIBUTE", Table.class); try { - PreparedStatement statement = jdbcConnection.prepareStatement("select NAME, USER_ID from USER_ATTRIBUTE"); + PreparedStatement statement = jdbcConnection.prepareStatement("select NAME, USER_ID from " + getTableName("USER_ATTRIBUTE")); try { ResultSet resultSet = statement.executeQuery(); diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml index 9ad3c64d35..52930a3a78 100755 --- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml +++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml @@ -61,5 +61,10 @@ + + + + + diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java index a79c82b228..e011e114f3 100755 --- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java +++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java @@ -4,6 +4,7 @@ import org.hibernate.ejb.AvailableSettings; import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.connections.jpa.updater.JpaUpdaterProvider; +import org.keycloak.connections.jpa.util.JpaUtils; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; @@ -108,7 +109,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide String schema = config.get("schema"); if (schema != null) { - properties.put("hibernate.default_schema", schema); + properties.put(JpaUtils.HIBERNATE_DEFAULT_SCHEMA, schema); } if (databaseSchema != null) { diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java new file mode 100644 index 0000000000..7e29d31ab2 --- /dev/null +++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java @@ -0,0 +1,16 @@ +package org.keycloak.connections.jpa.util; + +import javax.persistence.EntityManager; + +/** + * @author Marek Posolda + */ +public class JpaUtils { + + public static final String HIBERNATE_DEFAULT_SCHEMA = "hibernate.default_schema"; + + public static String getTableNameForNativeQuery(String tableName, EntityManager em) { + String schema = (String) em.getEntityManagerFactory().getProperties().get(HIBERNATE_DEFAULT_SCHEMA); + return (schema==null) ? tableName : schema + "." + tableName; + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java index c2fab6f776..fc551c7edf 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java @@ -1,5 +1,6 @@ package org.keycloak.models.jpa; +import org.keycloak.connections.jpa.util.JpaUtils; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; @@ -522,7 +523,8 @@ public class ClientAdapter implements ClientModel { entity.getRoles().remove(role); entity.getDefaultRoles().remove(role); - em.createNativeQuery("delete from COMPOSITE_ROLE where CHILD_ROLE = :role").setParameter("role", role).executeUpdate(); + String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em); + em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", role).executeUpdate(); em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", role).executeUpdate(); role.setClient(null); em.flush(); 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 2d099d60ae..6268735339 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 @@ -1,5 +1,6 @@ package org.keycloak.models.jpa; +import org.keycloak.connections.jpa.util.JpaUtils; import org.keycloak.enums.SslRequired; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationFlowModel; @@ -973,7 +974,8 @@ public class RealmAdapter implements RealmModel { realm.getRoles().remove(roleEntity); realm.getDefaultRoles().remove(roleEntity); - em.createNativeQuery("delete from COMPOSITE_ROLE where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate(); + String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em); + em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate(); em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", roleEntity).executeUpdate(); em.remove(roleEntity); From 89660d06e57d1f68c2e054695ecd426e1d4f4b3c Mon Sep 17 00:00:00 2001 From: mposolda Date: Thu, 3 Sep 2015 11:47:06 +0200 Subject: [PATCH 2/2] KEYCLOAK-1697 Fix UserProvider.searchForUsersByAttribute --- .../models/UserFederationManager.java | 4 +-- .../org/keycloak/models/UserProvider.java | 2 +- .../models/file/FileUserProvider.java | 18 ++++------- .../infinispan/DefaultCacheUserProvider.java | 4 +-- .../keycloak/models/jpa/JpaUserProvider.java | 32 +++++-------------- .../jpa/entities/UserAttributeEntity.java | 1 + .../keycloak/adapters/MongoUserProvider.java | 7 ++-- .../testsuite/model/UserModelTest.java | 21 ++++++------ 8 files changed, 34 insertions(+), 55 deletions(-) diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java index fd4569ba20..13c05f8908 100755 --- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java +++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java @@ -314,8 +314,8 @@ public class UserFederationManager implements UserProvider { } @Override - public List searchForUserByUserAttributes(Map attributes, RealmModel realm) { - return session.userStorage().searchForUserByUserAttributes(attributes, realm); + public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + return session.userStorage().searchForUserByUserAttribute(attrName, attrValue, realm); } @Override diff --git a/model/api/src/main/java/org/keycloak/models/UserProvider.java b/model/api/src/main/java/org/keycloak/models/UserProvider.java index 1690b7a70c..8712f2bfc4 100755 --- a/model/api/src/main/java/org/keycloak/models/UserProvider.java +++ b/model/api/src/main/java/org/keycloak/models/UserProvider.java @@ -37,7 +37,7 @@ public interface UserProvider extends Provider { List searchForUserByAttributes(Map attributes, RealmModel realm, int firstResult, int maxResults); // Searching by UserModel.attribute (not property) - List searchForUserByUserAttributes(Map attributes, RealmModel realm); + List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm); Set getFederatedIdentities(UserModel user, RealmModel realm); FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm); diff --git a/model/file/src/main/java/org/keycloak/models/file/FileUserProvider.java b/model/file/src/main/java/org/keycloak/models/file/FileUserProvider.java index 0bcc37a8b2..40547a82db 100755 --- a/model/file/src/main/java/org/keycloak/models/file/FileUserProvider.java +++ b/model/file/src/main/java/org/keycloak/models/file/FileUserProvider.java @@ -255,22 +255,18 @@ public class FileUserProvider implements UserProvider { } @Override - public List searchForUserByUserAttributes(Map attributes, RealmModel realm) { + public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { Collection users = inMemoryModel.getUsers(realm.getId()); - for (Map.Entry entry : attributes.entrySet()) { - - List matchedUsers = new ArrayList<>(); - for (UserModel user : users) { - List vals = user.getAttribute(entry.getKey()); - if (vals.contains(entry.getValue())) { - matchedUsers.add(user); - } + List matchedUsers = new ArrayList<>(); + for (UserModel user : users) { + List vals = user.getAttribute(attrName); + if (vals.contains(attrValue)) { + matchedUsers.add(user); } - users = matchedUsers; } - return (List) users; + return matchedUsers; } @Override diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java index a34d70ac45..68b31edb3b 100644 --- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java +++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java @@ -235,8 +235,8 @@ public class DefaultCacheUserProvider implements CacheUserProvider { } @Override - public List searchForUserByUserAttributes(Map attributes, RealmModel realm) { - return getDelegate().searchForUserByUserAttributes(attributes, realm); + public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + return getDelegate().searchForUserByUserAttribute(attrName, attrValue, realm); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java index 4f02f6c090..d92e93ddea 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java @@ -13,6 +13,7 @@ import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserProvider; import org.keycloak.models.jpa.entities.FederatedIdentityEntity; +import org.keycloak.models.jpa.entities.UserAttributeEntity; import org.keycloak.models.jpa.entities.UserEntity; import org.keycloak.models.utils.CredentialValidation; import org.keycloak.models.utils.KeycloakModelUtils; @@ -400,32 +401,15 @@ public class JpaUserProvider implements UserProvider { } @Override - public List searchForUserByUserAttributes(Map attributes, RealmModel realm) { - StringBuilder builder = new StringBuilder("select attr.user,count(attr.user) from UserAttributeEntity attr where attr.user.realmId = :realmId"); - boolean first = true; - for (Map.Entry entry : attributes.entrySet()) { - String attrName = entry.getKey(); - if (first) { - builder.append(" and "); - first = false; - } else { - builder.append(" or "); - } - builder.append(" ( attr.name like :").append(attrName); - builder.append(" and attr.value like :").append(attrName).append("val )"); - } - builder.append(" group by attr.user having count(attr.user) = " + attributes.size()); - Query query = em.createQuery(builder.toString()); - query.setParameter("realmId", realm.getId()); - for (Map.Entry entry : attributes.entrySet()) { - query.setParameter(entry.getKey(), entry.getKey()); - query.setParameter(entry.getKey() + "val", entry.getValue()); - } - List results = query.getResultList(); + public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + TypedQuery query = em.createNamedQuery("getAttributesByNameAndValue", UserAttributeEntity.class); + query.setParameter("name", attrName); + query.setParameter("value", attrValue); + List results = query.getResultList(); List users = new ArrayList(); - for (Object o : results) { - UserEntity user = (UserEntity) ((Object[])o)[0]; + for (UserAttributeEntity attr : results) { + UserEntity user = attr.getUser(); users.add(new UserAdapter(realm, em, user)); } return users; diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java index 430c796421..ec0eb8614e 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java @@ -21,6 +21,7 @@ import java.util.Set; * @version $Revision: 1 $ */ @NamedQueries({ + @NamedQuery(name="getAttributesByNameAndValue", query="select attr from UserAttributeEntity attr where attr.name = :name and attr.value = :value"), @NamedQuery(name="deleteUserAttributesByRealm", query="delete from UserAttributeEntity attr where attr.user IN (select u from UserEntity u where u.realmId=:realmId)"), @NamedQuery(name="deleteUserAttributesByRealmAndLink", query="delete from UserAttributeEntity attr where attr.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)") }) diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java index a433fead56..308d9fb1d4 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java @@ -231,13 +231,10 @@ public class MongoUserProvider implements UserProvider { } @Override - public List searchForUserByUserAttributes(Map attributes, RealmModel realm) { + public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { QueryBuilder queryBuilder = new QueryBuilder() .and("realmId").is(realm.getId()); - - for (Map.Entry entry : attributes.entrySet()) { - queryBuilder.and("attributes." + entry.getKey()).is(entry.getValue()); - } + queryBuilder.and("attributes." + attrName).is(attrValue); List users = getMongoStore().loadEntities(MongoUserEntity.class, queryBuilder.get(), invocationContext); return convertUserEntities(realm, users); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java index 8fbfdcec78..258dd3cbd7 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java @@ -193,8 +193,8 @@ public class UserModelTest extends AbstractModelTest { Assert.assertEquals("val23", attrVals.get(0)); } - // @Test - public void testSearchByUserAttributes() throws Exception { + @Test + public void testSearchByUserAttribute() throws Exception { RealmModel realm = realmManager.createRealm("original"); UserModel user1 = session.users().addUser(realm, "user1"); UserModel user2 = session.users().addUser(realm, "user2"); @@ -210,20 +210,21 @@ public class UserModelTest extends AbstractModelTest { commit(); - Map attributes = new HashMap(); - attributes.put("key1", "value1"); - List users = session.users().searchForUserByUserAttributes(attributes, realm); + List users = session.users().searchForUserByUserAttribute("key1", "value1", realm); Assert.assertEquals(2, users.size()); Assert.assertTrue(users.contains(user1)); Assert.assertTrue(users.contains(user2)); - attributes.put("key2", "value21"); - users = session.users().searchForUserByUserAttributes(attributes, realm); - Assert.assertEquals(1, users.size()); + users = session.users().searchForUserByUserAttribute("key2", "value21", realm); + Assert.assertEquals(2, users.size()); Assert.assertTrue(users.contains(user1)); + Assert.assertTrue(users.contains(user3)); - attributes.put("key3", "value3"); - users = session.users().searchForUserByUserAttributes(attributes, realm); + users = session.users().searchForUserByUserAttribute("key2", "value22", realm); + Assert.assertEquals(1, users.size()); + Assert.assertTrue(users.contains(user2)); + + users = session.users().searchForUserByUserAttribute("key3", "value3", realm); Assert.assertEquals(0, users.size()); }