From f8f4de938914ba2302c905cde8f910f86c3b8b2b Mon Sep 17 00:00:00 2001 From: mposolda Date: Mon, 26 Oct 2015 11:05:38 +0100 Subject: [PATCH] KEYCLOAK-2005 Fixed migration to take less time. Added UserProvider.grantToAllUsers --- .../migration/migrators/MigrateTo1_6_0.java | 6 ++-- .../models/UserFederationManager.java | 6 ++++ .../org/keycloak/models/UserProvider.java | 2 ++ .../models/file/FileUserProvider.java | 7 +++++ .../infinispan/DefaultCacheUserProvider.java | 6 ++++ .../keycloak/models/jpa/JpaUserProvider.java | 8 ++++- .../jpa/entities/UserRoleMappingEntity.java | 3 +- .../keycloak/adapters/MongoUserProvider.java | 13 ++++++++ .../testsuite/model/UserModelTest.java | 31 +++++++++++++++++++ 9 files changed, 76 insertions(+), 6 deletions(-) diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java index 88817acb27..45c73a31cf 100644 --- a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java +++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java @@ -63,10 +63,8 @@ public class MigrateTo1_6_0 { KeycloakModelUtils.setupOfflineTokens(realm); RoleModel role = realm.getRole(Constants.OFFLINE_ACCESS_ROLE); - // Check if possible to avoid iterating over users - for (UserModel user : session.userStorage().getUsers(realm, true)) { - user.grantRole(role); - } + // Bulk grant of offline_access role to all users + session.users().grantToAllUsers(realm, role); } ClientModel adminConsoleClient = realm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID); 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 bea51e0473..cde5eb9411 100755 --- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java +++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java @@ -333,6 +333,12 @@ public class UserFederationManager implements UserProvider { return session.userStorage().getFederatedIdentity(user, socialProvider, realm); } + @Override + public void grantToAllUsers(RealmModel realm, RoleModel role) { + // not federation-aware for now + session.userStorage().grantToAllUsers(realm, role); + } + @Override public void preRemove(RealmModel realm) { for (UserFederationProviderModel federation : realm.getUserFederationProviders()) { 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 7d7064d76e..82be2fe160 100755 --- a/model/api/src/main/java/org/keycloak/models/UserProvider.java +++ b/model/api/src/main/java/org/keycloak/models/UserProvider.java @@ -43,6 +43,8 @@ public interface UserProvider extends Provider { Set getFederatedIdentities(UserModel user, RealmModel realm); FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm); + void grantToAllUsers(RealmModel realm, RoleModel role); + void preRemove(RealmModel realm); void preRemove(RealmModel realm, UserFederationProviderModel link); 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 8edfe3ec2e..8c24262a12 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 @@ -422,6 +422,13 @@ public class FileUserProvider implements UserProvider { return this.addUser(realm, KeycloakModelUtils.generateId(), username.toLowerCase(), true, true); } + @Override + public void grantToAllUsers(RealmModel realm, RoleModel role) { + for (UserModel user : inMemoryModel.getUsers(realm.getId())) { + user.grantRole(role); + } + } + @Override public void preRemove(RealmModel realm) { // Nothing to do here? Federation links are attached to users, which are removed by InMemoryModel 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 69fc5cf577..9045f911f0 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 @@ -303,6 +303,12 @@ public class DefaultCacheUserProvider implements CacheUserProvider { return getDelegate().validCredentials(realm, input); } + @Override + public void grantToAllUsers(RealmModel realm, RoleModel role) { + realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm + getDelegate().grantToAllUsers(realm, role); + } + @Override public void preRemove(RealmModel realm) { realmInvalidations.add(realm.getId()); 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 d4d533a9dc..c9031476cf 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 @@ -147,7 +147,13 @@ public class JpaUserProvider implements UserProvider { } } - + @Override + public void grantToAllUsers(RealmModel realm, RoleModel role) { + int num = em.createNamedQuery("grantRoleToAllUsers") + .setParameter("realmId", realm.getId()) + .setParameter("roleId", role.getId()) + .executeUpdate(); + } @Override public void preRemove(RealmModel realm) { diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java index 1ce81e8297..45dfc3de95 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java @@ -23,7 +23,8 @@ import java.io.Serializable; @NamedQuery(name="deleteUserRoleMappingsByRealm", query="delete from UserRoleMappingEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId)"), @NamedQuery(name="deleteUserRoleMappingsByRealmAndLink", query="delete from UserRoleMappingEntity mapping where mapping.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"), @NamedQuery(name="deleteUserRoleMappingsByRole", query="delete from UserRoleMappingEntity m where m.roleId = :roleId"), - @NamedQuery(name="deleteUserRoleMappingsByUser", query="delete from UserRoleMappingEntity m where m.user = :user") + @NamedQuery(name="deleteUserRoleMappingsByUser", query="delete from UserRoleMappingEntity m where m.user = :user"), + @NamedQuery(name="grantRoleToAllUsers", query="insert into UserRoleMappingEntity (roleId, user) select role.id, user from RoleEntity role, UserEntity user where role.id = :roleId AND role.realm.id = :realmId AND user.realmId = :realmId") }) @Table(name="USER_ROLE_MAPPING") 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 358e6f2d28..9fc9735559 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 @@ -368,6 +368,19 @@ public class MongoUserProvider implements UserProvider { return this.addUser(realm, null, username, true, true); } + @Override + public void grantToAllUsers(RealmModel realm, RoleModel role) { + DBObject query = new QueryBuilder() + .and("realmId").is(realm.getId()) + .get(); + + DBObject update = new QueryBuilder() + .and("$push").is(new BasicDBObject("roleIds", role.getId())) + .get(); + + int count = getMongoStore().updateEntities(MongoUserEntity.class, query, update, invocationContext); + } + @Override public void preRemove(RealmModel realm) { DBObject query = new QueryBuilder() 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 258dd3cbd7..66018552c5 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 @@ -5,6 +5,7 @@ import org.junit.Test; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserModel.RequiredAction; import org.keycloak.services.managers.ClientManager; @@ -283,6 +284,36 @@ public class UserModelTest extends AbstractModelTest { Assert.assertNull(session.users().getUserByUsername("user1", realm)); } + @Test + public void testGrantToAll() { + RealmModel realm1 = realmManager.createRealm("realm1"); + RoleModel role1 = realm1.addRole("role1"); + UserModel user1 = realmManager.getSession().users().addUser(realm1, "user1"); + UserModel user2 = realmManager.getSession().users().addUser(realm1, "user2"); + + RealmModel realm2 = realmManager.createRealm("realm2"); + UserModel realm2User1 = realmManager.getSession().users().addUser(realm2, "user1"); + + commit(); + + realm1 = realmManager.getRealmByName("realm1"); + role1 = realm1.getRole("role1"); + realmManager.getSession().users().grantToAllUsers(realm1, role1); + + commit(); + + realm1 = realmManager.getRealmByName("realm1"); + role1 = realm1.getRole("role1"); + user1 = realmManager.getSession().users().getUserByUsername("user1", realm1); + user2 = realmManager.getSession().users().getUserByUsername("user2", realm1); + Assert.assertTrue(user1.hasRole(role1)); + Assert.assertTrue(user2.hasRole(role1)); + + realm2 = realmManager.getRealmByName("realm2"); + realm2User1 = realmManager.getSession().users().getUserByUsername("user1", realm2); + Assert.assertFalse(realm2User1.hasRole(role1)); + } + public static void assertEquals(UserModel expected, UserModel actual) { Assert.assertEquals(expected.getUsername(), actual.getUsername()); Assert.assertEquals(expected.getCreatedTimestamp(), actual.getCreatedTimestamp());