diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java index f18f4eb21b9..cc9e54b7ea5 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java @@ -17,6 +17,8 @@ package org.keycloak.representations.idm; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; + import java.util.List; import java.util.Map; @@ -62,6 +64,7 @@ public class ClientRepresentation { private Boolean useTemplateConfig; private Boolean useTemplateScope; private Boolean useTemplateMappers; + private ResourceServerRepresentation authorizationSettings; public String getId() { @@ -241,6 +244,9 @@ public class ClientRepresentation { } public Boolean getAuthorizationServicesEnabled() { + if (authorizationSettings != null) { + return true; + } return authorizationServicesEnabled; } @@ -353,4 +359,11 @@ public class ClientRepresentation { this.useTemplateMappers = useTemplateMappers; } + public ResourceServerRepresentation getAuthorizationSettings() { + return authorizationSettings; + } + + public void setAuthorizationSettings(ResourceServerRepresentation authorizationSettings) { + this.authorizationSettings = authorizationSettings; + } } diff --git a/distribution/demo-dist/src/main/xslt/standalone.xsl b/distribution/demo-dist/src/main/xslt/standalone.xsl index 17d949691d8..4ef3f0e3ea8 100755 --- a/distribution/demo-dist/src/main/xslt/standalone.xsl +++ b/distribution/demo-dist/src/main/xslt/standalone.xsl @@ -89,7 +89,7 @@ - + diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java index ff646bbfe54..fb7c91b796b 100644 --- a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java +++ b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java @@ -24,6 +24,7 @@ import org.keycloak.authorization.policy.provider.PolicyProvider; import org.keycloak.authorization.policy.provider.PolicyProviderFactory; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderFactory; @@ -64,17 +65,19 @@ public final class AuthorizationProvider implements Provider { private final StoreFactory storeFactory; private final List policyProviderFactories; private final KeycloakSession keycloakSession; + private final RealmModel realm; - public AuthorizationProvider(KeycloakSession session, StoreFactory storeFactory, Executor scheduller) { + public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Executor scheduller) { this.keycloakSession = session; + this.realm = realm; this.storeFactory = storeFactory; this.scheduller = scheduller; this.policyProviderFactories = configurePolicyProviderFactories(session); this.policyEvaluator = new DefaultPolicyEvaluator(this, this.policyProviderFactories); } - public AuthorizationProvider(KeycloakSession session, StoreFactory storeFactory) { - this(session, storeFactory, Runnable::run); + public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory) { + this(session, realm, storeFactory, Runnable::run); } /** @@ -120,6 +123,10 @@ public final class AuthorizationProvider implements Provider { return this.keycloakSession; } + public RealmModel getRealm() { + return realm; + } + private List configurePolicyProviderFactories(KeycloakSession session) { List providerFactories = session.getKeycloakSessionFactory().getProviderFactories(PolicyProvider.class); diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java index ae4dcc2fe91..7ca684c2514 100644 --- a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java +++ b/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java @@ -18,10 +18,14 @@ package org.keycloak.authorization; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; import org.keycloak.provider.ProviderFactory; /** * @author Pedro Igor */ public interface AuthorizationProviderFactory extends ProviderFactory { + + AuthorizationProvider create(KeycloakSession session, RealmModel realm); } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 73c907bb85e..e2d4799206d 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -17,6 +17,14 @@ package org.keycloak.models.utils; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.authorization.store.PolicyStore; +import org.keycloak.authorization.store.ResourceStore; +import org.keycloak.authorization.store.StoreFactory; import org.keycloak.component.ComponentModel; import org.keycloak.events.Event; import org.keycloak.events.admin.AdminEvent; @@ -31,6 +39,7 @@ import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.GroupModel; import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelException; import org.keycloak.models.OTPPolicy; import org.keycloak.models.ProtocolMapperModel; @@ -72,8 +81,25 @@ import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.common.util.Time; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.representations.idm.authorization.ScopeRepresentation; +import org.keycloak.util.JsonSerialization; -import java.util.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * @author Bill Burke @@ -767,4 +793,181 @@ public class ModelToRepresentation { rep.setConfig(component.getConfig()); return rep; } + + public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider) { + ScopeRepresentation scope = new ScopeRepresentation(); + + scope.setId(model.getId()); + scope.setName(model.getName()); + scope.setIconUri(model.getIconUri()); + + StoreFactory storeFactory = authorizationProvider.getStoreFactory(); + + scope.setResources(new ArrayList<>()); + + storeFactory.getResourceStore().findByScope(model.getId()).forEach(resource -> scope.getResources().add(toRepresentation(resource, resource.getResourceServer(), authorizationProvider))); + + PolicyStore policyStore = storeFactory.getPolicyStore(); + + scope.setPolicies(new ArrayList<>()); + + policyStore.findByScopeIds(Arrays.asList(model.getId()), model.getResourceServer().getId()).forEach(policyModel -> { + PolicyRepresentation policy = new PolicyRepresentation(); + + policy.setId(policyModel.getId()); + policy.setName(policyModel.getName()); + policy.setType(policyModel.getType()); + + if (!scope.getPolicies().contains(policy)) { + scope.getPolicies().add(policy); + } + }); + + return scope; + } + + public static ResourceServerRepresentation toRepresentation(ResourceServer model, ClientModel client) { + ResourceServerRepresentation server = new ResourceServerRepresentation(); + + server.setId(model.getId()); + server.setClientId(model.getClientId()); + server.setName(client.getClientId()); + server.setAllowRemoteResourceManagement(model.isAllowRemoteResourceManagement()); + server.setPolicyEnforcementMode(model.getPolicyEnforcementMode()); + + return server; + } + + public static PolicyRepresentation toRepresentation(Policy model, AuthorizationProvider authorization) { + PolicyRepresentation representation = new PolicyRepresentation(); + + representation.setId(model.getId()); + representation.setName(model.getName()); + representation.setDescription(model.getDescription()); + representation.setType(model.getType()); + representation.setDecisionStrategy(model.getDecisionStrategy()); + representation.setLogic(model.getLogic()); + representation.setConfig(new HashMap<>(model.getConfig())); + + List policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId()); + + representation.setDependentPolicies(policies.stream().map(policy -> { + PolicyRepresentation representation1 = new PolicyRepresentation(); + + representation1.setId(policy.getId()); + representation1.setName(policy.getName()); + + return representation1; + }).collect(Collectors.toList())); + + List associatedPolicies = new ArrayList<>(); + + List obj = model.getAssociatedPolicies().stream().map(policy -> { + PolicyRepresentation representation1 = new PolicyRepresentation(); + + representation1.setId(policy.getId()); + representation1.setName(policy.getName()); + representation1.setType(policy.getType()); + + associatedPolicies.add(representation1); + + return policy.getId(); + }).collect(Collectors.toList()); + + representation.setAssociatedPolicies(associatedPolicies); + + try { + representation.getConfig().put("applyPolicies", JsonSerialization.writeValueAsString(obj)); + } catch (IOException e) { + e.printStackTrace(); + } + + return representation; + } + + public static ResourceRepresentation toRepresentation(Resource model, ResourceServer resourceServer, AuthorizationProvider authorization) { + ResourceRepresentation resource = new ResourceRepresentation(); + + resource.setId(model.getId()); + resource.setType(model.getType()); + resource.setName(model.getName()); + resource.setUri(model.getUri()); + resource.setIconUri(model.getIconUri()); + + ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation(); + + owner.setId(model.getOwner()); + + KeycloakSession keycloakSession = authorization.getKeycloakSession(); + RealmModel realm = authorization.getRealm(); + + if (owner.getId().equals(resourceServer.getClientId())) { + ClientModel clientModel = realm.getClientById(resourceServer.getClientId()); + owner.setName(clientModel.getClientId()); + } else { + UserModel userModel = keycloakSession.users().getUserById(owner.getId(), realm); + + if (userModel == null) { + throw new RuntimeException("Could not find the user [" + owner.getId() + "] who owns the Resource [" + resource.getId() + "]."); + } + + owner.setName(userModel.getUsername()); + } + + resource.setOwner(owner); + + resource.setScopes(model.getScopes().stream().map(model1 -> { + ScopeRepresentation scope = new ScopeRepresentation(); + scope.setId(model1.getId()); + scope.setName(model1.getName()); + String iconUri = model1.getIconUri(); + if (iconUri != null) { + scope.setIconUri(iconUri); + } + return scope; + }).collect(Collectors.toSet())); + + resource.setTypedScopes(new ArrayList<>()); + + if (resource.getType() != null) { + ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore(); + for (Resource typed : resourceStore.findByType(resource.getType())) { + if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(resource.getId())) { + resource.setTypedScopes(typed.getScopes().stream().map(model1 -> { + ScopeRepresentation scope = new ScopeRepresentation(); + scope.setId(model1.getId()); + scope.setName(model1.getName()); + String iconUri = model1.getIconUri(); + if (iconUri != null) { + scope.setIconUri(iconUri); + } + return scope; + }).filter(scopeRepresentation -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList())); + } + } + } + + resource.setPolicies(new ArrayList<>()); + + Set policies = new HashSet<>(); + PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore(); + + policies.addAll(policyStore.findByResource(resource.getId())); + policies.addAll(policyStore.findByResourceType(resource.getType(), resourceServer.getId())); + policies.addAll(policyStore.findByScopeIds(resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId())); + + for (Policy policyModel : policies) { + PolicyRepresentation policy = new PolicyRepresentation(); + + policy.setId(policyModel.getId()); + policy.setName(policyModel.getName()); + policy.setType(policyModel.getType()); + + if (!resource.getPolicies().contains(policy)) { + resource.getPolicies().add(policy); + } + } + + return resource; + } } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 7e813d64595..f36f0104106 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -18,8 +18,16 @@ package org.keycloak.models.utils; import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.AuthorizationProviderFactory; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.ResourceServerStore; +import org.keycloak.authorization.store.ResourceStore; +import org.keycloak.authorization.store.ScopeStore; +import org.keycloak.authorization.store.StoreFactory; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.component.ComponentModel; import org.keycloak.hash.Pbkdf2PasswordHashProvider; @@ -84,6 +92,12 @@ import org.keycloak.representations.idm.UserFederationMapperRepresentation; import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.representations.idm.authorization.ScopeRepresentation; +import org.keycloak.util.JsonSerialization; import java.io.IOException; import java.util.ArrayList; @@ -95,8 +109,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; - -import static java.lang.Boolean.TRUE; +import java.util.function.Function; +import java.util.stream.Collectors; public class RepresentationToModel { @@ -277,6 +291,13 @@ public class RepresentationToModel { } } + if (rep.getClients() != null) { + rep.getClients().forEach(clientRepresentation -> { + ClientModel client = newRealm.getClientByClientId(clientRepresentation.getClientId()); + importAuthorizationSettings(clientRepresentation, client, session); + }); + } + if (rep.getSmtpServer() != null) { newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer())); } @@ -1022,22 +1043,6 @@ public class RepresentationToModel { if (resourceRep.isUseTemplateMappers() != null) client.setUseTemplateMappers(resourceRep.isUseTemplateMappers()); else client.setUseTemplateMappers(resourceRep.getClientTemplate() != null); - boolean createResourceServer = TRUE.equals(resourceRep.getAuthorizationServicesEnabled()); - - if (createResourceServer) { - AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class); - ResourceServerStore resourceServerStore = provider.getStoreFactory().getResourceServerStore(); - - client.setServiceAccountsEnabled(true); - client.setBearerOnly(false); - client.setPublicClient(false); - - ResourceServer resourceServer = resourceServerStore.create(client.getId()); - - resourceServer.setAllowRemoteResourceManagement(true); - resourceServer.setPolicyEnforcementMode(PolicyEnforcementMode.ENFORCING); - } - return client; } @@ -1656,4 +1661,432 @@ public class RepresentationToModel { model.setName(rep.getName()); return model; } + + public static void importAuthorizationSettings(ClientRepresentation clientRepresentation, ClientModel client, KeycloakSession session) { + if (Boolean.TRUE.equals(clientRepresentation.getAuthorizationServicesEnabled())) { + AuthorizationProviderFactory authorizationFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class); + AuthorizationProvider authorization = authorizationFactory.create(session, client.getRealm()); + + client.setServiceAccountsEnabled(true); + client.setBearerOnly(false); + client.setPublicClient(false); + + ResourceServerRepresentation rep = clientRepresentation.getAuthorizationSettings(); + + if (rep == null) { + rep = new ResourceServerRepresentation(); + } + + rep.setClientId(client.getId()); + + toModel(rep, authorization); + } + } + + public static void toModel(ResourceServerRepresentation rep, AuthorizationProvider authorization) { + ResourceServerStore resourceServerStore = authorization.getStoreFactory().getResourceServerStore(); + ResourceServer resourceServer; + ResourceServer existing = resourceServerStore.findByClient(rep.getClientId()); + + if (existing == null) { + resourceServer = resourceServerStore.create(rep.getClientId()); + resourceServer.setAllowRemoteResourceManagement(true); + resourceServer.setPolicyEnforcementMode(PolicyEnforcementMode.ENFORCING); + } else { + resourceServer = existing; + } + + resourceServer.setPolicyEnforcementMode(rep.getPolicyEnforcementMode()); + resourceServer.setAllowRemoteResourceManagement(rep.isAllowRemoteResourceManagement()); + + StoreFactory storeFactory = authorization.getStoreFactory(); + ScopeStore scopeStore = storeFactory.getScopeStore(); + + rep.getScopes().forEach(scope -> { + toModel(scope, resourceServer, authorization); + }); + + KeycloakSession session = authorization.getKeycloakSession(); + RealmModel realm = authorization.getRealm(); + + rep.getResources().forEach(resourceRepresentation -> { + ResourceOwnerRepresentation owner = resourceRepresentation.getOwner(); + + if (owner == null) { + owner = new ResourceOwnerRepresentation(); + resourceRepresentation.setOwner(owner); + } + + owner.setId(resourceServer.getClientId()); + + if (owner.getName() != null) { + UserModel user = session.users().getUserByUsername(owner.getName(), realm); + + if (user != null) { + owner.setId(user.getId()); + } + } + + toModel(resourceRepresentation, resourceServer, authorization); + }); + + rep.getPolicies().forEach(policyRepresentation -> { + Map config = policyRepresentation.getConfig(); + + String roles = config.get("roles"); + + if (roles != null && !roles.isEmpty()) { + try { + List rolesMap = JsonSerialization.readValue(roles, List.class); + config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> { + String roleName = roleConfig.get("id").toString(); + String clientId = null; + int clientIdSeparator = roleName.indexOf("/"); + + if (clientIdSeparator != -1) { + clientId = roleName.substring(0, clientIdSeparator); + roleName = roleName.substring(clientIdSeparator + 1); + } + + RoleModel role; + + if (clientId == null) { + role = realm.getRole(roleName); + } else { + role = realm.getClientByClientId(clientId).getRole(roleName); + } + + // fallback to find any client role with the given name + if (role == null) { + String finalRoleName = roleName; + role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null) + .findFirst().orElse(null); + } + + if (role == null) { + throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found."); + } + + roleConfig.put("id", role.getId()); + return roleConfig; + }).collect(Collectors.toList()))); + } catch (Exception e) { + throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); + } + } + + String users = config.get("users"); + + if (users != null && !users.isEmpty()) { + try { + List usersMap = JsonSerialization.readValue(users, List.class); + config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userName -> session.users().getUserByUsername(userName, realm).getId()).collect(Collectors.toList()))); + } catch (Exception e) { + throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); + } + } + + String scopes = config.get("scopes"); + + if (scopes != null && !scopes.isEmpty()) { + try { + List scopesMap = JsonSerialization.readValue(scopes, List.class); + config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> { + Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId()); + + if (newScope == null) { + throw new RuntimeException("Scope with name [" + scopeName + "] not defined."); + } + + return newScope.getId(); + }).collect(Collectors.toList()))); + } catch (Exception e) { + throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); + } + } + + String policyResources = config.get("resources"); + + if (policyResources != null && !policyResources.isEmpty()) { + ResourceStore resourceStore = storeFactory.getResourceStore(); + try { + List resources = JsonSerialization.readValue(policyResources, List.class); + config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> { + return resourceStore.findByName(resourceName, resourceServer.getId()).getId(); + }).collect(Collectors.toList()))); + } catch (Exception e) { + throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); + } + } + + String applyPolicies = config.get("applyPolicies"); + + if (applyPolicies != null && !applyPolicies.isEmpty()) { + PolicyStore policyStore = storeFactory.getPolicyStore(); + try { + List policies = JsonSerialization.readValue(applyPolicies, List.class); + config.put("applyPolicies", JsonSerialization.writeValueAsString(policies.stream().map(policyName -> { + Policy policy = policyStore.findByName(policyName, resourceServer.getId()); + + if (policy == null) { + throw new RuntimeException("Policy with name [" + policyName + "] not defined."); + } + + return policy.getId(); + }).collect(Collectors.toList()))); + } catch (Exception e) { + throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); + } + } + + toModel(policyRepresentation, resourceServer, authorization); + }); + } + + public static Policy toModel(PolicyRepresentation policy, ResourceServer resourceServer, AuthorizationProvider authorization) { + PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore(); + Policy existing; + + if (policy.getId() != null) { + existing = policyStore.findById(policy.getId()); + } else { + existing = policyStore.findByName(policy.getName(), resourceServer.getId()); + } + + if (existing != null) { + existing.setName(policy.getName()); + existing.setDescription(policy.getDescription()); + existing.setConfig(policy.getConfig()); + existing.setDecisionStrategy(policy.getDecisionStrategy()); + existing.setLogic(policy.getLogic()); + + updateResources(existing, authorization); + updateAssociatedPolicies(existing, resourceServer, authorization); + updateScopes(existing, authorization); + + return existing; + } + + Policy model = policyStore.create(policy.getName(), policy.getType(), resourceServer); + + model.setDescription(policy.getDescription()); + model.setDecisionStrategy(policy.getDecisionStrategy()); + model.setLogic(policy.getLogic()); + model.setConfig(policy.getConfig()); + + updateResources(model, authorization); + updateAssociatedPolicies(model, resourceServer, authorization); + updateScopes(model, authorization); + + policy.setId(model.getId()); + + return model; + } + + private static void updateScopes(Policy policy, AuthorizationProvider authorization) { + String scopes = policy.getConfig().get("scopes"); + if (scopes != null) { + String[] scopeIds; + + try { + scopeIds = JsonSerialization.readValue(scopes, String[].class); + } catch (IOException e) { + throw new RuntimeException(e); + } + + StoreFactory storeFactory = authorization.getStoreFactory(); + + for (String scopeId : scopeIds) { + boolean hasScope = false; + + for (Scope scopeModel : new HashSet(policy.getScopes())) { + if (scopeModel.getId().equals(scopeId)) { + hasScope = true; + } + } + if (!hasScope) { + policy.addScope(storeFactory.getScopeStore().findById(scopeId)); + } + } + + for (Scope scopeModel : new HashSet(policy.getScopes())) { + boolean hasScope = false; + + for (String scopeId : scopeIds) { + if (scopeModel.getId().equals(scopeId)) { + hasScope = true; + } + } + if (!hasScope) { + policy.removeScope(scopeModel); + } + } + } + } + + private static void updateAssociatedPolicies(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization) { + String policies = policy.getConfig().get("applyPolicies"); + + if (policies != null) { + String[] policyIds; + + try { + policyIds = JsonSerialization.readValue(policies, String[].class); + } catch (IOException e) { + throw new RuntimeException(e); + } + + StoreFactory storeFactory = authorization.getStoreFactory(); + PolicyStore policyStore = storeFactory.getPolicyStore(); + + for (String policyId : policyIds) { + boolean hasPolicy = false; + + for (Policy policyModel : new HashSet(policy.getAssociatedPolicies())) { + if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) { + hasPolicy = true; + } + } + + + if (!hasPolicy) { + Policy associatedPolicy = policyStore.findById(policyId); + + if (associatedPolicy == null) { + associatedPolicy = policyStore.findByName(policyId, resourceServer.getId()); + } + + policy.addAssociatedPolicy(associatedPolicy); + } + } + + for (Policy policyModel : new HashSet(policy.getAssociatedPolicies())) { + boolean hasPolicy = false; + + for (String policyId : policyIds) { + if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) { + hasPolicy = true; + } + } + if (!hasPolicy) { + policy.removeAssociatedPolicy(policyModel);; + } + } + } + } + + private static void updateResources(Policy policy, AuthorizationProvider authorization) { + String resources = policy.getConfig().get("resources"); + if (resources != null) { + String[] resourceIds; + + try { + resourceIds = JsonSerialization.readValue(resources, String[].class); + } catch (IOException e) { + throw new RuntimeException(e); + } + + StoreFactory storeFactory = authorization.getStoreFactory(); + + for (String resourceId : resourceIds) { + boolean hasResource = false; + for (Resource resourceModel : new HashSet(policy.getResources())) { + if (resourceModel.getId().equals(resourceId)) { + hasResource = true; + } + } + if (!hasResource && !"".equals(resourceId)) { + policy.addResource(storeFactory.getResourceStore().findById(resourceId)); + } + } + + for (Resource resourceModel : new HashSet(policy.getResources())) { + boolean hasResource = false; + + for (String resourceId : resourceIds) { + if (resourceModel.getId().equals(resourceId)) { + hasResource = true; + } + } + + if (!hasResource) { + policy.removeResource(resourceModel); + } + } + } + } + + public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) { + ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore(); + Resource existing; + + if (resource.getId() != null) { + existing = resourceStore.findById(resource.getId()); + } else { + existing = resourceStore.findByName(resource.getName(), resourceServer.getId()); + } + + if (existing != null) { + existing.setName(resource.getName()); + existing.setType(resource.getType()); + existing.setUri(resource.getUri()); + existing.setIconUri(resource.getIconUri()); + + existing.updateScopes(resource.getScopes().stream() + .map((ScopeRepresentation scope) -> toModel(scope, resourceServer, authorization)) + .collect(Collectors.toSet())); + return existing; + } + + ResourceOwnerRepresentation owner = resource.getOwner(); + + if (owner == null) { + owner = new ResourceOwnerRepresentation(); + owner.setId(resourceServer.getClientId()); + } + + if (owner.getId() == null) { + throw new RuntimeException("No owner specified for resource [" + resource.getName() + "]."); + } + + Resource model = resourceStore.create(resource.getName(), resourceServer, owner.getId()); + + model.setType(resource.getType()); + model.setUri(resource.getUri()); + model.setIconUri(resource.getIconUri()); + + Set scopes = resource.getScopes(); + + if (scopes != null) { + model.updateScopes(scopes.stream().map((Function) scope -> toModel(scope, resourceServer, authorization)).collect(Collectors.toSet())); + } + + resource.setId(model.getId()); + + return model; + } + + public static Scope toModel(ScopeRepresentation scope, ResourceServer resourceServer, AuthorizationProvider authorization) { + StoreFactory storeFactory = authorization.getStoreFactory(); + ScopeStore scopeStore = storeFactory.getScopeStore(); + Scope existing; + + if (scope.getId() != null) { + existing = scopeStore.findById(scope.getId()); + } else { + existing = scopeStore.findByName(scope.getName(), resourceServer.getId()); + } + + if (existing != null) { + existing.setName(scope.getName()); + existing.setIconUri(scope.getIconUri()); + return existing; + } + + Scope model = scopeStore.create(scope.getName(), resourceServer); + model.setIconUri(scope.getIconUri()); + scope.setId(model.getId()); + + return model; + } } diff --git a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java index 5df5a0b08b4..6146594f4ef 100644 --- a/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java +++ b/services/src/main/java/org/keycloak/authorization/DefaultAuthorizationProviderFactory.java @@ -22,6 +22,7 @@ import org.keycloak.Config; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.RealmModel; import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider; import java.util.concurrent.Executor; @@ -35,13 +36,7 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide @Override public AuthorizationProvider create(KeycloakSession session) { - StoreFactory storeFactory = session.getProvider(CachedStoreFactoryProvider.class); - - if (storeFactory == null) { - storeFactory = session.getProvider(StoreFactory.class); - } - - return new AuthorizationProvider(session, storeFactory); + return create(session, session.getContext().getRealm()); } @Override @@ -70,4 +65,15 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide public String getId() { return "authorization"; } -} + + @Override + public AuthorizationProvider create(KeycloakSession session, RealmModel realm) { + StoreFactory storeFactory = session.getProvider(CachedStoreFactoryProvider.class); + + if (storeFactory == null) { + storeFactory = session.getProvider(StoreFactory.class); + } + + return new AuthorizationProvider(session, realm, storeFactory); + } +} \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java index 4a115ae05a5..4af34adf602 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java @@ -90,15 +90,15 @@ public class PolicyEvaluationService { public void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) { KeycloakIdentity identity = createIdentity(evaluationRequest); EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity); - authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(evaluationRequest, authorization, identity, asyncResponse)); + authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(authorization, identity, asyncResponse)); } - private DecisionResultCollector createDecisionCollector(PolicyEvaluationRequest evaluationRequest, AuthorizationProvider authorization, KeycloakIdentity identity, AsyncResponse asyncResponse) { + private DecisionResultCollector createDecisionCollector(AuthorizationProvider authorization, KeycloakIdentity identity, AsyncResponse asyncResponse) { return new DecisionResultCollector() { @Override protected void onComplete(List results) { try { - asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(evaluationRequest, results, resourceServer, authorization, identity)).build()); + asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(results, resourceServer, authorization, identity)).build()); } catch (Throwable cause) { asyncResponse.resume(cause); } diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java index 7c529e992fa..d7e6dee2a45 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java @@ -17,15 +17,11 @@ */ package org.keycloak.authorization.admin; -import com.fasterxml.jackson.databind.ObjectMapper; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.admin.util.Models; import org.keycloak.authorization.model.Policy; -import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; import org.keycloak.authorization.policy.provider.PolicyProviderFactory; import org.keycloak.authorization.store.PolicyStore; @@ -45,11 +41,10 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import java.io.IOException; -import java.util.HashSet; import java.util.stream.Collectors; -import static org.keycloak.authorization.admin.util.Models.toRepresentation; +import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; +import static org.keycloak.models.utils.RepresentationToModel.toModel; /** * @author Pedro Igor @@ -72,12 +67,7 @@ public class PolicyService { @NoCache public Response create(PolicyRepresentation representation) { this.auth.requireManage(); - Policy policy = Models.toModel(representation, this.resourceServer, authorization); - - updateResources(policy, authorization); - updateAssociatedPolicies(policy); - updateScopes(policy, authorization); - + Policy policy = toModel(representation, this.resourceServer, authorization); PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization); if (resource != null) { @@ -108,15 +98,7 @@ public class PolicyService { return Response.status(Status.NOT_FOUND).build(); } - policy.setName(representation.getName()); - policy.setDescription(representation.getDescription()); - policy.setConfig(representation.getConfig()); - policy.setDecisionStrategy(representation.getDecisionStrategy()); - policy.setLogic(representation.getLogic()); - - updateResources(policy, authorization); - updateAssociatedPolicies(policy); - updateScopes(policy, authorization); + policy = toModel(representation, resourceServer, authorization); PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization); @@ -262,137 +244,4 @@ public class PolicyService { return null; } - - private void updateScopes(Policy policy, AuthorizationProvider authorization) { - String scopes = policy.getConfig().get("scopes"); - if (scopes != null) { - String[] scopeIds; - - try { - scopeIds = new ObjectMapper().readValue(scopes, String[].class); - } catch (IOException e) { - throw new RuntimeException(e); - } - - StoreFactory storeFactory = authorization.getStoreFactory(); - - for (String scopeId : scopeIds) { - boolean hasScope = false; - - for (Scope scopeModel : new HashSet(policy.getScopes())) { - if (scopeModel.getId().equals(scopeId)) { - hasScope = true; - } - } - if (!hasScope) { - policy.addScope(storeFactory.getScopeStore().findById(scopeId)); - } - } - - for (Scope scopeModel : new HashSet(policy.getScopes())) { - boolean hasScope = false; - - for (String scopeId : scopeIds) { - if (scopeModel.getId().equals(scopeId)) { - hasScope = true; - } - } - if (!hasScope) { - policy.removeScope(scopeModel); - } - } - } - } - - private void updateAssociatedPolicies(Policy policy) { - String policies = policy.getConfig().get("applyPolicies"); - - if (policies != null) { - String[] policyIds; - - try { - policyIds = new ObjectMapper().readValue(policies, String[].class); - } catch (IOException e) { - throw new RuntimeException(e); - } - - StoreFactory storeFactory = authorization.getStoreFactory(); - PolicyStore policyStore = storeFactory.getPolicyStore(); - - for (String policyId : policyIds) { - boolean hasPolicy = false; - - for (Policy policyModel : new HashSet(policy.getAssociatedPolicies())) { - if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) { - hasPolicy = true; - } - } - - - if (!hasPolicy) { - Policy associatedPolicy = policyStore.findById(policyId); - - if (associatedPolicy == null) { - associatedPolicy = policyStore.findByName(policyId, this.resourceServer.getId()); - } - - policy.addAssociatedPolicy(associatedPolicy); - } - } - - for (Policy policyModel : new HashSet(policy.getAssociatedPolicies())) { - boolean hasPolicy = false; - - for (String policyId : policyIds) { - if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) { - hasPolicy = true; - } - } - if (!hasPolicy) { - policy.removeAssociatedPolicy(policyModel);; - } - } - } - } - - private void updateResources(Policy policy, AuthorizationProvider authorization) { - String resources = policy.getConfig().get("resources"); - if (resources != null) { - String[] resourceIds; - - try { - resourceIds = new ObjectMapper().readValue(resources, String[].class); - } catch (IOException e) { - throw new RuntimeException(e); - } - - StoreFactory storeFactory = authorization.getStoreFactory(); - - for (String resourceId : resourceIds) { - boolean hasResource = false; - for (Resource resourceModel : new HashSet(policy.getResources())) { - if (resourceModel.getId().equals(resourceId)) { - hasResource = true; - } - } - if (!hasResource && !"".equals(resourceId)) { - policy.addResource(storeFactory.getResourceStore().findById(resourceId)); - } - } - - for (Resource resourceModel : new HashSet(policy.getResources())) { - boolean hasResource = false; - - for (String resourceId : resourceIds) { - if (resourceModel.getId().equals(resourceId)) { - hasResource = true; - } - } - - if (!hasResource) { - policy.removeResource(resourceModel); - } - } - } - } } diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java index 567675f23dc..d02b8270f76 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java @@ -19,33 +19,25 @@ package org.keycloak.authorization.admin; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.admin.util.Models; -import org.keycloak.authorization.model.Policy; -import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.ScopeStore; import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.exportimport.util.ExportUtils; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; -import org.keycloak.models.UserFederationManager; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.Logic; import org.keycloak.representations.idm.authorization.PolicyRepresentation; -import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; -import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.services.resources.admin.RealmAuth; -import org.keycloak.util.JsonSerialization; -import javax.management.relation.Role; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -57,14 +49,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; + +import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; /** * @author Pedro Igor @@ -73,7 +60,6 @@ public class ResourceServerService { private final AuthorizationProvider authorization; private final RealmAuth auth; - private final RealmModel realm; private final KeycloakSession session; private ResourceServer resourceServer; private final ClientModel client; @@ -83,7 +69,6 @@ public class ResourceServerService { this.session = authorization.getKeycloakSession(); this.client = client; this.resourceServer = resourceServer; - this.realm = client.getRealm(); this.auth = auth; } @@ -128,7 +113,7 @@ public class ResourceServerService { @Produces("application/json") public Response findById() { this.auth.requireView(); - return Response.ok(Models.toRepresentation(this.resourceServer, this.realm)).build(); + return Response.ok(toRepresentation(this.resourceServer, this.client)).build(); } @Path("/settings") @@ -136,59 +121,7 @@ public class ResourceServerService { @Produces("application/json") public Response exportSettings() { this.auth.requireManage(); - StoreFactory storeFactory = authorization.getStoreFactory(); - ResourceServerRepresentation settings = Models.toRepresentation(resourceServer, this.realm); - - settings.setId(null); - settings.setName(null); - settings.setClientId(null); - - List resources = storeFactory.getResourceStore().findByResourceServer(resourceServer.getId()) - .stream().map(resource -> { - ResourceRepresentation rep = Models.toRepresentation(resource, resourceServer, authorization); - - if (rep.getOwner().getId().equals(this.resourceServer.getClientId())) { - rep.setOwner(null); - } else { - rep.getOwner().setId(null); - } - rep.setId(null); - rep.setPolicies(null); - rep.getScopes().forEach(scopeRepresentation -> { - scopeRepresentation.setId(null); - scopeRepresentation.setIconUri(null); - }); - - return rep; - }).collect(Collectors.toList()); - - settings.setResources(resources); - - List policies = new ArrayList<>(); - PolicyStore policyStore = storeFactory.getPolicyStore(); - - policies.addAll(policyStore.findByResourceServer(resourceServer.getId()) - .stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope")) - .map(policy -> createPolicyRepresentation(storeFactory, policy)).collect(Collectors.toList())); - policies.addAll(policyStore.findByResourceServer(resourceServer.getId()) - .stream().filter(policy -> policy.getType().equals("resource") || policy.getType().equals("scope")) - .map(policy -> createPolicyRepresentation(storeFactory, policy)).collect(Collectors.toList())); - - settings.setPolicies(policies); - - List scopes = storeFactory.getScopeStore().findByResourceServer(resourceServer.getId()).stream().map(scope -> { - ScopeRepresentation rep = Models.toRepresentation(scope, authorization); - - rep.setId(null); - rep.setPolicies(null); - rep.setResources(null); - - return rep; - }).collect(Collectors.toList()); - - settings.setScopes(scopes); - - return Response.ok(settings).build(); + return Response.ok(ExportUtils.exportAuthorizationSettings(session, client)).build(); } @Path("/import") @@ -197,172 +130,9 @@ public class ResourceServerService { public Response importSettings(@Context final UriInfo uriInfo, ResourceServerRepresentation rep) throws IOException { this.auth.requireManage(); - resourceServer.setPolicyEnforcementMode(rep.getPolicyEnforcementMode()); - resourceServer.setAllowRemoteResourceManagement(rep.isAllowRemoteResourceManagement()); + rep.setClientId(client.getId()); - StoreFactory storeFactory = authorization.getStoreFactory(); - ResourceStore resourceStore = storeFactory.getResourceStore(); - ScopeStore scopeStore = storeFactory.getScopeStore(); - ScopeService scopeResource = new ScopeService(resourceServer, this.authorization, this.auth); - - ResteasyProviderFactory.getInstance().injectProperties(scopeResource); - - rep.getScopes().forEach(scope -> { - Scope existing = scopeStore.findByName(scope.getName(), resourceServer.getId()); - - if (existing != null) { - scopeResource.update(existing.getId(), scope); - } else { - scopeResource.create(scope); - } - }); - - ResourceSetService resourceSetResource = new ResourceSetService(resourceServer, this.authorization, this.auth); - - rep.getResources().forEach(resourceRepresentation -> { - ResourceOwnerRepresentation owner = resourceRepresentation.getOwner(); - - if (owner == null) { - owner = new ResourceOwnerRepresentation(); - } - - owner.setId(resourceServer.getClientId()); - - if (owner.getName() != null) { - UserModel user = this.session.users().getUserByUsername(owner.getName(), this.realm); - - if (user != null) { - owner.setId(user.getId()); - } - } - - Resource existing = resourceStore.findByName(resourceRepresentation.getName(), this.resourceServer.getId()); - - if (existing != null) { - resourceSetResource.update(existing.getId(), resourceRepresentation); - } else { - resourceSetResource.create(resourceRepresentation); - } - }); - - PolicyStore policyStore = storeFactory.getPolicyStore(); - PolicyService policyResource = new PolicyService(resourceServer, this.authorization, this.auth); - - ResteasyProviderFactory.getInstance().injectProperties(policyResource); - - rep.getPolicies().forEach(policyRepresentation -> { - Map config = policyRepresentation.getConfig(); - - String roles = config.get("roles"); - - if (roles != null && !roles.isEmpty()) { - try { - List rolesMap = JsonSerialization.readValue(roles, List.class); - config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> { - String roleName = roleConfig.get("id").toString(); - String clientId = null; - int clientIdSeparator = roleName.indexOf("/"); - - if (clientIdSeparator != -1) { - clientId = roleName.substring(0, clientIdSeparator); - roleName = roleName.substring(clientIdSeparator + 1); - } - - RoleModel role; - - if (clientId == null) { - role = realm.getRole(roleName); - } else { - role = realm.getClientByClientId(clientId).getRole(roleName); - } - - // fallback to find any client role with the given name - if (role == null) { - String finalRoleName = roleName; - role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null) - .findFirst().orElse(null); - } - - if (role == null) { - throw new RuntimeException("Error while importing configuration. Role [" + role + "] could not be found."); - } - - roleConfig.put("id", role.getId()); - return roleConfig; - }).collect(Collectors.toList()))); - } catch (Exception e) { - throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); - } - } - - String users = config.get("users"); - - if (users != null && !users.isEmpty()) { - try { - List usersMap = JsonSerialization.readValue(users, List.class); - config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userName -> this.session.users().getUserByUsername(userName, this.realm).getId()).collect(Collectors.toList()))); - } catch (Exception e) { - throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); - } - } - - String scopes = config.get("scopes"); - - if (scopes != null && !scopes.isEmpty()) { - try { - List scopesMap = JsonSerialization.readValue(scopes, List.class); - config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> { - Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId()); - - if (newScope == null) { - throw new RuntimeException("Scope with name [" + scopeName + "] not defined."); - } - - return newScope.getId(); - }).collect(Collectors.toList()))); - } catch (Exception e) { - throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); - } - } - - String policyResources = config.get("resources"); - - if (policyResources != null && !policyResources.isEmpty()) { - try { - List resources = JsonSerialization.readValue(policyResources, List.class); - config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> storeFactory.getResourceStore().findByName(resourceName, resourceServer.getId()).getId()).collect(Collectors.toList()))); - } catch (Exception e) { - throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); - } - } - - String applyPolicies = config.get("applyPolicies"); - - if (applyPolicies != null && !applyPolicies.isEmpty()) { - try { - List policies = JsonSerialization.readValue(applyPolicies, List.class); - config.put("applyPolicies", JsonSerialization.writeValueAsString(policies.stream().map(policyName -> { - Policy policy = policyStore.findByName(policyName, resourceServer.getId()); - - if (policy == null) { - throw new RuntimeException("Policy with name [" + policyName + "] not defined."); - } - - return policy.getId(); - }).collect(Collectors.toList()))); - } catch (Exception e) { - throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e); - } - } - - Policy existing = policyStore.findByName(policyRepresentation.getName(), this.resourceServer.getId()); - - if (existing != null) { - policyResource.update(existing.getId(), policyRepresentation); - } else { - policyResource.create(policyRepresentation); - } - }); + RepresentationToModel.toModel(rep, authorization); return Response.noContent().build(); } @@ -458,61 +228,4 @@ public class ResourceServerService { serviceAccount.grantRole(umaProtectionRole); } } - - private PolicyRepresentation createPolicyRepresentation(StoreFactory storeFactory, Policy policy) { - try { - PolicyRepresentation rep = Models.toRepresentation(policy, authorization); - - rep.setId(null); - rep.setDependentPolicies(null); - - Map config = rep.getConfig(); - - String roles = config.get("roles"); - - if (roles != null && !roles.isEmpty()) { - List rolesMap = JsonSerialization.readValue(roles, List.class); - config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleMap -> { - roleMap.put("id", realm.getRoleById(roleMap.get("id").toString()).getName()); - return roleMap; - }).collect(Collectors.toList()))); - } - - String users = config.get("users"); - - if (users != null && !users.isEmpty()) { - UserFederationManager userManager = this.session.users(); - List userIds = JsonSerialization.readValue(users, List.class); - config.put("users", JsonSerialization.writeValueAsString(userIds.stream().map(userId -> userManager.getUserById(userId, this.realm).getUsername()).collect(Collectors.toList()))); - } - - String scopes = config.get("scopes"); - - if (scopes != null && !scopes.isEmpty()) { - ScopeStore scopeStore = storeFactory.getScopeStore(); - List scopeIds = JsonSerialization.readValue(scopes, List.class); - config.put("scopes", JsonSerialization.writeValueAsString(scopeIds.stream().map(scopeId -> scopeStore.findById(scopeId).getName()).collect(Collectors.toList()))); - } - - String policyResources = config.get("resources"); - - if (policyResources != null && !policyResources.isEmpty()) { - ResourceStore resourceStore = storeFactory.getResourceStore(); - List resourceIds = JsonSerialization.readValue(policyResources, List.class); - config.put("resources", JsonSerialization.writeValueAsString(resourceIds.stream().map(resourceId -> resourceStore.findById(resourceId).getName()).collect(Collectors.toList()))); - } - - Set associatedPolicies = policy.getAssociatedPolicies(); - - if (!associatedPolicies.isEmpty()) { - config.put("applyPolicies", JsonSerialization.writeValueAsString(associatedPolicies.stream().map(associated -> associated.getName()).collect(Collectors.toList()))); - } - - rep.setAssociatedPolicies(null); - - return rep; - } catch (Exception e) { - throw new RuntimeException("Error while exporting policy [" + policy.getName() + "].", e); - } - } } diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java index 8bb4a9b8aa1..c628a82f3ee 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java @@ -19,7 +19,6 @@ package org.keycloak.authorization.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.admin.util.Models; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; @@ -27,7 +26,6 @@ import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.representations.idm.authorization.ResourceRepresentation; -import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.services.ErrorResponse; import org.keycloak.services.resources.admin.RealmAuth; @@ -45,7 +43,8 @@ import javax.ws.rs.core.Response.Status; import java.util.List; import java.util.stream.Collectors; -import static org.keycloak.authorization.admin.util.Models.toRepresentation; +import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; +import static org.keycloak.models.utils.RepresentationToModel.toModel; /** * @author Pedro Igor @@ -75,7 +74,7 @@ public class ResourceSetService { return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists."); } - Resource model = Models.toModel(resource, this.resourceServer, authorization); + Resource model = toModel(resource, this.resourceServer, authorization); ResourceRepresentation representation = new ResourceRepresentation(); @@ -99,14 +98,7 @@ public class ResourceSetService { return Response.status(Status.NOT_FOUND).build(); } - model.setName(resource.getName()); - model.setType(resource.getType()); - model.setUri(resource.getUri()); - model.setIconUri(resource.getIconUri()); - - model.updateScopes(resource.getScopes().stream() - .map((ScopeRepresentation scope) -> Models.toModel(scope, this.resourceServer, authorization)) - .collect(Collectors.toSet())); + toModel(resource, resourceServer, authorization); return Response.noContent().build(); } diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java index 97b85418f58..df0e6daee47 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java @@ -44,8 +44,8 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import static org.keycloak.authorization.admin.util.Models.toModel; -import static org.keycloak.authorization.admin.util.Models.toRepresentation; +import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; +import static org.keycloak.models.utils.RepresentationToModel.toModel; /** * @author Pedro Igor @@ -88,8 +88,7 @@ public class ScopeService { return Response.status(Status.NOT_FOUND).build(); } - model.setName(scope.getName()); - model.setIconUri(scope.getIconUri()); + toModel(scope, resourceServer, authorization); return Response.noContent().build(); } diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java index 37d07e089dd..d320a644f90 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java +++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponse.java @@ -20,7 +20,6 @@ package org.keycloak.authorization.admin.representation; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.Decision.Effect; -import org.keycloak.authorization.admin.util.Models; import org.keycloak.authorization.common.KeycloakIdentity; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.ResourceServer; @@ -28,6 +27,7 @@ import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.policy.evaluation.Result; import org.keycloak.authorization.policy.evaluation.Result.PolicyResult; import org.keycloak.authorization.util.Permissions; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.AccessToken; import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation; @@ -56,7 +56,7 @@ public class PolicyEvaluationResponse { } - public static PolicyEvaluationResponse build(PolicyEvaluationRequest evaluationRequest, List results, ResourceServer resourceServer, AuthorizationProvider authorization, KeycloakIdentity identity) { + public static PolicyEvaluationResponse build(List results, ResourceServer resourceServer, AuthorizationProvider authorization, KeycloakIdentity identity) { PolicyEvaluationResponse response = new PolicyEvaluationResponse(); List resultsRep = new ArrayList<>(); AccessToken accessToken = identity.getAccessToken(); @@ -80,21 +80,16 @@ public class PolicyEvaluationResponse { resultsRep.add(rep); if (result.getPermission().getResource() != null) { - rep.setResource(Models.toRepresentation(result.getPermission().getResource(), resourceServer, authorization)); + rep.setResource(ModelToRepresentation.toRepresentation(result.getPermission().getResource(), resourceServer, authorization)); } else { ResourceRepresentation resource = new ResourceRepresentation(); - resource.setName("Any Resource with Scopes " + result.getPermission().getScopes().stream().map(new Function() { - @Override - public String apply(Scope scope) { - return scope.getName(); - } - }).collect(Collectors.toList())); + resource.setName("Any Resource with Scopes " + result.getPermission().getScopes().stream().map(Scope::getName).collect(Collectors.toList())); rep.setResource(resource); } - rep.setScopes(result.getPermission().getScopes().stream().map(scope -> Models.toRepresentation(scope, authorization)).collect(Collectors.toList())); + rep.setScopes(result.getPermission().getScopes().stream().map(scope -> ModelToRepresentation.toRepresentation(scope, authorization)).collect(Collectors.toList())); List policies = new ArrayList<>(); @@ -163,7 +158,7 @@ public class PolicyEvaluationResponse { if (policy.getStatus().equals(Effect.DENY)) { Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId()); - for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scopeModel -> Models.toRepresentation(scopeModel, authorization)).collect(Collectors.toList())) { + for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scopeModel -> ModelToRepresentation.toRepresentation(scopeModel, authorization)).collect(Collectors.toList())) { if (!policy.getScopes().contains(scope)) { policy.getScopes().add(scope); } @@ -185,7 +180,7 @@ public class PolicyEvaluationResponse { private static PolicyResultRepresentation toRepresentation(PolicyResult policy, AuthorizationProvider authorization) { PolicyResultRepresentation policyResultRep = new PolicyResultRepresentation(); - policyResultRep.setPolicy(Models.toRepresentation(policy.getPolicy(), authorization)); + policyResultRep.setPolicy(ModelToRepresentation.toRepresentation(policy.getPolicy(), authorization)); policyResultRep.setStatus(policy.getStatus()); policyResultRep.setAssociatedPolicies(policy.getAssociatedPolicies().stream().map(result -> toRepresentation(result, authorization)).collect(Collectors.toList())); diff --git a/services/src/main/java/org/keycloak/authorization/admin/util/Models.java b/services/src/main/java/org/keycloak/authorization/admin/util/Models.java deleted file mode 100644 index f233e985755..00000000000 --- a/services/src/main/java/org/keycloak/authorization/admin/util/Models.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2016 Red Hat, Inc., and individual 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.authorization.admin.util; - -import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.ErrorCode; -import org.keycloak.authorization.model.Policy; -import org.keycloak.authorization.model.Resource; -import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.authorization.model.Scope; -import org.keycloak.authorization.store.PolicyStore; -import org.keycloak.authorization.store.ResourceServerStore; -import org.keycloak.authorization.store.ResourceStore; -import org.keycloak.authorization.store.StoreFactory; -import org.keycloak.models.ClientModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.representations.idm.authorization.PolicyRepresentation; -import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation; -import org.keycloak.representations.idm.authorization.ResourceRepresentation; -import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; -import org.keycloak.representations.idm.authorization.ScopeRepresentation; -import org.keycloak.services.ErrorResponseException; -import org.keycloak.util.JsonSerialization; - -import javax.ws.rs.core.Response.Status; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -/** - * Some utility methods to transform models to representations and vice-versa. - * - * @author Pedro Igor - */ -public final class Models { - - public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider) { - ScopeRepresentation scope = new ScopeRepresentation(); - - scope.setId(model.getId()); - scope.setName(model.getName()); - scope.setIconUri(model.getIconUri()); - - StoreFactory storeFactory = authorizationProvider.getStoreFactory(); - - scope.setResources(new ArrayList<>()); - - storeFactory.getResourceStore().findByScope(model.getId()).forEach(resource -> scope.getResources().add(toRepresentation(resource, resource.getResourceServer(), authorizationProvider))); - - PolicyStore policyStore = storeFactory.getPolicyStore(); - - scope.setPolicies(new ArrayList<>()); - - policyStore.findByScopeIds(Arrays.asList(model.getId()), model.getResourceServer().getId()).forEach(policyModel -> { - PolicyRepresentation policy = new PolicyRepresentation(); - - policy.setId(policyModel.getId()); - policy.setName(policyModel.getName()); - policy.setType(policyModel.getType()); - - if (!scope.getPolicies().contains(policy)) { - scope.getPolicies().add(policy); - } - }); - - return scope; - } - - public static Scope toModel(ScopeRepresentation scope, ResourceServer resourceServer, AuthorizationProvider authorization) { - StoreFactory storeFactory = authorization.getStoreFactory(); - Scope model = storeFactory.getScopeStore().findByName(scope.getName(), resourceServer.getId()); - - if (model == null) { - model = storeFactory.getScopeStore().create(scope.getName(), resourceServer); - - model.setIconUri(scope.getIconUri()); - } - - return model; - } - - public static ResourceServerRepresentation toRepresentation(ResourceServer model, RealmModel realm) { - ResourceServerRepresentation server = new ResourceServerRepresentation(); - - server.setId(model.getId()); - server.setClientId(model.getClientId()); - ClientModel clientById = realm.getClientById(model.getClientId()); - server.setName(clientById.getClientId()); - server.setAllowRemoteResourceManagement(model.isAllowRemoteResourceManagement()); - server.setPolicyEnforcementMode(model.getPolicyEnforcementMode()); - - return server; - } - - public static ResourceServer toModel(ResourceServerRepresentation server, AuthorizationProvider authorization) { - RealmModel realm = authorization.getKeycloakSession().getContext().getRealm(); - ClientModel client = realm.getClientById(server.getClientId()); - - if (client == null) { - throw new ErrorResponseException(ErrorCode.INVALID_CLIENT_ID, "Client with id [" + server.getClientId() + "] not found in realm [" + realm.getName() + "].", Status.BAD_REQUEST); - } - - if (!client.isServiceAccountsEnabled()) { - throw new ErrorResponseException(ErrorCode.INVALID_CLIENT_ID, "Client with id [" + server.getClientId() + "] must have a service account.", Status.BAD_REQUEST); - } - - ResourceServer existingResourceServer = authorization.getStoreFactory().getResourceServerStore().findByClient(client.getId()); - - if (existingResourceServer != null) { - throw new ErrorResponseException(ErrorCode.INVALID_CLIENT_ID, "Resource server already exists with client id [" + server.getClientId() + "].", Status.BAD_REQUEST); - } - - if (server.getName() == null) { - server.setName(client.getName()); - } - - ResourceServer model = authorization.getStoreFactory().getResourceServerStore().create(client.getId()); - - model.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement()); - model.setPolicyEnforcementMode(server.getPolicyEnforcementMode()); - - return model; - } - - public static PolicyRepresentation toRepresentation(Policy model, AuthorizationProvider authorization) { - PolicyRepresentation representation = new PolicyRepresentation(); - - representation.setId(model.getId()); - representation.setName(model.getName()); - representation.setDescription(model.getDescription()); - representation.setType(model.getType()); - representation.setDecisionStrategy(model.getDecisionStrategy()); - representation.setLogic(model.getLogic()); - representation.setConfig(new HashMap<>(model.getConfig())); - - List policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId()); - - representation.setDependentPolicies(policies.stream().map(policy -> { - PolicyRepresentation representation1 = new PolicyRepresentation(); - - representation1.setId(policy.getId()); - representation1.setName(policy.getName()); - - return representation1; - }).collect(Collectors.toList())); - - List associatedPolicies = new ArrayList<>(); - - List obj = model.getAssociatedPolicies().stream().map(policy -> { - PolicyRepresentation representation1 = new PolicyRepresentation(); - - representation1.setId(policy.getId()); - representation1.setName(policy.getName()); - representation1.setType(policy.getType()); - - associatedPolicies.add(representation1); - - return policy.getId(); - }).collect(Collectors.toList()); - - representation.setAssociatedPolicies(associatedPolicies); - - try { - representation.getConfig().put("applyPolicies", JsonSerialization.writeValueAsString(obj)); - } catch (IOException e) { - e.printStackTrace(); - } - - return representation; - } - - public static Policy toModel(PolicyRepresentation policy, ResourceServer resourceServer, AuthorizationProvider authorizationManager) { - Policy model = authorizationManager.getStoreFactory().getPolicyStore().create(policy.getName(), policy.getType(), resourceServer); - - model.setDescription(policy.getDescription()); - model.setDecisionStrategy(policy.getDecisionStrategy()); - model.setLogic(policy.getLogic()); - model.setConfig(policy.getConfig()); - - return model; - } - - public static ResourceRepresentation toRepresentation(Resource model, ResourceServer resourceServer, AuthorizationProvider authorization) { - ResourceRepresentation resource = new ResourceRepresentation(); - - resource.setId(model.getId()); - resource.setType(model.getType()); - resource.setName(model.getName()); - resource.setUri(model.getUri()); - resource.setIconUri(model.getIconUri()); - - ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation(); - - owner.setId(model.getOwner()); - - KeycloakSession keycloakSession = authorization.getKeycloakSession(); - RealmModel realm = keycloakSession.getContext().getRealm(); - - if (owner.getId().equals(resourceServer.getClientId())) { - ClientModel clientModel = realm.getClientById(resourceServer.getClientId()); - owner.setName(clientModel.getClientId()); - } else { - UserModel userModel = keycloakSession.users().getUserById(owner.getId(), realm); - - if (userModel == null) { - throw new ErrorResponseException("invalid_owner", "Could not find the user [" + owner.getId() + "] who owns the Resource [" + resource.getId() + "].", Status.BAD_REQUEST); - } - - owner.setName(userModel.getUsername()); - } - - resource.setOwner(owner); - - resource.setScopes(model.getScopes().stream().map(model1 -> { - ScopeRepresentation scope = new ScopeRepresentation(); - scope.setId(model1.getId()); - scope.setName(model1.getName()); - String iconUri = model1.getIconUri(); - if (iconUri != null) { - scope.setIconUri(iconUri); - } - return scope; - }).collect(Collectors.toSet())); - - resource.setTypedScopes(new ArrayList<>()); - - if (resource.getType() != null) { - ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore(); - for (Resource typed : resourceStore.findByType(resource.getType())) { - if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(resource.getId())) { - resource.setTypedScopes(typed.getScopes().stream().map(model1 -> { - ScopeRepresentation scope = new ScopeRepresentation(); - scope.setId(model1.getId()); - scope.setName(model1.getName()); - String iconUri = model1.getIconUri(); - if (iconUri != null) { - scope.setIconUri(iconUri); - } - return scope; - }).filter(scopeRepresentation -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList())); - } - } - } - - resource.setPolicies(new ArrayList<>()); - - Set policies = new HashSet<>(); - PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore(); - - policies.addAll(policyStore.findByResource(resource.getId())); - policies.addAll(policyStore.findByResourceType(resource.getType(), resourceServer.getId())); - policies.addAll(policyStore.findByScopeIds(resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId())); - - for (Policy policyModel : policies) { - PolicyRepresentation policy = new PolicyRepresentation(); - - policy.setId(policyModel.getId()); - policy.setName(policyModel.getName()); - policy.setType(policyModel.getType()); - - if (!resource.getPolicies().contains(policy)) { - resource.getPolicies().add(policy); - } - } - - return resource; - } - - public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) { - ResourceOwnerRepresentation owner = resource.getOwner(); - - if (owner == null) { - owner = new ResourceOwnerRepresentation(); - owner.setId(resourceServer.getClientId()); - } - - if (owner.getId() == null) { - throw new ErrorResponseException("invalid_owner", "No owner specified for resource [" + resource.getName() + "].", Status.BAD_REQUEST); - } - - ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore(); - Resource model = resourceStore.create(resource.getName(), resourceServer, owner.getId()); - - model.setType(resource.getType()); - model.setUri(resource.getUri()); - model.setIconUri(resource.getIconUri()); - - Set scopes = resource.getScopes(); - - if (scopes != null) { - model.updateScopes(scopes.stream().map((Function) scope -> toModel(scope, resourceServer, authorization)).collect(Collectors.toSet())); - } - - return model; - } -} diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java index e45b9765720..168d62ba6ea 100644 --- a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java +++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java @@ -19,12 +19,12 @@ package org.keycloak.authorization.protection.resource; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.admin.ResourceSetService; -import org.keycloak.authorization.admin.util.Models; import org.keycloak.authorization.identity.Identity; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.protection.resource.representation.UmaResourceRepresentation; import org.keycloak.authorization.protection.resource.representation.UmaScopeRepresentation; import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; @@ -133,25 +133,25 @@ public class ResourceService { if ("name".equals(filterType)) { resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getName())).collect(Collectors.toSet()).stream() - .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization)) + .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization)) .collect(Collectors.toList())); } else if ("type".equals(filterType)) { resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getType())).collect(Collectors.toSet()).stream() - .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization)) + .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization)) .collect(Collectors.toList())); } else if ("uri".equals(filterType)) { resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getUri())).collect(Collectors.toSet()).stream() - .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization)) + .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization)) .collect(Collectors.toList())); } else if ("owner".equals(filterType)) { resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getOwner())).collect(Collectors.toSet()).stream() - .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization)) + .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization)) .collect(Collectors.toList())); } } } else { resources = storeFactory.getResourceStore().findByOwner(identity.getId()).stream() - .map(resource -> Models.toRepresentation(resource, this.resourceServer, authorization)) + .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization)) .collect(Collectors.toSet()); } 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 9665a50a498..1b2caf946e3 100755 --- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java +++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java @@ -22,17 +22,61 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.AuthorizationProviderFactory; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.store.PolicyStore; +import org.keycloak.authorization.store.ResourceStore; +import org.keycloak.authorization.store.ScopeStore; +import org.keycloak.authorization.store.StoreFactory; import org.keycloak.common.Version; import org.keycloak.common.util.Base64; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.component.ComponentModel; -import org.keycloak.models.*; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientTemplateModel; +import org.keycloak.models.FederatedIdentityModel; +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.UserConsentModel; +import org.keycloak.models.UserCredentialValueModel; +import org.keycloak.models.UserFederationManager; +import org.keycloak.models.UserModel; import org.keycloak.models.utils.ModelToRepresentation; -import org.keycloak.representations.idm.*; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ClientTemplateRepresentation; +import org.keycloak.representations.idm.ComponentExportRepresentation; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.FederatedIdentityRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.RolesRepresentation; +import org.keycloak.representations.idm.ScopeMappingRepresentation; +import org.keycloak.representations.idm.UserConsentRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.representations.idm.authorization.ScopeRepresentation; +import org.keycloak.util.JsonSerialization; import java.io.IOException; import java.io.OutputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; /** * @author Marek Posolda @@ -58,7 +102,7 @@ public class ExportUtils { List clients = realm.getClients(); List clientReps = new ArrayList<>(); for (ClientModel app : clients) { - ClientRepresentation clientRep = exportClient(app); + ClientRepresentation clientRep = exportClient(session, app); clientReps.add(clientRep); } rep.setClients(clientReps); @@ -207,12 +251,137 @@ public class ExportUtils { * @param client * @return full ApplicationRepresentation */ - public static ClientRepresentation exportClient(ClientModel client) { + public static ClientRepresentation exportClient(KeycloakSession session, ClientModel client) { ClientRepresentation clientRep = ModelToRepresentation.toRepresentation(client); clientRep.setSecret(client.getSecret()); + clientRep.setAuthorizationSettings(exportAuthorizationSettings(session,client)); return clientRep; } + public static ResourceServerRepresentation exportAuthorizationSettings(KeycloakSession session, ClientModel client) { + AuthorizationProviderFactory providerFactory = (AuthorizationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class); + AuthorizationProvider authorization = providerFactory.create(session, client.getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer settingsModel = authorization.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + + if (settingsModel == null) { + return null; + } + + ResourceServerRepresentation representation = toRepresentation(settingsModel, client); + + representation.setId(null); + representation.setName(null); + representation.setClientId(null); + + List resources = storeFactory.getResourceStore().findByResourceServer(settingsModel.getId()) + .stream().map(resource -> { + ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization); + + if (rep.getOwner().getId().equals(settingsModel.getClientId())) { + rep.setOwner(null); + } else { + rep.getOwner().setId(null); + } + rep.setId(null); + rep.setPolicies(null); + rep.getScopes().forEach(scopeRepresentation -> { + scopeRepresentation.setId(null); + scopeRepresentation.setIconUri(null); + }); + + return rep; + }).collect(Collectors.toList()); + + representation.setResources(resources); + + List policies = new ArrayList<>(); + PolicyStore policyStore = storeFactory.getPolicyStore(); + + policies.addAll(policyStore.findByResourceServer(settingsModel.getId()) + .stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope")) + .map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList())); + policies.addAll(policyStore.findByResourceServer(settingsModel.getId()) + .stream().filter(policy -> policy.getType().equals("resource") || policy.getType().equals("scope")) + .map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList())); + + representation.setPolicies(policies); + + List scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel.getId()).stream().map(scope -> { + ScopeRepresentation rep = toRepresentation(scope, authorization); + + rep.setId(null); + rep.setPolicies(null); + rep.setResources(null); + + return rep; + }).collect(Collectors.toList()); + + representation.setScopes(scopes); + + return representation; + } + + private static PolicyRepresentation createPolicyRepresentation(AuthorizationProvider authorizationProvider, Policy policy) { + KeycloakSession session = authorizationProvider.getKeycloakSession(); + RealmModel realm = authorizationProvider.getRealm(); + StoreFactory storeFactory = authorizationProvider.getStoreFactory(); + try { + PolicyRepresentation rep = toRepresentation(policy, authorizationProvider); + + rep.setId(null); + rep.setDependentPolicies(null); + + Map config = rep.getConfig(); + + String roles = config.get("roles"); + + if (roles != null && !roles.isEmpty()) { + List rolesMap = JsonSerialization.readValue(roles, List.class); + config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleMap -> { + roleMap.put("id", realm.getRoleById(roleMap.get("id").toString()).getName()); + return roleMap; + }).collect(Collectors.toList()))); + } + + String users = config.get("users"); + + if (users != null && !users.isEmpty()) { + UserFederationManager userManager = session.users(); + List userIds = JsonSerialization.readValue(users, List.class); + config.put("users", JsonSerialization.writeValueAsString(userIds.stream().map(userId -> userManager.getUserById(userId, realm).getUsername()).collect(Collectors.toList()))); + } + + String scopes = config.get("scopes"); + + if (scopes != null && !scopes.isEmpty()) { + ScopeStore scopeStore = storeFactory.getScopeStore(); + List scopeIds = JsonSerialization.readValue(scopes, List.class); + config.put("scopes", JsonSerialization.writeValueAsString(scopeIds.stream().map(scopeId -> scopeStore.findById(scopeId).getName()).collect(Collectors.toList()))); + } + + String policyResources = config.get("resources"); + + if (policyResources != null && !policyResources.isEmpty()) { + ResourceStore resourceStore = storeFactory.getResourceStore(); + List resourceIds = JsonSerialization.readValue(policyResources, List.class); + config.put("resources", JsonSerialization.writeValueAsString(resourceIds.stream().map(resourceId -> resourceStore.findById(resourceId).getName()).collect(Collectors.toList()))); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + + if (!associatedPolicies.isEmpty()) { + config.put("applyPolicies", JsonSerialization.writeValueAsString(associatedPolicies.stream().map(associated -> associated.getName()).collect(Collectors.toList()))); + } + + rep.setAssociatedPolicies(null); + + return rep; + } catch (Exception e) { + throw new RuntimeException("Error while exporting policy [" + policy.getName() + "].", e); + } + } + public static List exportRoles(Collection roles) { List roleReps = new ArrayList(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 5d12db4ca59..e4dfe0eea25 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -733,7 +733,7 @@ public class AccountTest extends TestRealmKeycloakTest { Assert.assertTrue(applicationsPage.isCurrent()); Map apps = applicationsPage.getApplications(); - Assert.assertEquals(3, apps.size()); + Assert.assertEquals(4, apps.size()); AccountApplicationsPage.AppEntry accountEntry = apps.get("Account"); Assert.assertEquals(2, accountEntry.getRolesAvailable().size()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractDefaultAuthzConfigAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractDefaultAuthzConfigAdapterTest.java index a6167eb0f55..87c0024b00b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractDefaultAuthzConfigAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractDefaultAuthzConfigAdapterTest.java @@ -21,12 +21,16 @@ import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.Test; +import org.keycloak.admin.client.resource.AuthorizationResource; +import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -67,6 +71,21 @@ public abstract class AbstractDefaultAuthzConfigAdapterTest extends AbstractExam assertTrue(this.driver.getPageSource().contains("Your permissions are")); assertTrue(this.driver.getPageSource().contains("Default Resource")); + + boolean hasDefaultPermission = false; + boolean hasDefaultPolicy = false; + + for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { + if ("Default Policy".equals(policy.getName())) { + hasDefaultPolicy = true; + } + if ("Default Permission".equals(policy.getName())) { + hasDefaultPermission = true; + } + } + + assertTrue(hasDefaultPermission); + assertTrue(hasDefaultPolicy); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); } @@ -95,4 +114,14 @@ public abstract class AbstractDefaultAuthzConfigAdapterTest extends AbstractExam // enable authorization services in order to generate the default config and continue with tests clients.get(client.getId()).update(client); } + + private AuthorizationResource getAuthorizationResource() throws FileNotFoundException { + return getClientResource(RESOURCE_SERVER_ID).authorization(); + } + + private ClientResource getClientResource(String clientId) { + ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients(); + ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0); + return clients.get(resourceServer.getId()); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java index d7946ce6c72..006dc920ecf 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java @@ -17,6 +17,7 @@ package org.keycloak.testsuite.admin; import org.jboss.logging.Logger; +import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RoleResource; @@ -192,4 +193,13 @@ public class ApiUtil { } return contains; } + + public static AuthorizationResource findAuthorizationSettings(RealmResource realm, String clientId) { + for (ClientRepresentation c : realm.clients().findAll()) { + if (c.getClientId().equals(clientId)) { + return realm.clients().get(c.getId()).authorization(); + } + } + return null; + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java index dd5729a8a09..c87bdf13fe0 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java @@ -814,7 +814,7 @@ public class UserTest extends AbstractAdminTest { // List realm roles assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION); - assertNames(roles.realmLevel().listAvailable(), "admin"); + assertNames(roles.realmLevel().listAvailable(), "admin", "customer-user-premium"); assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION); // List client roles diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java index f4553031c99..0543238e030 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java @@ -432,7 +432,7 @@ public class GroupTest extends AbstractGroupTest { // List realm roles assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite"); - assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user"); + assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user", "customer-user-premium"); assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child"); // List client roles diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java index 95b327fb2a3..19aee3fecee 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java @@ -17,14 +17,18 @@ package org.keycloak.testsuite.exportimport; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; + import org.junit.Assert; import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientTemplateResource; import org.keycloak.admin.client.resource.RealmResource; @@ -50,6 +54,10 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserFederationMapperRepresentation; import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.client.KeycloakTestingClient; import org.keycloak.testsuite.util.RealmRepUtil; @@ -86,7 +94,7 @@ public class ExportImportUtil { Assert.assertEquals(0, userRsc.getFederatedIdentity().size()); List resources = realmRsc.clients().findAll(); - Assert.assertEquals(8, resources.size()); + Assert.assertEquals(9, resources.size()); // Test applications imported ClientRepresentation application = ApiUtil.findClientByClientId(realmRsc, "Application").toRepresentation(); @@ -97,7 +105,7 @@ public class ExportImportUtil { Assert.assertNotNull(otherApp); Assert.assertNull(nonExisting); List clients = realmRsc.clients().findAll(); - Assert.assertEquals(8, clients.size()); + Assert.assertEquals(9, clients.size()); Assert.assertTrue(hasClient(clients, application)); Assert.assertTrue(hasClient(clients, otherApp)); Assert.assertTrue(hasClient(clients, accountApp)); @@ -366,6 +374,8 @@ public class ExportImportUtil { UserRepresentation linked = testingClient.testing().getUserByServiceAccountClient(realm.getRealm(), otherApp.getClientId());//session.users().getUserByServiceAccountClient(otherApp); Assert.assertNotNull(linked); Assert.assertEquals("my-service-user", linked.getUsername()); + + assertAuthorizationSettings(realmRsc); } private static boolean isProtocolMapperGranted(Map consent, ProtocolMapperRepresentation mapperRep) { @@ -544,4 +554,89 @@ public class ExportImportUtil { return false; } + private static void assertAuthorizationSettings(RealmResource realmRsc) { + AuthorizationResource authzResource = ApiUtil.findAuthorizationSettings(realmRsc, "test-app-authz"); + + Assert.assertNotNull(authzResource); + + List resources = authzResource.resources().resources(); + Assert.assertEquals(4, resources.size()); + ResourceServerRepresentation authzSettings = authzResource.getSettings(); + List> resourcePredicates = new ArrayList<>(); + resourcePredicates.add(resourceRep -> { + if ("Admin Resource".equals(resourceRep.getName())) { + Assert.assertEquals(authzSettings.getClientId(), resourceRep.getOwner().getId()); + Assert.assertEquals("/protected/admin/*", resourceRep.getUri()); + Assert.assertEquals("http://test-app-authz/protected/admin", resourceRep.getType()); + Assert.assertEquals("http://icons.com/icon-admin", resourceRep.getIconUri()); + Assert.assertEquals(1, resourceRep.getScopes().size()); + return true; + } + return false; + }); + resourcePredicates.add(resourceRep -> { + if ("Protected Resource".equals(resourceRep.getName())) { + Assert.assertEquals(authzSettings.getClientId(), resourceRep.getOwner().getId()); + Assert.assertEquals("/*", resourceRep.getUri()); + Assert.assertEquals("http://test-app-authz/protected/resource", resourceRep.getType()); + Assert.assertEquals("http://icons.com/icon-resource", resourceRep.getIconUri()); + Assert.assertEquals(1, resourceRep.getScopes().size()); + return true; + } + return false; + }); + resourcePredicates.add(resourceRep -> { + if ("Premium Resource".equals(resourceRep.getName())) { + Assert.assertEquals(authzSettings.getClientId(), resourceRep.getOwner().getId()); + Assert.assertEquals("/protected/premium/*", resourceRep.getUri()); + Assert.assertEquals("urn:test-app-authz:protected:resource", resourceRep.getType()); + Assert.assertEquals("http://icons.com/icon-premium", resourceRep.getIconUri()); + Assert.assertEquals(1, resourceRep.getScopes().size()); + return true; + } + return false; + }); + resourcePredicates.add(resourceRep -> { + if ("Main Page".equals(resourceRep.getName())) { + Assert.assertEquals(authzSettings.getClientId(), resourceRep.getOwner().getId()); + Assert.assertNull(resourceRep.getUri()); + Assert.assertEquals("urn:test-app-authz:protected:resource", resourceRep.getType()); + Assert.assertEquals("http://icons.com/icon-main-page", resourceRep.getIconUri()); + Assert.assertEquals(3, resourceRep.getScopes().size()); + return true; + } + return false; + }); + assertPredicate(resources, resourcePredicates); + + List scopes = authzResource.scopes().scopes(); + Assert.assertEquals(6, scopes.size()); + List> scopePredicates = new ArrayList<>(); + scopePredicates.add(scopeRepresentation -> "admin-access".equals(scopeRepresentation.getName())); + scopePredicates.add(scopeRepresentation -> "resource-access".equals(scopeRepresentation.getName())); + scopePredicates.add(scopeRepresentation -> "premium-access".equals(scopeRepresentation.getName())); + scopePredicates.add(scopeRepresentation -> "urn:test-app-authz:page:main:actionForAdmin".equals(scopeRepresentation.getName())); + scopePredicates.add(scopeRepresentation -> "urn:test-app-authz:page:main:actionForUser".equals(scopeRepresentation.getName())); + scopePredicates.add(scopeRepresentation -> "urn:test-app-authz:page:main:actionForPremiumUser".equals(scopeRepresentation.getName())); + assertPredicate(scopes, scopePredicates); + + List policies = authzResource.policies().policies(); + Assert.assertEquals(10, policies.size()); + List> policyPredicates = new ArrayList<>(); + policyPredicates.add(policyRepresentation -> "Any Admin Policy".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "Any User Policy".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "Only Premium User Policy".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "All Users Policy".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "Premium Resource Permission".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "Administrative Resource Permission".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "Protected Resource Permission".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "Action 1 on Main Page Resource Permission".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "Action 2 on Main Page Resource Permission".equals(policyRepresentation.getName())); + policyPredicates.add(policyRepresentation -> "Action 3 on Main Page Resource Permission".equals(policyRepresentation.getName())); + assertPredicate(policies, policyPredicates); + } + + private static void assertPredicate(List source, List> predicate) { + Assert.assertTrue(!source.stream().filter(object -> !predicate.stream().filter(predicate1 -> predicate1.test(object)).findFirst().isPresent()).findAny().isPresent()); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json index 23be71982dc..6cd3ef62563 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json @@ -204,6 +204,168 @@ } } ] + }, + { + "clientId": "test-app-authz", + "enabled": true, + "baseUrl": "/test-app-authz", + "adminUrl": "/test-app-authz", + "bearerOnly": false, + "authorizationSettings": { + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "resources": [ + { + "name": "Admin Resource", + "uri": "/protected/admin/*", + "type": "http://test-app-authz/protected/admin", + "icon_uri" : "http://icons.com/icon-admin", + "scopes": [ + { + "name": "admin-access" + } + ] + }, + { + "name": "Protected Resource", + "uri": "/*", + "type": "http://test-app-authz/protected/resource", + "icon_uri" : "http://icons.com/icon-resource", + "scopes": [ + { + "name": "resource-access" + } + ] + }, + { + "name": "Premium Resource", + "uri": "/protected/premium/*", + "type": "urn:test-app-authz:protected:resource", + "icon_uri" : "http://icons.com/icon-premium", + "scopes": [ + { + "name": "premium-access" + } + ] + }, + { + "name": "Main Page", + "type": "urn:test-app-authz:protected:resource", + "icon_uri" : "http://icons.com/icon-main-page", + "scopes": [ + { + "name": "urn:test-app-authz:page:main:actionForAdmin" + }, + { + "name": "urn:test-app-authz:page:main:actionForUser" + }, + { + "name": "urn:test-app-authz:page:main:actionForPremiumUser" + } + ] + } + ], + "policies": [ + { + "name": "Any Admin Policy", + "description": "Defines that adminsitrators can do something", + "type": "role", + "config": { + "roles": "[{\"id\":\"admin\"}]" + } + }, + { + "name": "Any User Policy", + "description": "Defines that any user can do something", + "type": "role", + "config": { + "roles": "[{\"id\":\"user\"}]" + } + }, + { + "name": "Only Premium User Policy", + "description": "Defines that only premium users can do something", + "type": "role", + "logic": "POSITIVE", + "config": { + "roles": "[{\"id\":\"customer-user-premium\"}]" + } + }, + { + "name": "All Users Policy", + "description": "Defines that all users can do something", + "type": "aggregate", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "applyPolicies": "[\"Any User Policy\",\"Any Admin Policy\",\"Only Premium User Policy\"]" + } + }, + { + "name": "Premium Resource Permission", + "description": "A policy that defines access to premium resources", + "type": "resource", + "decisionStrategy": "UNANIMOUS", + "config": { + "resources": "[\"Premium Resource\"]", + "applyPolicies": "[\"Only Premium User Policy\"]" + } + }, + { + "name": "Administrative Resource Permission", + "description": "A policy that defines access to administrative resources", + "type": "resource", + "decisionStrategy": "UNANIMOUS", + "config": { + "resources": "[\"Admin Resource\"]", + "applyPolicies": "[\"Any Admin Policy\"]" + } + }, + { + "name": "Protected Resource Permission", + "description": "A policy that defines access to any protected resource", + "type": "resource", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "resources": "[\"Protected Resource\"]", + "applyPolicies": "[\"All Users Policy\"]" + } + }, + { + "name": "Action 1 on Main Page Resource Permission", + "description": "A policy that defines access to action 1 on the main page", + "type": "scope", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "scopes": "[\"urn:test-app-authz:page:main:actionForAdmin\"]", + "applyPolicies": "[\"Any Admin Policy\"]" + } + }, + { + "name": "Action 2 on Main Page Resource Permission", + "description": "A policy that defines access to action 2 on the main page", + "type": "scope", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "scopes": "[\"urn:test-app-authz:page:main:actionForUser\"]", + "applyPolicies": "[\"Any User Policy\"]" + } + }, + { + "name": "Action 3 on Main Page Resource Permission", + "description": "A policy that defines access to action 3 on the main page", + "type": "scope", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "scopes": "[\"urn:test-app-authz:page:main:actionForPremiumUser\"]", + "applyPolicies": "[\"Only Premium User Policy\"]" + } + } + ] + }, + "redirectUris": [ + "/test-app-authz/*" + ], + "secret": "secret" } ], "oauthClients" : [ @@ -239,6 +401,13 @@ "realm" : [ { "name": "admin" + }, + { + "name": "user" + }, + { + "name": "customer-user-premium", + "description": "Have User Premium privileges" } ], "application" : { diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json index 11e25d3f6ff..c0b2b6c6dce 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json @@ -117,6 +117,164 @@ "http://localhost:8180/auth/realms/master/app/*" ], "secret": "password" + }, + { + "clientId": "test-app-authz", + "enabled": true, + "baseUrl": "/test-app-authz", + "adminUrl": "/test-app-authz", + "bearerOnly": false, + "authorizationSettings": { + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "resources": [ + { + "name": "Admin Resource", + "uri": "/protected/admin/*", + "type": "http://test-app-authz/protected/admin", + "scopes": [ + { + "name": "admin-access" + } + ] + }, + { + "name": "Protected Resource", + "uri": "/*", + "type": "http://test-app-authz/protected/resource", + "scopes": [ + { + "name": "resource-access" + } + ] + }, + { + "name": "Premium Resource", + "uri": "/protected/premium/*", + "type": "urn:test-app-authz:protected:resource", + "scopes": [ + { + "name": "premium-access" + } + ] + }, + { + "name": "Main Page", + "type": "urn:test-app-authz:protected:resource", + "scopes": [ + { + "name": "urn:test-app-authz:page:main:actionForAdmin" + }, + { + "name": "urn:test-app-authz:page:main:actionForUser" + }, + { + "name": "urn:test-app-authz:page:main:actionForPremiumUser" + } + ] + } + ], + "policies": [ + { + "name": "Any Admin Policy", + "description": "Defines that adminsitrators can do something", + "type": "role", + "config": { + "roles": "[{\"id\":\"admin\"}]" + } + }, + { + "name": "Any User Policy", + "description": "Defines that any user can do something", + "type": "role", + "config": { + "roles": "[{\"id\":\"user\"}]" + } + }, + { + "name": "Only Premium User Policy", + "description": "Defines that only premium users can do something", + "type": "role", + "logic": "POSITIVE", + "config": { + "roles": "[{\"id\":\"customer-user-premium\"}]" + } + }, + { + "name": "All Users Policy", + "description": "Defines that all users can do something", + "type": "aggregate", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "applyPolicies": "[\"Any User Policy\",\"Any Admin Policy\",\"Only Premium User Policy\"]" + } + }, + { + "name": "Premium Resource Permission", + "description": "A policy that defines access to premium resources", + "type": "resource", + "decisionStrategy": "UNANIMOUS", + "config": { + "resources": "[\"Premium Resource\"]", + "applyPolicies": "[\"Only Premium User Policy\"]" + } + }, + { + "name": "Administrative Resource Permission", + "description": "A policy that defines access to administrative resources", + "type": "resource", + "decisionStrategy": "UNANIMOUS", + "config": { + "resources": "[\"Admin Resource\"]", + "applyPolicies": "[\"Any Admin Policy\"]" + } + }, + { + "name": "Protected Resource Permission", + "description": "A policy that defines access to any protected resource", + "type": "resource", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "resources": "[\"Protected Resource\"]", + "applyPolicies": "[\"All Users Policy\"]" + } + }, + { + "name": "Action 1 on Main Page Resource Permission", + "description": "A policy that defines access to action 1 on the main page", + "type": "scope", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "scopes": "[\"urn:test-app-authz:page:main:actionForAdmin\"]", + "applyPolicies": "[\"Any Admin Policy\"]" + } + }, + { + "name": "Action 2 on Main Page Resource Permission", + "description": "A policy that defines access to action 2 on the main page", + "type": "scope", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "scopes": "[\"urn:test-app-authz:page:main:actionForUser\"]", + "applyPolicies": "[\"Any User Policy\"]" + } + }, + { + "name": "Action 3 on Main Page Resource Permission", + "description": "A policy that defines access to action 3 on the main page", + "type": "scope", + "decisionStrategy": "AFFIRMATIVE", + "config": { + "scopes": "[\"urn:test-app-authz:page:main:actionForPremiumUser\"]", + "applyPolicies": "[\"Only Premium User Policy\"]" + } + } + ] + }, + "redirectUris": [ + "/test-app-authz/*" + ], + "secret": "secret" } ], "roles" : { @@ -128,6 +286,10 @@ { "name": "admin", "description": "Have Administrator privileges" + }, + { + "name": "customer-user-premium", + "description": "Have User Premium privileges" } ], "client" : {