diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java index 7565e2471b..ff22c5a574 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java @@ -140,6 +140,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory roles) { + KeycloakSession session = authorization.getKeycloakSession(); RealmModel realm = authorization.getRealm(); Set updatedRoles = new HashSet<>(); @@ -175,7 +176,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null) + role = realm.getClientsStream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null) .findFirst().orElse(null); } @@ -215,7 +216,8 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory updateResourceServer(clientModel, removedRole, resourceServerStore, policyStore)); + realm.getClientsStream() + .forEach(clientModel -> updateResourceServer(clientModel, removedRole, resourceServerStore, policyStore)); } else { ClientModel clientModel = (ClientModel) container; updateResourceServer(clientModel, removedRole, resourceServerStore, policyStore); diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java index 14622c5640..8194e1c200 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java @@ -182,7 +182,7 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory { + realm.getClientsStream().forEach(clientModel -> { ResourceServer resourceServer = resourceServerStore.findById(clientModel.getId()); if (resourceServer != null) { diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java index 394fbc1247..46f4d8c57b 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java @@ -38,6 +38,7 @@ import org.keycloak.storage.ldap.mappers.AbstractLDAPStorageMapper; import org.keycloak.storage.ldap.mappers.membership.CommonLDAPGroupMapper; import org.keycloak.storage.ldap.mappers.membership.CommonLDAPGroupMapperConfig; import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode; +import org.keycloak.storage.ldap.mappers.membership.MembershipType; import org.keycloak.storage.ldap.mappers.membership.UserRolesRetrieveStrategy; import org.keycloak.storage.user.SynchronizationResult; @@ -46,7 +47,8 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.keycloak.storage.ldap.mappers.membership.MembershipType; +import java.util.function.Consumer; +import java.util.stream.Stream; /** * Map realm roles or roles of particular client to LDAP groups @@ -178,10 +180,9 @@ public class RoleLDAPStorageMapper extends AbstractLDAPStorageMapper implements RoleContainerModel roleContainer = getTargetRoleContainer(realm); - Set keycloakRoles = roleContainer.getRoles(); + Stream keycloakRoles = roleContainer.getRolesStream(); - for (RoleModel keycloakRole : keycloakRoles) { - String roleName = keycloakRole.getName(); + Consumer syncRoleFromKCToLDAP = roleName -> { if (ldapRoleNames.contains(roleName)) { syncResult.increaseUpdated(); } else { @@ -189,7 +190,8 @@ public class RoleLDAPStorageMapper extends AbstractLDAPStorageMapper implements createLDAPRole(roleName); syncResult.increaseAdded(); } - } + }; + keycloakRoles.map(RoleModel::getName).forEach(syncRoleFromKCToLDAP); return syncResult; } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java index dce35317ae..989dd1e12d 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Stream; @@ -661,19 +662,9 @@ public class ClientAdapter implements ClientModel, CachedObject { if (isUpdated()) return updated.hasScope(role); if (cached.isFullScopeAllowed() || cached.getScope().contains(role.getId())) return true; - Set roles = getScopeMappings(); + if (getScopeMappingsStream().anyMatch(r -> r.hasRole(role))) return true; - for (RoleModel mapping : roles) { - if (mapping.hasRole(role)) return true; - } - - roles = getRoles(); - if (roles.contains(role)) return true; - - for (RoleModel mapping : roles) { - if (mapping.hasRole(role)) return true; - } - return false; + return getRolesStream().anyMatch(r -> (Objects.equals(r, role) || r.hasRole(role))); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java index 3495913bab..dd3af804b9 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java @@ -583,9 +583,9 @@ public class RealmCacheSession implements CacheRealmProvider { invalidationEvents.add(ClientRemovedEvent.create(client)); cache.clientRemoval(realm.getId(), id, client.getClientId(), invalidations); - for (RoleModel role : client.getRoles()) { + client.getRolesStream().forEach(role -> { roleRemovalInvalidations(role.getId(), role.getName(), client.getId()); - } + }); if (client.isServiceAccountsEnabled()) { UserModel serviceAccount = session.users().getServiceAccount(client); diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientRemovedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientRemovedEvent.java index 2d4869327d..3b0b0a3d95 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientRemovedEvent.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientRemovedEvent.java @@ -17,7 +17,6 @@ package org.keycloak.models.cache.infinispan.events; -import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -29,6 +28,8 @@ import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + import org.infinispan.commons.marshall.Externalizer; import org.infinispan.commons.marshall.MarshallUtil; import org.infinispan.commons.marshall.SerializeWith; @@ -51,10 +52,7 @@ public class ClientRemovedEvent extends InvalidationEvent implements RealmCacheI event.realmId = client.getRealm().getId(); event.clientUuid = client.getId(); event.clientId = client.getClientId(); - event.clientRoles = new HashMap<>(); - for (RoleModel clientRole : client.getRoles()) { - event.clientRoles.put(clientRole.getId(), clientRole.getName()); - } + event.clientRoles = client.getRolesStream().collect(Collectors.toMap(RoleModel::getId, RoleModel::getName)); return event; } 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 7436303613..206b4ecacd 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 @@ -46,6 +46,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Stream; /** @@ -688,19 +689,14 @@ public class ClientAdapter implements ClientModel, JpaModel { @Override public boolean hasScope(RoleModel role) { if (isFullScopeAllowed()) return true; - Set roles = getScopeMappings(); - if (roles.contains(role)) return true; - for (RoleModel mapping : roles) { - if (mapping.hasRole(role)) return true; - } - roles = getRoles(); - if (roles.contains(role)) return true; + Predicate hasRoleOrEquals = r -> (Objects.equals(r, role) || r.hasRole(role)); - for (RoleModel mapping : roles) { - if (mapping.hasRole(role)) return true; + if (getScopeMappingsStream().anyMatch(hasRoleOrEquals)) { + return true; } - return false; + + return getRolesStream().anyMatch(hasRoleOrEquals); } @Override 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 b23aab8be6..262ec3e69e 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 @@ -26,9 +26,6 @@ import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import com.google.common.base.Functions; import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -454,10 +451,7 @@ public abstract class MapClientAdapter extends AbstractClientModel roles = getRoles(); - if (roles.contains(role)) return true; - - return roles.stream().anyMatch(r -> r.hasRole(role)); + return getRolesStream().anyMatch(r -> (Objects.equals(r, role) || r.hasRole(role))); } /*************** Default roles ****************/ diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java index d1f76c47a8..378b979ce7 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java @@ -25,8 +25,6 @@ import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel.ClientUpdatedEvent; -import org.keycloak.models.RealmProvider; -import org.keycloak.models.RoleModel; import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.common.Serialization; import java.util.Comparator; @@ -41,7 +39,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.keycloak.models.map.storage.MapStorage; import static org.keycloak.common.util.StackUtil.getShortStackTrace; -import org.keycloak.models.RoleProvider; public class MapClientProvider implements ClientProvider { diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java index 476c0d34e1..ecfc859eaf 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java @@ -35,7 +35,7 @@ public class RealmSynchronizer implements Synchronizer { AuthorizationProvider authorizationProvider = providerFactory.create(event.getKeycloakSession()); StoreFactory storeFactory = authorizationProvider.getStoreFactory(); - event.getRealm().getClients().forEach(clientModel -> { + event.getRealm().getClientsStream().forEach(clientModel -> { ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); if (resourceServer != null) { diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java index af4c997163..892c9050dc 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java @@ -89,7 +89,7 @@ public class UserSynchronizer implements Synchronizer { RealmModel realm = event.getRealm(); UserModel userModel = event.getUser(); - realm.getClients().forEach(clientModel -> { + realm.getClientsStream().forEach(clientModel -> { ResourceServer resourceServer = resourceServerStore.findById(clientModel.getId()); if (resourceServer != null) { diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java index ec019f216d..8ccb3ef458 100755 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java @@ -19,12 +19,10 @@ package org.keycloak.migration.migrators; import org.keycloak.migration.ModelVersion; import org.keycloak.models.AuthenticationFlowModel; -import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.OTPPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.DefaultAuthenticationFlows; -import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.idm.RealmRepresentation; import java.util.List; @@ -43,17 +41,17 @@ public class MigrateTo1_5_0 implements Migration { public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { - migrateRealm(realm); + migrateRealm(session, realm); } } @Override public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) { - migrateRealm(realm); + migrateRealm(session, realm); } - protected void migrateRealm(RealmModel realm) { + protected void migrateRealm(KeycloakSession session, RealmModel realm) { DefaultAuthenticationFlows.migrateFlows(realm); // add reset credentials flo realm.setOTPPolicy(OTPPolicy.DEFAULT_POLICY); realm.setBrowserFlow(realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW)); @@ -74,8 +72,6 @@ public class MigrateTo1_5_0 implements Migration { realm.setClientAuthenticationFlow(clientAuthFlow); } - for (ClientModel client : realm.getClients()) { - client.setClientAuthenticatorType(KeycloakModelUtils.getDefaultClientAuthenticatorType()); - } + realm.getClientsStream().forEach(MigrationUtils::setDefaultClientAuthenticatorType); } } diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java index 2bb7f88360..550dac4ccf 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java @@ -74,7 +74,7 @@ public class MigrateTo2_1_0 implements Migration { AuthorizationProvider authorizationProvider = session.getProvider(AuthorizationProvider.class); StoreFactory storeFactory = authorizationProvider.getStoreFactory(); PolicyStore policyStore = storeFactory.getPolicyStore(); - realm.getClients().forEach(clientModel -> { + realm.getClientsStream().forEach(clientModel -> { ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); if (resourceServer != null) { diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java index af9fe9ec72..a2e8c1a149 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java @@ -19,7 +19,6 @@ package org.keycloak.migration.migrators; import org.keycloak.migration.ModelVersion; -import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -40,9 +39,7 @@ public class MigrateTo2_3_0 implements Migration { } protected void migrateRealm(RealmModel realm) { - for (ClientModel client : realm.getClients()) { - MigrationUtils.updateProtocolMappers(client); - } + realm.getClientsStream().forEach(MigrationUtils::updateProtocolMappers); for (ClientScopeModel clientScope : realm.getClientScopes()) { MigrationUtils.updateProtocolMappers(clientScope); diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_0_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_0_0.java index f9667659cd..2e8eb3d94a 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_0_0.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_0_0.java @@ -55,7 +55,7 @@ public class MigrateTo3_0_0 implements Migration { } protected void migrateRealm(RealmModel realm) { - realm.getClients().stream() + realm.getClientsStream() .filter(clientModel -> defaultClients.contains(clientModel.getId())) .filter(clientModel -> Objects.isNull(clientModel.getProtocol())) .forEach(clientModel -> clientModel.setProtocol("openid-connect")); diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java index 659ca624ae..96e0a440a1 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java @@ -94,7 +94,7 @@ public class MigrateTo4_0_0 implements Migration { // If client has scope for offline_access role (either directly or through fullScopeAllowed), then add offline_access client // scope as optional scope to the client. If it's indirectly (no fullScopeAllowed), then remove role from the scoped roles RoleModel offlineAccessRole = realm.getRole(OAuth2Constants.OFFLINE_ACCESS); - ClientScopeModel offlineAccessScope = null; + ClientScopeModel offlineAccessScope; if (offlineAccessRole == null) { LOG.infof("Role 'offline_access' not available in realm '%s'. Skip migration of offline_access client scope.", realm.getName()); } else { @@ -102,32 +102,32 @@ public class MigrateTo4_0_0 implements Migration { if (offlineAccessScope == null) { LOG.infof("Client scope 'offline_access' not available in realm '%s'. Skip migration of offline_access client scope.", realm.getName()); } else { - for (ClientModel client : realm.getClients()) { - if ("openid-connect".equals(client.getProtocol()) - && !client.isBearerOnly() - && client.hasScope(offlineAccessRole) - && !client.getClientScopes(false, true).containsKey(OAuth2Constants.OFFLINE_ACCESS)) { - LOG.debugf("Adding client scope 'offline_access' as optional scope to client '%s' in realm '%s'.", client.getClientId(), realm.getName()); - client.addClientScope(offlineAccessScope, false); - - if (!client.isFullScopeAllowed()) { - LOG.debugf("Removing role scope mapping for role 'offline_access' from client '%s' in realm '%s'.", client.getClientId(), realm.getName()); - client.deleteScopeMapping(offlineAccessRole); - } - } - } + realm.getClientsStream() + .filter(MigrationUtils::isOIDCNonBearerOnlyClient) + .filter(c -> c.hasScope(offlineAccessRole)) + .filter(c -> !c.getClientScopes(false, true).containsKey(OAuth2Constants.OFFLINE_ACCESS)) + .peek(c -> { + LOG.debugf("Adding client scope 'offline_access' as optional scope to client '%s' in realm '%s'.", c.getClientId(), realm.getName()); + c.addClientScope(offlineAccessScope, false); + }) + .filter(c -> !c.isFullScopeAllowed()) + .forEach(c -> { + LOG.debugf("Removing role scope mapping for role 'offline_access' from client '%s' in realm '%s'.", c.getClientId(), realm.getName()); + c.deleteScopeMapping(offlineAccessRole); + }); } } // Clients with consentRequired, which don't have any client scopes will be added itself to require consent, so that consent screen is shown when users authenticate - for (ClientModel client : realm.getClients()) { - if (client.isConsentRequired() && client.getClientScopes(true, true).isEmpty()) { - LOG.debugf("Adding client '%s' of realm '%s' to display itself on consent screen", client.getClientId(), realm.getName()); - client.setDisplayOnConsentScreen(true); - String consentText = client.getName()==null ? client.getClientId() : client.getName(); - client.setConsentScreenText(consentText); - } - } + realm.getClientsStream() + .filter(ClientModel::isConsentRequired) + .filter(c -> c.getClientScopes(true, true).isEmpty()) + .forEach(c -> { + LOG.debugf("Adding client '%s' of realm '%s' to display itself on consent screen", c.getClientId(), realm.getName()); + c.setDisplayOnConsentScreen(true); + String consentText = c.getName() == null ? c.getClientId() : c.getName(); + c.setConsentScreenText(consentText); + }); } } diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_6_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_6_0.java index 717c2ed625..1891cf68c2 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_6_0.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_6_0.java @@ -20,7 +20,6 @@ package org.keycloak.migration.migrators; import org.jboss.logging.Logger; import org.keycloak.migration.MigrationProvider; import org.keycloak.migration.ModelVersion; -import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -63,12 +62,12 @@ public class MigrateTo4_6_0 implements Migration { LOG.debugf("Added '%s' and '%s' default client scopes", rolesScope.getName(), webOriginsScope.getName()); // Assign "roles" and "web-origins" clientScopes to all the OIDC clients - for (ClientModel client : realm.getClients()) { - if ((client.getProtocol()==null || "openid-connect".equals(client.getProtocol())) && (!client.isBearerOnly())) { - client.addClientScope(rolesScope, true); - client.addClientScope(webOriginsScope, true); - } - } + realm.getClientsStream() + .filter(MigrationUtils::isOIDCNonBearerOnlyClient) + .forEach(c -> { + c.addClientScope(rolesScope, true); + c.addClientScope(webOriginsScope, true); + }); LOG.debugf("Client scope '%s' assigned to all the clients", rolesScope.getName()); } diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo6_0_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo6_0_0.java index 6abfbed1fb..d04780ba95 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo6_0_0.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo6_0_0.java @@ -20,7 +20,6 @@ package org.keycloak.migration.migrators; import org.jboss.logging.Logger; import org.keycloak.migration.MigrationProvider; import org.keycloak.migration.ModelVersion; -import org.keycloak.models.ClientModel; import org.keycloak.models.ClientScopeModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -63,11 +62,9 @@ public class MigrateTo6_0_0 implements Migration { LOG.debugf("Added '%s' optional client scope", mpJWTScope.getName()); // assign 'microprofile-jwt' optional client scope to all the OIDC clients. - for (ClientModel client : realm.getClients()) { - if ((client.getProtocol() == null || "openid-connect".equals(client.getProtocol())) && (!client.isBearerOnly())) { - client.addClientScope(mpJWTScope, false); - } - } + realm.getClientsStream() + .filter(MigrationUtils::isOIDCNonBearerOnlyClient) + .forEach(c -> c.addClientScope(mpJWTScope, false)); LOG.debugf("Client scope '%s' assigned to all the clients", mpJWTScope.getName()); } diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java index 8326bc6402..80686254e8 100644 --- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java @@ -107,4 +107,11 @@ public class MigrationUtils { } } + public static void setDefaultClientAuthenticatorType(ClientModel s) { + s.setClientAuthenticatorType(KeycloakModelUtils.getDefaultClientAuthenticatorType()); + } + + public static boolean isOIDCNonBearerOnlyClient(ClientModel c) { + return (c.getProtocol() == null || "openid-connect".equals(c.getProtocol())) && !c.isBearerOnly(); + } } diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index 10f9adea31..1b4cc73d6c 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -586,13 +586,10 @@ public final class KeycloakModelUtils { } public static boolean isClientScopeUsed(RealmModel realm, ClientScopeModel clientScope) { - for (ClientModel client : realm.getClients()) { - if ((client.getClientScopes(true, false).containsKey(clientScope.getName())) || - (client.getClientScopes(false, false).containsKey(clientScope.getName()))) { - return true; - } - } - return false; + return realm.getClientsStream() + .filter(c -> (c.getClientScopes(true, false).containsKey(clientScope.getName())) || + (c.getClientScopes(false, false).containsKey(clientScope.getName()))) + .findFirst().isPresent(); } public static ClientScopeModel getClientScopeByName(RealmModel realm, String clientScopeName) { @@ -602,14 +599,7 @@ public final class KeycloakModelUtils { } } // check if we are referencing a client instead of a scope - if (realm.getClients() != null) { - for (ClientModel client : realm.getClients()) { - if (clientScopeName.equals(client.getClientId())) { - return client; - } - } - } - return null; + return realm.getClientsStream().filter(c -> clientScopeName.equals(c.getClientId())).findFirst().orElse(null); } /** diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java index ed9232ed1b..dd811f743c 100755 --- a/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java @@ -25,11 +25,10 @@ import org.keycloak.models.RealmModel; import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEventListener; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -62,7 +61,7 @@ public abstract class AbstractLoginProtocolFactory implements LoginProtocolFacto // Create default client scopes for realm built-in clients too if (addScopesToExistingClients) { - addDefaultClientScopes(newRealm, newRealm.getClients()); + addDefaultClientScopes(newRealm, newRealm.getClientsStream()); } } @@ -73,27 +72,27 @@ public abstract class AbstractLoginProtocolFactory implements LoginProtocolFacto protected void addDefaultClientScopes(RealmModel realm, ClientModel newClient) { - addDefaultClientScopes(realm, Arrays.asList(newClient)); + addDefaultClientScopes(realm, Stream.of(newClient)); } - protected void addDefaultClientScopes(RealmModel realm, List newClients) { + protected void addDefaultClientScopes(RealmModel realm, Stream newClients) { Set defaultClientScopes = realm.getDefaultClientScopes(true).stream() .filter(clientScope -> getId().equals(clientScope.getProtocol())) .collect(Collectors.toSet()); - if (!defaultClientScopes.isEmpty()) { - for (ClientModel newClient : newClients) { - newClient.addClientScopes(defaultClientScopes, true); - } - } Set nonDefaultClientScopes = realm.getDefaultClientScopes(false).stream() .filter(clientScope -> getId().equals(clientScope.getProtocol())) .collect(Collectors.toSet()); - if (!nonDefaultClientScopes.isEmpty()) { - for (ClientModel newClient : newClients) { - newClient.addClientScopes(nonDefaultClientScopes, false); - } - } + + Consumer addDefault = c -> c.addClientScopes(defaultClientScopes, true); + Consumer addNonDefault = c -> c.addClientScopes(nonDefaultClientScopes, false); + + if (!defaultClientScopes.isEmpty() && !nonDefaultClientScopes.isEmpty()) + newClients.forEach(addDefault.andThen(addNonDefault)); + else if (!defaultClientScopes.isEmpty()) + newClients.forEach(addDefault); + else if (!nonDefaultClientScopes.isEmpty()) + newClients.forEach(addNonDefault); } protected abstract void addDefaults(ClientModel realm); diff --git a/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java b/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java index 435a3ce0e5..68d582a296 100755 --- a/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java @@ -49,7 +49,6 @@ public interface RoleContainerModel { boolean removeRole(RoleModel role); - // TODO switch all usages to the stream variant @Deprecated default Set getRoles() { return getRolesStream().collect(Collectors.toSet()); @@ -57,7 +56,6 @@ public interface RoleContainerModel { Stream getRolesStream(); - // TODO switch all usages to the stream variant @Deprecated default Set getRoles(Integer firstResult, Integer maxResults) { return getRolesStream(firstResult, maxResults).collect(Collectors.toSet()); @@ -65,7 +63,6 @@ public interface RoleContainerModel { Stream getRolesStream(Integer firstResult, Integer maxResults); - // TODO switch all usages to the stream variant @Deprecated default Set searchForRoles(String search, Integer first, Integer max) { return searchForRolesStream(search, first, max).collect(Collectors.toSet()); diff --git a/server-spi/src/main/java/org/keycloak/models/utils/DefaultRoles.java b/server-spi/src/main/java/org/keycloak/models/utils/DefaultRoles.java index 450c4eb162..3a3bfaed80 100644 --- a/server-spi/src/main/java/org/keycloak/models/utils/DefaultRoles.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/DefaultRoles.java @@ -22,7 +22,11 @@ import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -35,13 +39,10 @@ public class DefaultRoles { set.add(realm.getRole(r)); } - for (ClientModel application : realm.getClients()) { - for (String r : application.getDefaultRoles()) { - set.add(application.getRole(r)); - } - } - return set; + Function> defaultRoles = i -> i.getDefaultRoles().stream().map(i::getRole).collect(Collectors.toSet()); + realm.getClientsStream().map(defaultRoles).forEach(set::addAll); + return set; } public static void addDefaultRoles(RealmModel realm, UserModel userModel) { for (RoleModel role : getDefaultRoles(realm)) { diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java b/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java index bbf7876525..c9d8d9c1d9 100644 --- a/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java @@ -189,6 +189,16 @@ public class RoleUtils { .collect(Collectors.toSet()); } + /** + * @param roles + * @return stream with composite roles expanded + */ + public static Stream expandCompositeRolesStream(Stream roles) { + Set visited = new HashSet<>(); + + return roles.flatMap(roleModel -> RoleUtils.expandCompositeRolesStream(roleModel, visited)); + } + /** * @param user diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java index 4045e01ea2..be591e879f 100755 --- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java +++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.AuthorizationProviderFactory; @@ -120,7 +121,7 @@ public class ExportUtils { List clients = Collections.emptyList(); if (options.isClientsIncluded()) { - clients = realm.getClients(); + clients = realm.getClientsStream().collect(Collectors.toList()); List clientReps = new ArrayList<>(); for (ClientModel app : clients) { ClientRepresentation clientRep = exportClient(session, app); @@ -133,22 +134,18 @@ public class ExportUtils { if (options.isGroupsAndRolesIncluded()) { ModelToRepresentation.exportGroups(realm, rep); - List realmRoleReps = null; Map> clientRolesReps = new HashMap<>(); - Set realmRoles = realm.getRoles(); - if (realmRoles != null && realmRoles.size() > 0) { - realmRoleReps = exportRoles(realmRoles); - } + List realmRoleReps = exportRoles(realm.getRolesStream()); RolesRepresentation rolesRep = new RolesRepresentation(); - if (realmRoleReps != null) { + if (!realmRoleReps.isEmpty()) { rolesRep.setRealm(realmRoleReps); } if (options.isClientsIncluded()) { for (ClientModel client : clients) { - Set currentAppRoles = client.getRoles(); + Stream currentAppRoles = client.getRolesStream(); List currentAppRoleReps = exportRoles(currentAppRoles); clientRolesReps.put(client.getClientId(), currentAppRoleReps); } @@ -411,14 +408,8 @@ public class ExportUtils { } } - public static List exportRoles(Collection roles) { - List roleReps = new ArrayList(); - - for (RoleModel role : roles) { - RoleRepresentation roleRep = exportRole(role); - roleReps.add(roleRep); - } - return roleReps; + public static List exportRoles(Stream roles) { + return roles.map(ExportUtils::exportRole).collect(Collectors.toList()); } public static List getRoleNames(Collection roles) { diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java index a215ef8be8..778395b9d8 100755 --- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java +++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java @@ -38,6 +38,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -102,14 +103,8 @@ public class ApplicationsBean { private Set getApplications(KeycloakSession session, RealmModel realm, UserModel user) { Set clients = new HashSet<>(); - for (ClientModel client : realm.getClients()) { - // Don't show bearerOnly clients - if (client.isBearerOnly()) { - continue; - } - - clients.add(client); - } + Predicate bearerOnly = ClientModel::isBearerOnly; + clients.addAll(realm.getClientsStream().filter(bearerOnly.negate()).collect(Collectors.toSet())); List consents = session.users().getConsents(realm, user.getId()); diff --git a/services/src/main/java/org/keycloak/partialimport/ClientRolesPartialImport.java b/services/src/main/java/org/keycloak/partialimport/ClientRolesPartialImport.java index b27c7d6e91..b90ae60277 100644 --- a/services/src/main/java/org/keycloak/partialimport/ClientRolesPartialImport.java +++ b/services/src/main/java/org/keycloak/partialimport/ClientRolesPartialImport.java @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -68,11 +69,7 @@ public class ClientRolesPartialImport { ClientModel client = realm.getClientByClientId(clientId); if (client == null) return false; - for (RoleModel role : client.getRoles()) { - if (getName(roleRep).equals(role.getName())) return true; - } - - return false; + return client.getRolesStream().anyMatch(role -> Objects.equals(getName(roleRep), role.getName())); } // check if client currently exists or will exists as a result of this partial import diff --git a/services/src/main/java/org/keycloak/partialimport/RealmRolesPartialImport.java b/services/src/main/java/org/keycloak/partialimport/RealmRolesPartialImport.java index c820d12ce0..d94f338e6b 100644 --- a/services/src/main/java/org/keycloak/partialimport/RealmRolesPartialImport.java +++ b/services/src/main/java/org/keycloak/partialimport/RealmRolesPartialImport.java @@ -24,6 +24,7 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.services.resources.admin.RoleResource; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -56,20 +57,15 @@ public class RealmRolesPartialImport extends AbstractPartialImport Objects.equals(getName(roleRep), r.getName())) + .map(RoleModel::getId) + .findFirst().orElse(null); } @Override public boolean exists(RealmModel realm, KeycloakSession session, RoleRepresentation roleRep) { - for (RoleModel role : realm.getRoles()) { - if (getName(roleRep).equals(role.getName())) return true; - } - - return false; + return realm.getRolesStream().anyMatch(role -> Objects.equals(getName(roleRep), role.getName())); } @Override diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java index fcbe3ae23a..d0fadabb34 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -21,7 +21,6 @@ import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; -import org.keycloak.Token; import org.keycloak.TokenCategory; import org.keycloak.TokenVerifier; import org.keycloak.broker.oidc.OIDCIdentityProvider; @@ -61,7 +60,6 @@ import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper; import org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper; -import org.keycloak.protocol.oidc.utils.OAuth2Code; import org.keycloak.protocol.oidc.utils.OIDCResponseType; import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessTokenResponse; @@ -83,16 +81,15 @@ import org.keycloak.util.TokenUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; @@ -495,21 +492,21 @@ public class TokenManager { } else { // 1 - Client roles of this client itself - Set scopeMappings = new HashSet<>(client.getRoles()); + Stream scopeMappings = client.getRolesStream(); // 2 - Role mappings of client itself + default client scopes + optional client scopes requested by scope parameter (if applyScopeParam is true) for (ClientScopeModel clientScope : clientScopes) { if (logger.isTraceEnabled()) { logger.tracef("Adding client scope role mappings of client scope '%s' to client '%s'", clientScope.getName(), client.getClientId()); } - scopeMappings.addAll(clientScope.getScopeMappings()); + scopeMappings = Stream.concat(scopeMappings, clientScope.getScopeMappingsStream()); } // 3 - Expand scope mappings - scopeMappings = RoleUtils.expandCompositeRoles(scopeMappings); + scopeMappings = RoleUtils.expandCompositeRolesStream(scopeMappings); // Intersection of expanded user roles and expanded scopeMappings - roleMappings.retainAll(scopeMappings); + roleMappings.retainAll(scopeMappings.collect(Collectors.toSet())); return roleMappings; } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java index 561a4e225a..49020ff557 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java @@ -40,8 +40,8 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.IOException; import java.net.URI; +import java.util.Iterator; import java.util.Map; -import java.util.Set; /** * @author Bill Burke @@ -58,7 +58,7 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide if (client.isPublicClient() && !client.isBearerOnly()) rep.setPublicClient(true); if (client.isBearerOnly()) rep.setBearerOnly(true); - if (client.getRoles().size() > 0) rep.setUseResourceRoleMappings(true); + if (client.getRolesStream().count() > 0) rep.setUseResourceRoleMappings(true); rep.setResource(client.getClientId()); @@ -104,7 +104,7 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide static boolean showVerifyTokenAudience(ClientModel client) { // We want to verify-token-audience if service client has any client roles - if (client.getRoles().size() > 0) { + if (client.getRolesStream().count() > 0) { return true; } @@ -187,13 +187,21 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide rep.setEnforcerConfig(enforcerConfig); - Set clientRoles = client.getRoles(); + Iterator it = client.getRolesStream().iterator(); - if (clientRoles.size() == 1) { - if (clientRoles.iterator().next().getName().equals(Constants.AUTHZ_UMA_PROTECTION)) { - rep.setUseResourceRoleMappings(null); - } + RoleModel role = hasOnlyOne(it); + if (role != null && role.getName().equals(Constants.AUTHZ_UMA_PROTECTION)) { + rep.setUseResourceRoleMappings(null); } } } + + private RoleModel hasOnlyOne(Iterator it) { + if (!it.hasNext()) return null; + else { + RoleModel role = it.next(); + if (it.hasNext()) return null; + else return role; + } + } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientCliInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientCliInstallation.java index 9ad5e66d3e..446cf814e6 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientCliInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientCliInstallation.java @@ -53,7 +53,7 @@ public class KeycloakOIDCJbossSubsystemClientCliInstallation implements ClientIn if (KeycloakOIDCClientInstallation.showVerifyTokenAudience(client)) { builder.append(" verify-token-audience=true, \\\n"); } - if (client.getRoles().size() > 0) { + if (client.getRolesStream().count() > 0) { builder.append(" use-resource-role-mappings=true, \\\n"); } builder.append(" ssl-required=").append(realm.getSslRequired().name()).append(")\n\n"); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java index dd762dcc45..bde96077ee 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCJbossSubsystemClientInstallation.java @@ -73,7 +73,7 @@ public class KeycloakOIDCJbossSubsystemClientInstallation implements ClientInsta } } } - if (client.getRoles().size() > 0) { + if (client.getRolesStream().count() > 0) { buffer.append(" true\n"); } buffer.append("\n"); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java index dd8cebb2a1..d6e739d6f6 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java @@ -31,6 +31,7 @@ import java.net.URI; import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; /** * @author Stian Thorgersen @@ -70,13 +71,11 @@ public class RedirectUtils { } private static Set getValidateRedirectUris(KeycloakSession session) { - Set redirects = new HashSet<>(); - for (ClientModel client : session.getContext().getRealm().getClients()) { - if (client.isEnabled()) { - redirects.addAll(resolveValidRedirects(session, client.getRootUrl(), client.getRedirectUris())); - } - } - return redirects; + return session.getContext().getRealm().getClientsStream() + .filter(ClientModel::isEnabled) + .map(c -> resolveValidRedirects(session, c.getRootUrl(), c.getRedirectUris())) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); } private static String verifyRedirectUri(KeycloakSession session, String rootUrl, String redirectUri, Set validRedirects, boolean requireRedirectUri) { diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java index 3842ed5a66..f535acee71 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java @@ -22,7 +22,6 @@ import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.common.VerificationException; import org.keycloak.common.util.PemUtils; -import org.keycloak.common.util.StreamUtil; import org.keycloak.crypto.KeyStatus; import org.keycloak.dom.saml.v2.SAML2Object; import org.keycloak.dom.saml.v2.assertion.BaseIDAbstractType; @@ -75,7 +74,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.security.PublicKey; @@ -83,21 +81,16 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; -import java.util.Properties; import java.util.Set; import java.util.TreeSet; -import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.crypto.Algorithm; import org.keycloak.crypto.KeyUse; import org.keycloak.crypto.KeyWrapper; -import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.rotation.HardcodedKeyLocator; import org.keycloak.rotation.KeyLocator; -import org.keycloak.saml.SPMetadataDescriptor; import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; import org.keycloak.saml.validators.DestinationValidator; import org.keycloak.sessions.AuthenticationSessionModel; -import java.nio.charset.StandardCharsets; import javax.ws.rs.core.MultivaluedMap; import javax.xml.crypto.dsig.XMLSignature; import org.w3c.dom.Document; @@ -698,16 +691,11 @@ public class SamlService extends AuthorizationEndpointBase { public Response idpInitiatedSSO(@PathParam("client") String clientUrlName, @QueryParam("RelayState") String relayState) { event.event(EventType.LOGIN); CacheControlUtil.noBackButtonCacheControlHeader(); - ClientModel client = null; - for (ClientModel c : realm.getClients()) { - String urlName = c.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME); - if (urlName == null) - continue; - if (urlName.equals(clientUrlName)) { - client = c; - break; - } - } + ClientModel client = realm.getClientsStream() + .filter(c -> Objects.nonNull(c.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME))) + .filter(c -> Objects.equals(c.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME), clientUrlName)) + .findFirst().orElse(null); + if (client == null) { event.error(Errors.CLIENT_NOT_FOUND); return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.CLIENT_NOT_FOUND); diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicy.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicy.java index 322bfaf5ec..3ef1e089d5 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicy.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicy.java @@ -42,7 +42,7 @@ public class MaxClientsClientRegistrationPolicy implements ClientRegistrationPol @Override public void beforeRegister(ClientRegistrationContext context) throws ClientRegistrationPolicyException { RealmModel realm = session.getContext().getRealm(); - int currentCount = realm.getClients().size(); + long currentCount = realm.getClientsCount(); int maxCount = componentModel.get(MaxClientsClientRegistrationPolicyFactory.MAX_CLIENTS, MaxClientsClientRegistrationPolicyFactory.DEFAULT_MAX_CLIENTS); if (currentCount >= maxCount) { diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java index 61a7dfe61b..2d5c7a1f4c 100644 --- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java @@ -292,7 +292,7 @@ public class ClientManager { if (clientModel.isPublicClient() && !clientModel.isBearerOnly()) rep.setPublicClient(true); if (clientModel.isBearerOnly()) rep.setBearerOnly(true); - if (clientModel.getRoles().size() > 0) rep.setUseResourceRoleMappings(true); + if (clientModel.getRolesStream().count() > 0) rep.setUseResourceRoleMappings(true); rep.setResource(clientModel.getClientId()); @@ -336,7 +336,7 @@ public class ClientManager { } } } - if (clientModel.getRoles().size() > 0) { + if (clientModel.getRolesStream().count() > 0) { buffer.append(" true\n"); } buffer.append("\n"); diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java index f1fb256050..c72cd1f8e1 100755 --- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java @@ -59,6 +59,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; /** * @author Bill Burke @@ -267,14 +269,17 @@ public class ResourceAdminManager { public GlobalRequestResult logoutAll(RealmModel realm) { realm.setNotBefore(Time.currentTime()); - List resources = realm.getClients(); - logger.debugv("logging out {0} resources ", resources.size()); + Stream resources = realm.getClientsStream(); GlobalRequestResult finalResult = new GlobalRequestResult(); - for (ClientModel resource : resources) { - GlobalRequestResult currentResult = logoutClient(realm, resource, realm.getNotBefore()); + AtomicInteger counter = new AtomicInteger(0); + resources.forEach(r -> { + counter.getAndIncrement(); + GlobalRequestResult currentResult = logoutClient(realm, r, realm.getNotBefore()); finalResult.addAll(currentResult); - } + }); + logger.debugv("logging out {0} resources ", counter); + return finalResult; } @@ -328,10 +333,10 @@ public class ResourceAdminManager { public GlobalRequestResult pushRealmRevocationPolicy(RealmModel realm) { GlobalRequestResult finalResult = new GlobalRequestResult(); - for (ClientModel client : realm.getClients()) { - GlobalRequestResult currentResult = pushRevocationPolicy(realm, client, realm.getNotBefore()); + realm.getClientsStream().forEach(c -> { + GlobalRequestResult currentResult = pushRevocationPolicy(realm, c, realm.getNotBefore()); finalResult.addAll(currentResult); - } + }); return finalResult; } diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java index b54c4bb8e8..00f2d6389c 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -22,7 +22,6 @@ import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.OAuthErrorException; import org.keycloak.authentication.AuthenticationProcessor; -import org.keycloak.authentication.AuthenticationFlowException; import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator; import org.keycloak.authentication.authenticators.broker.util.PostBrokerLoginConstants; import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext; @@ -1120,7 +1119,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal private ParsedCodeContext samlIdpInitiatedSSO(final String clientUrlName) { event.event(EventType.LOGIN); CacheControlUtil.noBackButtonCacheControlHeader(); - Optional oClient = this.realmModel.getClients().stream() + Optional oClient = this.realmModel.getClientsStream() .filter(c -> Objects.equals(c.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME), clientUrlName)) .findFirst(); diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java index 907d94f2a9..95645055ec 100755 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java @@ -515,10 +515,7 @@ public class AccountRestService { consentModels.put(client.getClientId(), consent); } - List alwaysDisplayClients = realm.getAlwaysDisplayInConsoleClients(); - for(ClientModel client : alwaysDisplayClients) { - clients.add(client); - } + realm.getAlwaysDisplayInConsoleClientsStream().forEach(clients::add); List apps = new LinkedList(); for (ClientModel client : clients) { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java index 7580140965..48ce971d4c 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java @@ -53,18 +53,17 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; import javax.ws.rs.ext.Providers; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.stream.Collectors; /** * @author Bill Burke @@ -219,7 +218,7 @@ public class AdminConsole { if (realm.equals(masterRealm)) { logger.debug("setting up realm access for a master realm user"); createRealm = user.hasRole(masterRealm.getRole(AdminRoles.CREATE_REALM)); - addMasterRealmAccess(realm, user, realmAccess); + addMasterRealmAccess(user, realmAccess); } else { logger.debug("setting up realm access for a realm user"); addRealmAccess(realm, user, realmAccess); @@ -233,28 +232,26 @@ public class AdminConsole { private void addRealmAccess(RealmModel realm, UserModel user, Map> realmAdminAccess) { RealmManager realmManager = new RealmManager(session); ClientModel realmAdminApp = realm.getClientByClientId(realmManager.getRealmAdminClientId(realm)); - Set roles = realmAdminApp.getRoles(); - for (RoleModel role : roles) { - if (!user.hasRole(role)) continue; - if (!realmAdminAccess.containsKey(realm.getName())) { - realmAdminAccess.put(realm.getName(), new HashSet()); - } - realmAdminAccess.get(realm.getName()).add(role.getName()); - } - + getRealmAdminAccess(realmAdminApp, user, realmAdminAccess); } - private void addMasterRealmAccess(RealmModel masterRealm, UserModel user, Map> realmAdminAccess) { + private void addMasterRealmAccess(UserModel user, Map> realmAdminAccess) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { ClientModel realmAdminApp = realm.getMasterAdminClient(); - Set roles = realmAdminApp.getRoles(); - for (RoleModel role : roles) { - if (!user.hasRole(role)) continue; - if (!realmAdminAccess.containsKey(realm.getName())) { - realmAdminAccess.put(realm.getName(), new HashSet()); - } - realmAdminAccess.get(realm.getName()).add(role.getName()); + getRealmAdminAccess(realmAdminApp, user, realmAdminAccess); + } + } + + private void getRealmAdminAccess(ClientModel client, UserModel user, Map> realmAdminAccess) { + Set roles = client.getRolesStream().filter(user::hasRole) + .map(RoleModel::getName).collect(Collectors.toSet()); + + if (!roles.isEmpty()) { + if (!realmAdminAccess.containsKey(realm.getName())) { + realmAdminAccess.put(realm.getName(), roles); + } else { + realmAdminAccess.get(realm.getName()).addAll(roles); } } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java index 9a459b5cd6..def76853ab 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java @@ -19,7 +19,6 @@ package org.keycloak.services.resources.admin; import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; -import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.events.admin.OperationType; @@ -46,14 +45,13 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; -import java.text.MessageFormat; import java.util.ArrayList; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Properties; import java.util.Set; -import java.util.stream.Collectors; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; /** * @resource Client Role Mappings @@ -120,17 +118,13 @@ public class ClientRoleMappingsResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getCompositeClientRoleMappings(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { + public Stream getCompositeClientRoleMappings(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { viewPermission.require(); - Set roles = client.getRoles(); - List mapRep = new ArrayList(); - for (RoleModel roleModel : roles) { - if (user.hasRole(roleModel)) { - mapRep.add(briefRepresentation ? ModelToRepresentation.toBriefRepresentation(roleModel) : ModelToRepresentation.toRepresentation(roleModel)); - } - } - return mapRep; + Stream roles = client.getRolesStream(); + Function toBriefRepresentation = briefRepresentation + ? ModelToRepresentation::toBriefRepresentation : ModelToRepresentation::toRepresentation; + return roles.filter(user::hasRole).map(toBriefRepresentation); } /** @@ -142,28 +136,13 @@ public class ClientRoleMappingsResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getAvailableClientRoleMappings() { + public Stream getAvailableClientRoleMappings() { viewPermission.require(); - Set available = client.getRoles(); - available = available.stream().filter(r -> - auth.roles().canMapRole(r) - ).collect(Collectors.toSet()); - return getAvailableRoles(user, available); - } - - public static List getAvailableRoles(RoleMapperModel mapper, Set available) { - Set roles = new HashSet(); - for (RoleModel roleModel : available) { - if (mapper.hasRole(roleModel)) continue; - roles.add(roleModel); - } - - List mappings = new ArrayList(); - for (RoleModel roleModel : roles) { - mappings.add(ModelToRepresentation.toBriefRepresentation(roleModel)); - } - return mappings; + return client.getRolesStream() + .filter(auth.roles()::canMapRole) + .filter(((Predicate) user::hasRole).negate()) + .map(ModelToRepresentation::toBriefRepresentation); } /** diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateScopeMappingsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateScopeMappingsResource.java index cbdf053cd5..2013c4ef11 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateScopeMappingsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateScopeMappingsResource.java @@ -17,10 +17,11 @@ package org.keycloak.services.resources.admin; -import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.function.BiPredicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -33,7 +34,6 @@ import org.keycloak.models.ClientScopeModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; -import org.keycloak.models.ScopeContainerModel; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.representations.idm.RoleRepresentation; @@ -92,44 +92,32 @@ public class ClientScopeEvaluateScopeMappingsResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getNotGrantedScopeMappings() { - List grantedRoles = getGrantedRoles(); + public Stream getNotGrantedScopeMappings() { + Set grantedRoles = getGrantedRoles(); - return roleContainer.getRoles().stream().filter((RoleModel role) -> { - - return !grantedRoles.contains(role); - - }).map((RoleModel role) -> { - - return ModelToRepresentation.toBriefRepresentation(role); - - }).collect(Collectors.toList()); + return roleContainer.getRolesStream() + .filter(r -> !grantedRoles.contains(r)) + .map(ModelToRepresentation::toBriefRepresentation); } - private List getGrantedRoles() { + private Set getGrantedRoles() { if (client.isFullScopeAllowed()) { - return new LinkedList<>(roleContainer.getRoles()); + // intentionally using deprecated method as a set is more appropriate here + return roleContainer.getRoles(); } Set clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client); - List result = new LinkedList<>(); + BiPredicate, RoleModel> hasClientScope = (scopes, role) -> + scopes.stream().anyMatch(scopeContainer -> scopeContainer.hasScope(role)); - for (RoleModel role : roleContainer.getRoles()) { - if (!auth.roles().canView(role)) continue; - - for (ScopeContainerModel scopeContainer : clientScopes) { - if (scopeContainer.hasScope(role)) { - result.add(role); - break; - } - } - } - - return result; + return roleContainer.getRolesStream() + .filter(auth.roles()::canView) + .filter(r -> hasClientScope.test(clientScopes, r)) + .collect(Collectors.toSet()); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index f120b412d8..15b52ae1ad 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -36,7 +36,6 @@ import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ForbiddenException; import org.keycloak.services.clientpolicy.AdminClientRegisterContext; import org.keycloak.services.clientpolicy.ClientPolicyException; -import org.keycloak.services.clientpolicy.DefaultClientPolicyManager; import org.keycloak.services.managers.ClientManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; @@ -57,10 +56,9 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.Objects; import java.util.Properties; +import java.util.stream.Stream; import static java.lang.Boolean.TRUE; @@ -101,65 +99,57 @@ public class ClientsResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getClients(@QueryParam("clientId") String clientId, + public Stream getClients(@QueryParam("clientId") String clientId, @QueryParam("viewableOnly") @DefaultValue("false") boolean viewableOnly, @QueryParam("search") @DefaultValue("false") boolean search, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) { - if (firstResult == null) { - firstResult = -1; - } - if (maxResults == null) { - maxResults = -1; - } - - List rep = new ArrayList<>(); boolean canView = auth.clients().canView(); - List clientModels; + Stream clientModels = Stream.empty(); if (clientId == null || clientId.trim().equals("")) { - clientModels = canView ? realm.getClients(firstResult, maxResults) : realm.getClients(); + clientModels = canView + ? realm.getClientsStream(firstResult, maxResults) + : realm.getClientsStream(); auth.clients().requireList(); + } else if (search) { + clientModels = canView + ? realm.searchClientByClientIdStream(clientId, firstResult, maxResults) + : realm.searchClientByClientIdStream(clientId, -1, -1); } else { - clientModels = Collections.emptyList(); - if(search) { - clientModels = canView ? realm.searchClientByClientId(clientId, firstResult, maxResults) : realm.searchClientByClientId(clientId, -1, -1); - } else { - ClientModel client = realm.getClientByClientId(clientId); - if(client != null) { - clientModels = Collections.singletonList(client); - } + ClientModel client = realm.getClientByClientId(clientId); + if (client != null) { + clientModels = Stream.of(client); } } - int idx = 0; + Stream s = clientModels + .map(c -> { + ClientRepresentation representation = null; + if (canView || auth.clients().canView(c)) { + representation = ModelToRepresentation.toRepresentation(c, session); + representation.setAccess(auth.clients().getAccess(c)); + } else if (!viewableOnly && auth.clients().canView(c)) { + representation = new ClientRepresentation(); + representation.setId(c.getId()); + representation.setClientId(c.getClientId()); + representation.setDescription(c.getDescription()); + } - for(ClientModel clientModel : clientModels) { - if (!canView) { - if (rep.size() == maxResults) { - return rep; - } + return representation; + }) + .filter(Objects::nonNull); + + if (!canView) { + if (firstResult != null && firstResult > 0) { + s = s.skip(firstResult); } - - ClientRepresentation representation = null; - - if (canView || auth.clients().canView(clientModel)) { - representation = ModelToRepresentation.toRepresentation(clientModel, session); - representation.setAccess(auth.clients().getAccess(clientModel)); - } else if (!viewableOnly && auth.clients().canView(clientModel)) { - representation = new ClientRepresentation(); - representation.setId(clientModel.getId()); - representation.setClientId(clientModel.getClientId()); - representation.setDescription(clientModel.getDescription()); - } - - if (representation != null) { - if (canView || idx++ >= firstResult) { - rep.add(representation); - } + if (maxResults != null && maxResults > 0) { + s = s.limit(maxResults); } } - return rep; + + return s; } private AuthorizationService getAuthorizationService(ClientModel clientModel) { diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java index 4ee07aa4c0..5f7717c54d 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java @@ -100,20 +100,20 @@ public class RoleContainerResource extends RoleResource { @QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { auth.roles().requireList(roleContainer); - Set roleModels; + Stream roleModels; if(search != null && search.trim().length() > 0) { - roleModels = roleContainer.searchForRoles(search, firstResult, maxResults); + roleModels = roleContainer.searchForRolesStream(search, firstResult, maxResults); } else if (!Objects.isNull(firstResult) && !Objects.isNull(maxResults)) { - roleModels = roleContainer.getRoles(firstResult, maxResults); + roleModels = roleContainer.getRolesStream(firstResult, maxResults); } else { - roleModels = roleContainer.getRoles(); + roleModels = roleContainer.getRolesStream(); } Function toRoleRepresentation = briefRepresentation ? ModelToRepresentation::toBriefRepresentation : ModelToRepresentation::toRepresentation; - return roleModels.stream().map(toRoleRepresentation); + return roleModels.map(toRoleRepresentation); } /** diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java index 80d24254ea..6abf8cb610 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java @@ -19,7 +19,6 @@ package org.keycloak.services.resources.admin; import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; -import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; import org.keycloak.common.ClientConnection; import org.keycloak.events.admin.OperationType; @@ -35,7 +34,6 @@ import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.ClientMappingsRepresentation; import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RoleRepresentation; -import org.keycloak.services.ErrorResponse; import org.keycloak.services.ErrorResponseException; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.storage.ReadOnlyException; @@ -53,15 +51,15 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; -import java.util.stream.Collectors; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; /** * Base resource for managing users @@ -183,17 +181,12 @@ public class RoleMapperResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getCompositeRealmRoleMappings(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { + public Stream getCompositeRealmRoleMappings(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { viewPermission.require(); - Set roles = realm.getRoles(); - List realmMappingsRep = new ArrayList(); - for (RoleModel roleModel : roles) { - if (roleMapper.hasRole(roleModel)) { - realmMappingsRep.add(briefRepresentation ? ModelToRepresentation.toBriefRepresentation(roleModel) : ModelToRepresentation.toRepresentation(roleModel)); - } - } - return realmMappingsRep; + Function toBriefRepresentation = briefRepresentation ? + ModelToRepresentation::toBriefRepresentation : ModelToRepresentation::toRepresentation; + return realm.getRolesStream().filter(roleMapper::hasRole).map(toBriefRepresentation); } /** @@ -205,14 +198,13 @@ public class RoleMapperResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getAvailableRealmRoleMappings() { + public Stream getAvailableRealmRoleMappings() { viewPermission.require(); - Set available = realm.getRoles(); - Set set = available.stream().filter(r -> - canMapRole(r) - ).collect(Collectors.toSet()); - return ClientRoleMappingsResource.getAvailableRoles(roleMapper, set); + return realm.getRolesStream() + .filter(this::canMapRole) + .filter(((Predicate) roleMapper::hasRole).negate()) + .map(ModelToRepresentation::toBriefRepresentation); } /** diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java index 12ef0a3b3d..b6deedea22 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java @@ -44,6 +44,9 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; /** * @resource Scope Mappings @@ -105,11 +108,13 @@ public class ScopeMappedClientResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getAvailableClientScopeMappings() { + public Stream getAvailableClientScopeMappings() { viewPermission.require(); - Set roles = scopedClient.getRoles(); - return ScopeMappedResource.getAvailable(auth, scopeContainer, roles); + return scopedClient.getRolesStream() + .filter(((Predicate) scopeContainer::hasScope).negate()) + .filter(auth.roles()::canMapClientScope) + .map(ModelToRepresentation::toBriefRepresentation); } /** @@ -125,11 +130,14 @@ public class ScopeMappedClientResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getCompositeClientScopeMappings(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { + public Stream getCompositeClientScopeMappings(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { viewPermission.require(); - Set roles = scopedClient.getRoles(); - return ScopeMappedResource.getComposite(scopeContainer, roles, briefRepresentation); + Function toBriefRepresentation = briefRepresentation ? + ModelToRepresentation::toBriefRepresentation : ModelToRepresentation::toRepresentation; + return scopedClient.getRolesStream() + .filter(scopeContainer::hasScope) + .map(toBriefRepresentation); } /** diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java index 9737285e92..879d26ed0b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java @@ -26,12 +26,12 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.ScopeContainerModel; -import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.ClientMappingsRepresentation; import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.util.ScopeMappedUtil; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -44,11 +44,15 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Base class for managing the scope mappings of a specific client. @@ -97,32 +101,22 @@ public class ScopeMappedResource { MappingsRepresentation all = new MappingsRepresentation(); Set realmMappings = scopeContainer.getRealmScopeMappings(); - if (realmMappings.size() > 0) { - List realmRep = new ArrayList(); + if (!realmMappings.isEmpty()) { + List realmRep = new LinkedList<>(); for (RoleModel roleModel : realmMappings) { realmRep.add(ModelToRepresentation.toBriefRepresentation(roleModel)); } all.setRealmMappings(realmRep); } - List clients = realm.getClients(); - if (clients.size() > 0) { - Map clientMappings = new HashMap(); - for (ClientModel client : clients) { - Set roleMappings = KeycloakModelUtils.getClientScopeMappings(client, this.scopeContainer); //client.getClientScopeMappings(this.client); - if (roleMappings.size() > 0) { - ClientMappingsRepresentation mappings = new ClientMappingsRepresentation(); - mappings.setId(client.getId()); - mappings.setClient(client.getClientId()); - List roles = new ArrayList(); - mappings.setMappings(roles); - for (RoleModel role : roleMappings) { - roles.add(ModelToRepresentation.toBriefRepresentation(role)); - } - clientMappings.put(client.getClientId(), mappings); - all.setClientMappings(clientMappings); - } - } + Stream clients = realm.getClientsStream(); + Map clientMappings = clients + .map(c -> ScopeMappedUtil.toClientMappingsRepresentation(c, scopeContainer)) + .filter(Objects::nonNull) + .collect(Collectors.toMap(ClientMappingsRepresentation::getClient, Function.identity())); + + if (!clientMappings.isEmpty()) { + all.setClientMappings(clientMappings); } return all; } @@ -160,25 +154,17 @@ public class ScopeMappedResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getAvailableRealmScopeMappings() { + public Stream getAvailableRealmScopeMappings() { viewPermission.require(); if (scopeContainer == null) { throw new NotFoundException("Could not find client"); } - Set roles = realm.getRoles(); - return getAvailable(auth, scopeContainer, roles); - } - - public static List getAvailable(AdminPermissionEvaluator auth, ScopeContainerModel client, Set roles) { - List available = new ArrayList(); - for (RoleModel roleModel : roles) { - if (client.hasScope(roleModel)) continue; - if (!auth.roles().canMapClientScope(roleModel)) continue; - available.add(ModelToRepresentation.toBriefRepresentation(roleModel)); - } - return available; + return realm.getRolesStream() + .filter(((Predicate) scopeContainer::hasScope).negate()) + .filter(auth.roles()::canMapClientScope) + .map(ModelToRepresentation::toBriefRepresentation); } /** @@ -196,23 +182,18 @@ public class ScopeMappedResource { @GET @Produces(MediaType.APPLICATION_JSON) @NoCache - public List getCompositeRealmScopeMappings(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { + public Stream getCompositeRealmScopeMappings(@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) { viewPermission.require(); if (scopeContainer == null) { throw new NotFoundException("Could not find client"); } - Set roles = realm.getRoles(); - return getComposite(scopeContainer, roles, briefRepresentation); - } - - public static List getComposite(ScopeContainerModel client, Set roles, boolean briefRepresentation) { - List composite = new ArrayList(); - for (RoleModel roleModel : roles) { - if (client.hasScope(roleModel)) composite.add(briefRepresentation ? ModelToRepresentation.toBriefRepresentation(roleModel) : ModelToRepresentation.toRepresentation(roleModel)); - } - return composite; + Function toBriefRepresentation = briefRepresentation ? + ModelToRepresentation::toBriefRepresentation : ModelToRepresentation::toRepresentation; + return realm.getRolesStream() + .filter(scopeContainer::hasScope) + .map(toBriefRepresentation); } /** diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java index eea964ce8c..77e32121d7 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java @@ -466,40 +466,40 @@ public class UserResource { @Produces(MediaType.APPLICATION_JSON) public List> getConsents() { auth.users().requireView(user); - List> result = new LinkedList<>(); Set offlineClients = new UserSessionManager(session).findClientsWithOfflineToken(realm, user); - for (ClientModel client : realm.getClients()) { - UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId()); - boolean hasOfflineToken = offlineClients.contains(client); + return realm.getClientsStream() + .map(client -> toConsent(client, offlineClients)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } - if (consent == null && !hasOfflineToken) { - continue; - } + private Map toConsent(ClientModel client, Set offlineClients) { + UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId()); + boolean hasOfflineToken = offlineClients.contains(client); - UserConsentRepresentation rep = (consent == null) ? null : ModelToRepresentation.toRepresentation(consent); - - Map currentRep = new HashMap<>(); - currentRep.put("clientId", client.getClientId()); - currentRep.put("grantedClientScopes", (rep==null ? Collections.emptyList() : rep.getGrantedClientScopes())); - currentRep.put("createdDate", (rep==null ? null : rep.getCreatedDate())); - currentRep.put("lastUpdatedDate", (rep==null ? null : rep.getLastUpdatedDate())); - - List> additionalGrants = new LinkedList<>(); - if (hasOfflineToken) { - Map offlineTokens = new HashMap<>(); - offlineTokens.put("client", client.getId()); - // TODO: translate - offlineTokens.put("key", "Offline Token"); - additionalGrants.add(offlineTokens); - } - currentRep.put("additionalGrants", additionalGrants); - - result.add(currentRep); + if (consent == null && !hasOfflineToken) { + return null; } - return result; + UserConsentRepresentation rep = (consent == null) ? null : ModelToRepresentation.toRepresentation(consent); + + Map currentRep = new HashMap<>(); + currentRep.put("clientId", client.getClientId()); + currentRep.put("grantedClientScopes", (rep == null ? Collections.emptyList() : rep.getGrantedClientScopes())); + currentRep.put("createdDate", (rep == null ? null : rep.getCreatedDate())); + currentRep.put("lastUpdatedDate", (rep == null ? null : rep.getLastUpdatedDate())); + + List> additionalGrants = new LinkedList<>(); + if (hasOfflineToken) { + Map offlineTokens = new HashMap<>(); + offlineTokens.put("client", client.getId()); + offlineTokens.put("key", "Offline Token"); + additionalGrants.add(offlineTokens); + } + currentRep.put("additionalGrants", additionalGrants); + return currentRep; } diff --git a/services/src/main/java/org/keycloak/services/util/ScopeMappedUtil.java b/services/src/main/java/org/keycloak/services/util/ScopeMappedUtil.java new file mode 100644 index 0000000000..fbcc3ebd02 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/util/ScopeMappedUtil.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.services.util; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.ScopeContainerModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.representations.idm.ClientMappingsRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; + +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class ScopeMappedUtil { + public static ClientMappingsRepresentation toClientMappingsRepresentation(ClientModel client, ScopeContainerModel scopeContainer) { + Set roleMappings = KeycloakModelUtils.getClientScopeMappings(client, scopeContainer); + + if (!roleMappings.isEmpty()) { + + ClientMappingsRepresentation mappings = new ClientMappingsRepresentation(); + mappings.setId(client.getId()); + mappings.setClient(client.getClientId()); + List roles = new LinkedList<>(); + mappings.setMappings(roles); + for (RoleModel role : roleMappings) { + roles.add(ModelToRepresentation.toBriefRepresentation(role)); + } + + return mappings; + } else { + return null; + } + } +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/condition/TestClientRolesCondition.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/condition/TestClientRolesCondition.java index 472ae82959..37ec1652d0 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/condition/TestClientRolesCondition.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/services/clientpolicy/condition/TestClientRolesCondition.java @@ -64,10 +64,11 @@ public class TestClientRolesCondition implements ClientPolicyConditionProvider { List rolesForMatching = getRolesForMatching(); if (rolesForMatching == null) return false; - client.getRoles().stream().forEach(i -> ClientPolicyLogger.log(logger, "client role = " + i.getName())); + client.getRolesStream().forEach(i -> ClientPolicyLogger.log(logger, "client role = " + i.getName())); rolesForMatching.stream().forEach(i -> ClientPolicyLogger.log(logger, "roles expected = " + i)); - boolean isMatched = rolesForMatching.stream().anyMatch(i->client.getRoles().stream().anyMatch(j->j.getName().equals(i))); + boolean isMatched = rolesForMatching.stream() + .anyMatch(i -> client.getRolesStream().anyMatch(j -> j.getName().equals(i))); if (isMatched) { ClientPolicyLogger.log(logger, "role matched."); } else { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java index 3502ab6bc6..9428fbdec5 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java @@ -34,6 +34,7 @@ import org.keycloak.testsuite.arquillian.annotation.ModelTest; import java.util.HashSet; import java.util.Set; +import java.util.stream.Stream; import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; @@ -52,17 +53,14 @@ public class CompositeRolesModelTest extends AbstractTestRealmKeycloakTest { Set requestedRoles = new HashSet<>(); Set roleMappings = user.getRoleMappings(); - Set scopeMappings = application.getScopeMappings(); - Set appRoles = application.getRoles(); - if (appRoles != null) scopeMappings.addAll(appRoles); + Stream scopeMappings = Stream.concat(application.getScopeMappingsStream(), application.getRolesStream()); - for (RoleModel role : roleMappings) { + scopeMappings.forEach(scope -> roleMappings.forEach(role -> { if (role.getContainer().equals(application)) requestedRoles.add(role); - for (RoleModel desiredRole : scopeMappings) { - Set visited = new HashSet<>(); - applyScope(role, desiredRole, visited, requestedRoles); - } - } + + Set visited = new HashSet<>(); + applyScope(role, scope, visited, requestedRoles); + })); return requestedRoles; } diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/TestCacheUtils.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/TestCacheUtils.java index c2fd00dacc..d48fe0f4f6 100644 --- a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/TestCacheUtils.java +++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/TestCacheUtils.java @@ -24,7 +24,6 @@ import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; -import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; /** @@ -35,12 +34,12 @@ public class TestCacheUtils { public static void cacheRealmWithEverything(KeycloakSession session, String realmName) { RealmModel realm = session.realms().getRealmByName(realmName); - for (ClientModel client : realm.getClients()) { - realm.getClientById(client.getId()); - realm.getClientByClientId(client.getClientId()); + realm.getClientsStream().forEach(c -> { + realm.getClientById(c.getId()); + realm.getClientByClientId(c.getClientId()); - cacheRoles(session, realm, client); - } + cacheRoles(session, realm, c); + }); cacheRoles(session, realm, realm); @@ -66,7 +65,7 @@ public class TestCacheUtils { } private static void cacheRoles(KeycloakSession session, RealmModel realm, RoleContainerModel roleContainer) { - for (RoleModel role : roleContainer.getRoles()) { + roleContainer.getRolesStream().forEach(role -> { realm.getRoleById(role.getId()); roleContainer.getRole(role.getName()); if (roleContainer instanceof RealmModel) { @@ -74,7 +73,7 @@ public class TestCacheUtils { } else { session.roles().getClientRole((ClientModel) roleContainer, role.getName()); } - } + }); } private static void cacheGroupRecursive(RealmModel realm, GroupModel group) {