From 3c8ed8e3d8463a16767bc8cde3dbb0a577a92b7b Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Fri, 29 Jul 2016 05:18:38 -0300 Subject: [PATCH 1/3] [KEYCLOAK-3372] - Code cleanup --- .../authorization/AbstractPolicyEnforcer.java | 27 +++-- .../KeycloakAdapterPolicyEnforcer.java | 2 +- .../authorization/PolicyEnforcer.java | 2 - .../adapters/config/PolicyEnforcerConfig.java | 7 +- .../authorization/PolicyRepresentation.java | 3 + .../photoz-restful-api-authz-service.json | 5 - .../example/photoz/album/AlbumService.java | 7 +- .../src/main/webapp/WEB-INF/keycloak.json | 13 ++- .../servlet-authz-app-config.json | 6 +- .../authorization/attribute/Attributes.java | 3 +- .../evaluation/DefaultPolicyEvaluator.java | 50 ++++++--- .../admin/PolicyEvaluationService.java | 12 +- .../AuthorizationTokenService.java | 18 ++- .../entitlement/EntitlementService.java | 19 ++-- .../authorization/util/Permissions.java | 105 +++++++++--------- .../example/photoz/album/AlbumService.java | 7 +- .../src/main/webapp/WEB-INF/keycloak.json | 15 ++- .../AbstractPhotozExampleAdapterTest.java | 7 ++ 18 files changed, 179 insertions(+), 129 deletions(-) diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java index 6b1fe19fd8..0c0fc236f9 100644 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java @@ -126,11 +126,13 @@ public abstract class AbstractPolicyEnforcer { List permissions = authorization.getPermissions(); for (Permission permission : permissions) { - Set allowedScopes = permission.getScopes(); - if (permission.getResourceSetId() != null) { if (isResourcePermission(actualPathConfig, permission)) { - if (((allowedScopes == null || allowedScopes.isEmpty()) && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) { + if (actualPathConfig.isInstance() && !matchResourcePermission(actualPathConfig, permission)) { + continue; + + } + if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) { LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions); if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) { this.paths.remove(actualPathConfig); @@ -138,11 +140,6 @@ public abstract class AbstractPolicyEnforcer { return true; } } - } else { - if ((allowedScopes.isEmpty() && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) { - LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions); - return true; - } } } @@ -151,6 +148,11 @@ public abstract class AbstractPolicyEnforcer { return false; } + private boolean hasResourceScopePermission(Set requiredScopes, Permission permission, PathConfig actualPathConfig) { + Set allowedScopes = permission.getScopes(); + return (allowedScopes.containsAll(requiredScopes) || allowedScopes.isEmpty()); + } + protected AuthzClient getAuthzClient() { return this.authzClient; } @@ -210,7 +212,6 @@ public abstract class AbstractPolicyEnforcer { config.setPath(targetResource.getUri()); config.setScopes(originalConfig.getScopes()); config.setMethods(originalConfig.getMethods()); - config.setInstance(true); config.setParentConfig(originalConfig); this.paths.add(config); @@ -244,13 +245,17 @@ public abstract class AbstractPolicyEnforcer { private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) { // first we try a match using resource id - boolean resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getId()); + boolean resourceMatch = matchResourcePermission(actualPathConfig, permission); // as a fallback, check if the current path is an instance and if so, check if parent's id matches the permission if (!resourceMatch && actualPathConfig.isInstance()) { - resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getParentConfig().getId()); + resourceMatch = matchResourcePermission(actualPathConfig.getParentConfig(), permission); } return resourceMatch; } + + private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) { + return permission.getResourceSetId().equals(actualPathConfig.getId()); + } } diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java index e151f7620e..a77676292c 100644 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java @@ -50,7 +50,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer { int retry = 2; AccessToken original = accessToken; - while (retry >= 0) { + while (retry > 0) { if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) { original.setAuthorization(accessToken.getAuthorization()); return true; diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java index 5c2612449d..aa6d3d25d0 100644 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java @@ -45,7 +45,6 @@ public class PolicyEnforcer { private static Logger LOGGER = Logger.getLogger(PolicyEnforcer.class); private final KeycloakDeployment deployment; - private final PathMatcher pathMatcher; private final AuthzClient authzClient; private final PolicyEnforcerConfig enforcerConfig; private final List paths; @@ -54,7 +53,6 @@ public class PolicyEnforcer { this.deployment = deployment; this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig(); this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient())); - this.pathMatcher = new PathMatcher(); this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig); if (LOGGER.isDebugEnabled()) { diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java index b2c5757faa..9cf710ac5d 100644 --- a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java +++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java @@ -109,7 +109,6 @@ public class PolicyEnforcerConfig { private List methods = new ArrayList<>(); private List scopes = Collections.emptyList(); private String id; - private boolean instance; @JsonIgnore private PathConfig parentConfig; @@ -178,11 +177,7 @@ public class PolicyEnforcerConfig { } public boolean isInstance() { - return instance; - } - - public void setInstance(boolean instance) { - this.instance = instance; + return this.parentConfig != null; } public void setParentConfig(PathConfig parentConfig) { diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java index dd219012dc..9a51031782 100644 --- a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java @@ -16,6 +16,8 @@ */ package org.keycloak.representations.idm.authorization; +import com.fasterxml.jackson.annotation.JsonInclude; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -35,6 +37,7 @@ public class PolicyRepresentation { private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS; private Map config = new HashMap(); private List dependentPolicies; + @JsonInclude(JsonInclude.Include.NON_EMPTY) private List associatedPolicies = new ArrayList<>(); public String getId() { diff --git a/examples/authz/photoz/photoz-restful-api-authz-service.json b/examples/authz/photoz/photoz-restful-api-authz-service.json index 6547d2fcc4..455948c493 100644 --- a/examples/authz/photoz/photoz-restful-api-authz-service.json +++ b/examples/authz/photoz/photoz-restful-api-authz-service.json @@ -52,7 +52,6 @@ "sessionName": "MainOwnerSession", "mavenArtifactGroupId": "org.keycloak", "moduleName": "PhotozAuthzOwnerPolicy", - "applyPolicies": "[]", "scannerPeriod": "1", "scannerPeriodUnit": "Hours" } @@ -64,7 +63,6 @@ "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "config": { - "applyPolicies": "[]", "roles": "[{\"id\":\"admin\",\"required\":true}]" } }, @@ -75,7 +73,6 @@ "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "config": { - "applyPolicies": "[]", "roles": "[{\"id\":\"user\"},{\"id\":\"manage-albums\",\"required\":true}]" } }, @@ -86,7 +83,6 @@ "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "config": { - "applyPolicies": "[]", "code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {\n $evaluation.grant();\n}" } }, @@ -117,7 +113,6 @@ "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "config": { - "applyPolicies": "[]", "code": "var context = $evaluation.getContext();\nvar identity = context.getIdentity();\nvar attributes = identity.getAttributes();\nvar email = attributes.getValue('email').asString(0);\n\nif (identity.hasRole('admin') || email.endsWith('@keycloak.org')) {\n $evaluation.grant();\n}" } }, diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java index c28bca3bc0..a5d7f161b4 100644 --- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java +++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java @@ -109,7 +109,12 @@ public class AlbumService { private void createProtectedResource(Album album) { try { - ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), new HashSet(), "/album/" + album.getId(), "http://photoz.com/album"); + HashSet scopes = new HashSet<>(); + + scopes.add(new ScopeRepresentation(SCOPE_ALBUM_VIEW)); + scopes.add(new ScopeRepresentation(SCOPE_ALBUM_DELETE)); + + ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), scopes, "/album/" + album.getId(), "http://photoz.com/album"); albumResource.setOwner(album.getUserId()); diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json index 95fb58bf70..6849d0771b 100644 --- a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json +++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json @@ -9,17 +9,18 @@ "secret": "secret" }, "policy-enforcer": { + "user-managed-access" : {}, "paths": [ { "path" : "/album/*", "methods" : [ - { - "method": "GET", - "scopes" : ["urn:photoz.com:scopes:album:view"] - }, { "method": "POST", "scopes" : ["urn:photoz.com:scopes:album:create"] + }, + { + "method": "GET", + "scopes" : ["urn:photoz.com:scopes:album:view"] } ] }, @@ -30,6 +31,10 @@ { "method": "DELETE", "scopes" : ["urn:photoz.com:scopes:album:delete"] + }, + { + "method": "GET", + "scopes" : ["urn:photoz.com:scopes:album:view"] } ] }, diff --git a/examples/authz/servlet-authz/servlet-authz-app-config.json b/examples/authz/servlet-authz/servlet-authz-app-config.json index d5fb1cb811..43ebde42b8 100644 --- a/examples/authz/servlet-authz/servlet-authz-app-config.json +++ b/examples/authz/servlet-authz/servlet-authz-app-config.json @@ -54,7 +54,7 @@ "description": "Defines that adminsitrators can do something", "type": "role", "config": { - "roles": "[\"admin\"]" + "roles": "[{\"id\":\"admin\"}]" } }, { @@ -62,7 +62,7 @@ "description": "Defines that any user can do something", "type": "role", "config": { - "roles": "[\"user\"]" + "roles": "[{\"id\":\"user\"}]" } }, { @@ -71,7 +71,7 @@ "type": "role", "logic": "POSITIVE", "config": { - "roles": "[\"user_premium\"]" + "roles": "[{\"id\":\"user_premium\"}]" } }, { diff --git a/server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java b/server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java index a70d3c075e..a0a7b6c0d1 100644 --- a/server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java +++ b/server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java @@ -67,7 +67,8 @@ public interface Attributes { * @return true if any attribute with name and value exist. Otherwise, returns false. */ default boolean containsValue(String name, String value) { - return toMap().getOrDefault(name, emptyList()).stream().anyMatch(value::equals); + Collection values = toMap().get(name); + return values != null && values.stream().anyMatch(value::equals); } /** diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java index e2ef2f96d1..d5fa9cc9fa 100644 --- a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java +++ b/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java @@ -32,8 +32,10 @@ import org.keycloak.authorization.store.StoreFactory; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -130,27 +132,49 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator { return true; } - if (policy.getScopes().isEmpty()) { - return true; - } + Set scopes = new HashSet<>(policy.getScopes()); - boolean hasScope = true; + if (scopes.isEmpty()) { + Set resources = new HashSet<>(); - for (Scope givenScope : policy.getScopes()) { - boolean hasGivenScope = false; + resources.addAll(policy.getResources()); - for (Scope scope : permission.getScopes()) { - if (givenScope.getId().equals(scope.getId())) { - hasGivenScope = true; - break; + for (Resource resource : resources) { + scopes.addAll(resource.getScopes()); + } + + if (!resources.isEmpty() && scopes.isEmpty()) { + return false; + } + + if (scopes.isEmpty()) { + Resource resource = permission.getResource(); + String type = resource.getType(); + + if (type != null) { + List resourcesByType = authorization.getStoreFactory().getResourceStore().findByType(type); + + for (Resource resourceType : resourcesByType) { + if (resourceType.getOwner().equals(resource.getResourceServer().getClientId())) { + resources.add(resourceType); + } + } } } - if (!hasGivenScope) { - return false; + for (Resource resource : resources) { + scopes.addAll(resource.getScopes()); } } - return hasScope; + for (Scope givenScope : scopes) { + for (Scope scope : permission.getScopes()) { + if (givenScope.getId().equals(scope.getId())) { + return true; + } + } + } + + return false; } } 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 8e4ec5e318..6712a9dac3 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java @@ -240,10 +240,16 @@ public class PolicyEvaluationService { } } - accessToken.addAccess(clientModel.getClientId()); - AccessToken.Access resourceAccess = accessToken.getResourceAccess(clientModel.getClientId()); + AccessToken.Access clientAccess = accessToken.addAccess(clientModel.getClientId()); + clientAccess.roles(new HashSet<>()); - userModel.getClientRoleMappings(clientModel).stream().map(RoleModel::getName).forEach(roleName -> resourceAccess.addRole(roleName)); + userModel.getClientRoleMappings(clientModel).stream().map(RoleModel::getName).forEach(roleName -> clientAccess.addRole(roleName)); + + ClientModel resourceServerClient = realm.getClientById(resourceServer.getClientId()); + AccessToken.Access resourceServerAccess = accessToken.addAccess(resourceServerClient.getClientId()); + resourceServerAccess.roles(new HashSet<>()); + + userModel.getClientRoleMappings(resourceServerClient).stream().map(RoleModel::getName).forEach(roleName -> resourceServerAccess.addRole(roleName)); } } } diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java index 405675a544..0fd347488d 100644 --- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java +++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java @@ -17,6 +17,7 @@ package org.keycloak.authorization.authorization; import org.jboss.resteasy.spi.HttpRequest; +import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.authorization.representation.AuthorizationRequest; @@ -50,8 +51,6 @@ import javax.ws.rs.container.Suspended; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -104,8 +103,12 @@ public class AuthorizationTokenService { List entitlements = Permissions.allPermits(results); if (entitlements.isEmpty()) { + HashMap error = new HashMap<>(); + + error.put(OAuth2Constants.ERROR, "not_authorized"); + asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.FORBIDDEN) - .entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN))) + .entity(error)) .allowedOrigins(identity.getAccessToken()) .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build()); } else { @@ -193,14 +196,7 @@ public class AuthorizationTokenService { return permissionsToEvaluate.entrySet().stream() .flatMap((Function>, Stream>) entry -> { Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey()); - if (entry.getValue().isEmpty()) { - return Arrays.asList(new ResourcePermission(entryResource, Collections.emptyList(), entryResource.getResourceServer())).stream(); - } else { - return entry.getValue().stream() - .map(scopeName -> storeFactory.getScopeStore().findByName(scopeName, entryResource.getResourceServer().getId())) - .filter(scope -> scope != null) - .map(scope -> new ResourcePermission(entryResource, Arrays.asList(scope), entryResource.getResourceServer())); - } + return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream(); }).collect(Collectors.toList()); } diff --git a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java index ccc457d6b9..e5986b78ae 100644 --- a/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java +++ b/services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java @@ -18,6 +18,7 @@ package org.keycloak.authorization.entitlement; import org.jboss.resteasy.spi.HttpRequest; +import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.common.KeycloakEvaluationContext; @@ -55,8 +56,6 @@ import javax.ws.rs.container.Suspended; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -119,8 +118,12 @@ public class EntitlementService { List entitlements = Permissions.allPermits(results); if (entitlements.isEmpty()) { + HashMap error = new HashMap<>(); + + error.put(OAuth2Constants.ERROR, "not_authorized"); + asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN) - .entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN))) + .entity(error)) .allowedOrigins(identity.getAccessToken()) .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build()); } else { @@ -249,15 +252,7 @@ public class EntitlementService { return permissionsToEvaluate.entrySet().stream() .flatMap((Function>, Stream>) entry -> { Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey()); - - if (entry.getValue().isEmpty()) { - return Arrays.asList(new ResourcePermission(entryResource, Collections.emptyList(), entryResource.getResourceServer())).stream(); - } else { - return entry.getValue().stream() - .map(scopeName -> storeFactory.getScopeStore().findByName(scopeName, entryResource.getResourceServer().getId())) - .filter(scope -> scope != null) - .map(scope -> new ResourcePermission(entryResource, Arrays.asList(scope), entryResource.getResourceServer())); - } + return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream(); }).collect(Collectors.toList()); } } diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java index ed49697d60..ebc57c2951 100644 --- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java +++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java @@ -27,6 +27,7 @@ import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.permission.ResourcePermission; import org.keycloak.authorization.policy.evaluation.Result; import org.keycloak.authorization.store.ResourceStore; +import org.keycloak.authorization.store.ScopeStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.representations.idm.authorization.Permission; @@ -61,91 +62,95 @@ public final class Permissions { StoreFactory storeFactory = authorization.getStoreFactory(); ResourceStore resourceStore = storeFactory.getResourceStore(); - resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resourceServer, authorization))); - resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resourceServer, authorization))); + resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()), authorization))); + resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()), authorization))); return permissions; } - public static List createResourcePermissions(Resource resource, ResourceServer resourceServer, AuthorizationProvider authorization) { + public static List createResourcePermissions(Resource resource, Set requestedScopes, AuthorizationProvider authorization) { List permissions = new ArrayList<>(); - List scopes = resource.getScopes(); - - if (scopes.isEmpty()) { - String type = resource.getType(); + String type = resource.getType(); + ResourceServer resourceServer = resource.getResourceServer(); + List scopes; + if (requestedScopes.isEmpty()) { + scopes = resource.getScopes(); // check if there is a typed resource whose scopes are inherited by the resource being requested. In this case, we assume that parent resource // is owned by the resource server itself - if (type != null) { + if (type != null && !resource.getOwner().equals(resourceServer.getClientId())) { StoreFactory storeFactory = authorization.getStoreFactory(); ResourceStore resourceStore = storeFactory.getResourceStore(); resourceStore.findByType(type).forEach(resource1 -> { if (resource1.getOwner().equals(resourceServer.getClientId())) { - scopes.addAll(resource1.getScopes()); + for (Scope typeScope : resource1.getScopes()) { + if (!scopes.contains(typeScope)) { + scopes.add(typeScope); + } + } } }); } + } else { + ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore(); + scopes = requestedScopes.stream().map(scopeName -> { + Scope byName = scopeStore.findByName(scopeName, resource.getResourceServer().getId()); + return byName; + }).collect(Collectors.toList()); } - if (scopes.size() > 1) { + if (scopes.isEmpty()) { + permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer())); + } else { for (Scope scope : scopes) { permissions.add(new ResourcePermission(resource, Arrays.asList(scope), resource.getResourceServer())); } - } else { - permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer())); } return permissions; } public static List allPermits(List evaluation) { - List permissions = evaluation.stream() - .filter(evaluationResult -> evaluationResult.getEffect().equals(Effect.PERMIT)) - .map(evaluationResult -> { - ResourcePermission permission = evaluationResult.getPermission(); - String resourceId = null; - String resourceName = null; + Map permissions = new HashMap<>(); - Resource resource = permission.getResource(); - - if (resource != null) { - resourceId = resource.getId(); - resourceName = resource.getName(); - } - - Set scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()); - - return new Permission(resourceId, resourceName, scopes); - }).collect(Collectors.toList()); - - Map perms = new HashMap<>(); - - permissions.forEach(permission -> { - Permission evalPermission = perms.get(permission.getResourceSetId()); - - if (evalPermission == null) { - evalPermission = permission; - perms.put(permission.getResourceSetId(), evalPermission); + for (Result evaluationResult : evaluation) { + ResourcePermission permission = evaluationResult.getPermission(); + Set scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()); + if (evaluationResult.getEffect().equals(Effect.DENY)) { + continue; } + Resource resource = permission.getResource(); - Set permissionScopes = permission.getScopes(); + if (resource != null) { + String resourceId = resource.getId(); + String resourceName = resource.getName(); + Permission evalPermission = permissions.get(resource.getId()); - if (permissionScopes != null && !permissionScopes.isEmpty()) { - Set scopes = evalPermission.getScopes(); - - if (scopes == null) { - scopes = new HashSet(); - evalPermission.setScopes(scopes); + if (evalPermission == null) { + evalPermission = new Permission(resourceId, resourceName, scopes); + permissions.put(resourceId, evalPermission); } - for (String scopeName : permissionScopes) { - if (!scopes.contains(scopeName)) { - scopes.add(scopeName); + if (scopes != null && !scopes.isEmpty()) { + Set finalScopes = evalPermission.getScopes(); + + if (finalScopes == null) { + finalScopes = new HashSet(); + evalPermission.setScopes(finalScopes); + } + + for (String scopeName : scopes) { + if (!finalScopes.contains(scopeName)) { + finalScopes.add(scopeName); + } } } + } else { + Permission scopePermission = new Permission(null, null, scopes); + permissions.put(scopePermission.toString(), scopePermission); } - }); + } - return perms.values().stream().collect(Collectors.toList()); + return permissions.values().stream().collect(Collectors.toList()); } } diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java index c28bca3bc0..a5d7f161b4 100644 --- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java +++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java @@ -109,7 +109,12 @@ public class AlbumService { private void createProtectedResource(Album album) { try { - ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), new HashSet(), "/album/" + album.getId(), "http://photoz.com/album"); + HashSet scopes = new HashSet<>(); + + scopes.add(new ScopeRepresentation(SCOPE_ALBUM_VIEW)); + scopes.add(new ScopeRepresentation(SCOPE_ALBUM_DELETE)); + + ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), scopes, "/album/" + album.getId(), "http://photoz.com/album"); albumResource.setOwner(album.getUserId()); diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json index 95fb58bf70..a3ac697cb3 100644 --- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json +++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json @@ -1,7 +1,7 @@ { "realm": "photoz", "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", - "auth-server-url": "http://localhost:8080/auth", + "auth-server-url": "http://localhost:8180/auth", "ssl-required": "external", "resource": "photoz-restful-api", "bearer-only" : true, @@ -9,17 +9,18 @@ "secret": "secret" }, "policy-enforcer": { + "user-managed-access" : {}, "paths": [ { "path" : "/album/*", "methods" : [ - { - "method": "GET", - "scopes" : ["urn:photoz.com:scopes:album:view"] - }, { "method": "POST", "scopes" : ["urn:photoz.com:scopes:album:create"] + }, + { + "method": "GET", + "scopes" : ["urn:photoz.com:scopes:album:view"] } ] }, @@ -30,6 +31,10 @@ { "method": "DELETE", "scopes" : ["urn:photoz.com:scopes:album:delete"] + }, + { + "method": "GET", + "scopes" : ["urn:photoz.com:scopes:album:view"] } ] }, diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java index 8a8d483a24..28662fa573 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java @@ -94,6 +94,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd importResourceServerSettings(); } + @Test public void testCreateDeleteAlbum() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); @@ -115,6 +116,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } } + @Test public void testOnlyOwnerCanDeleteAlbum() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); @@ -160,6 +162,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } } + @Test public void testRegularUserCanNotAccessAdminResources() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); @@ -172,6 +175,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } } + @Test public void testAdminOnlyFromSpecificAddress() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); @@ -264,6 +268,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } } + @Test public void testAdminWithoutPermissionsToDeleteScopePermission() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); @@ -327,6 +332,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } } + @Test public void testClientRoleRepresentingUserConsent() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); @@ -366,6 +372,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } } + @Test public void testClientRoleNotRequired() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); From 607b305c2f5a8cc620fc3a1f3f18161c22c786a6 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Fri, 29 Jul 2016 12:42:53 -0300 Subject: [PATCH 2/3] [KEYCLOAK-3372] - Adding tests for servlet authorization --- .../src/main/webapp/logout-include.jsp | 4 +- .../integration-arquillian/test-apps/pom.xml | 1 + .../test-apps/servlet-authz/README.md | 54 +++ .../test-apps/servlet-authz/pom.xml | 53 +++ .../servlet-authz-app-authz-service.json | 147 ++++++++ .../servlet-authz/servlet-authz-realm.json | 95 +++++ .../META-INF/jboss-deployment-structure.xml | 25 ++ .../src/main/webapp/WEB-INF/keycloak.json | 14 + .../src/main/webapp/WEB-INF/web.xml | 47 +++ .../src/main/webapp/accessDenied.jsp | 6 + .../servlet-authz/src/main/webapp/index.jsp | 35 ++ .../src/main/webapp/logout-include.jsp | 11 + .../main/webapp/protected/admin/onlyAdmin.jsp | 6 + .../src/main/webapp/protected/dynamicMenu.jsp | 48 +++ .../webapp/protected/premium/onlyPremium.jsp | 6 + .../test-apps/test-apps-dist/build.xml | 8 + .../AbstractServletAuthzAdapterTest.java | 352 ++++++++++++++++++ .../WildflyServletAuthzAdapterTest.java | 33 ++ .../tests/other/adapters/pom.xml | 6 + 19 files changed, 949 insertions(+), 2 deletions(-) create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/README.md create mode 100755 testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-realm.json create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/META-INF/jboss-deployment-structure.xml create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/WEB-INF/keycloak.json create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/WEB-INF/web.xml create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/accessDenied.jsp create mode 100755 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/index.jsp create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/logout-include.jsp create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp create mode 100644 testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java create mode 100644 testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyServletAuthzAdapterTest.java diff --git a/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp b/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp index 95365ea195..364d8877ab 100644 --- a/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp +++ b/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp @@ -7,5 +7,5 @@ String contextPath = request.getContextPath(); String redirectUri = scheme + "://" + host + ":" + port + contextPath; %> -

Click ">here to logout.

\ No newline at end of file +

Click here ">Sign Out

\ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml index 391e0d8206..fa4b0c19ab 100644 --- a/testsuite/integration-arquillian/test-apps/pom.xml +++ b/testsuite/integration-arquillian/test-apps/pom.xml @@ -20,5 +20,6 @@ js-database photoz hello-world-authz-service + servlet-authz \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/README.md b/testsuite/integration-arquillian/test-apps/servlet-authz/README.md new file mode 100644 index 0000000000..f93acb52ca --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/README.md @@ -0,0 +1,54 @@ +# About the Example Application + +This is a simple Servlet-based application that will introduce you to some of the main concepts around Keycloak Authorization Services. + +For this application, users can be regular users, premium users or administrators, where: + +* Regular users have very limited access. +* Premium users have access to the *premium area* +* Administrators have access to the *administration area* + +In Keycloak, all the paths being protected are resources on the server. + +This application will also show you how to create a dynamic menu with the permissions granted to an user. + +## Create the Example Realm and a Resource Server + +Considering that your Keycloak Server is up and running, log in to the Keycloak Administration Console. + +Now, create a new realm based on the following configuration file: + + examples/authz/servlet-authz/servlet-authz-realm.json + +That will import a pre-configured realm with everything you need to run this example. For more details about how to import a realm +into Keycloak, check the Keycloak's reference documentation. + +After importing that file, you'll have a new realm called ``servlet-authz``. + +Now, let's import another configuration using the Administration Console in order to configure the client application ``servlet-authz-app`` as a resource server with all resources, scopes, permissions and policies. + +Click on ``Clients`` on the left side menu. Click on the ``servlet-authz-app`` on the client listing page. This will +open the ``Client Details`` page. Once there, click on the `Authorization` tab. + +Click on the ``Select file`` button, which means you want to import a resource server configuration. Now select the file that is located at: + + examples/authz/servlet-authz/servlet-authz-app-config.json + +Now click ``Upload`` and the resource server will be updated accordingly. + +## Deploy and Run the Example Applications + +To deploy the example application, follow these steps: + + cd examples/authz/servlet-authz + mvn clean package wildfly:deploy + +Now, try to access the client application using the following URL: + + http://localhost:8080/servlet-authz-app + +If everything is correct, you will be redirect to Keycloak login page. You can login to the application with the following credentials: + +* username: jdoe / password: jdoe +* username: alice / password: alice +* username: admin / password: admin \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml new file mode 100755 index 0000000000..fc9f6b0641 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + + org.keycloak.testsuite + integration-arquillian-test-apps + 2.1.0-SNAPSHOT + + + servlet-authz-app + war + + Keycloak Authz: Servlet Authorization Test + Servlet Authorization Test + + + + + org.keycloak + keycloak-authz-client + ${project.version} + provided + + + org.keycloak + keycloak-core + ${project.version} + provided + + + + + ${project.artifactId} + + + org.jboss.as.plugins + jboss-as-maven-plugin + + false + + + + org.wildfly.plugins + wildfly-maven-plugin + + false + + + + + diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json b/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json new file mode 100644 index 0000000000..43ebde42b8 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-app-authz-service.json @@ -0,0 +1,147 @@ +{ + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "resources": [ + { + "name": "Admin Resource", + "uri": "/protected/admin/*", + "type": "http://servlet-authz/protected/admin", + "scopes": [ + { + "name": "urn:servlet-authz:protected:admin:access" + } + ] + }, + { + "name": "Protected Resource", + "uri": "/*", + "type": "http://servlet-authz/protected/resource", + "scopes": [ + { + "name": "urn:servlet-authz:protected:resource:access" + } + ] + }, + { + "name": "Premium Resource", + "uri": "/protected/premium/*", + "type": "urn:servlet-authz:protected:resource", + "scopes": [ + { + "name": "urn:servlet-authz:protected:premium:access" + } + ] + }, + { + "name": "Main Page", + "type": "urn:servlet-authz:protected:resource", + "scopes": [ + { + "name": "urn:servlet-authz:page:main:actionForAdmin" + }, + { + "name": "urn:servlet-authz:page:main:actionForUser" + }, + { + "name": "urn:servlet-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\":\"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:servlet-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:servlet-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:servlet-authz:page:main:actionForPremiumUser\"]", + "applyPolicies": "[\"Only Premium User Policy\"]" + } + } + ] +} \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-realm.json b/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-realm.json new file mode 100644 index 0000000000..371e4510f5 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/servlet-authz-realm.json @@ -0,0 +1,95 @@ +{ + "realm": "servlet-authz", + "enabled": true, + "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", + "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "requiredCredentials": [ + "password" + ], + "users": [ + { + "username": "alice", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "alice" + } + ], + "realmRoles": [ + "user" + ] + }, + { + "username": "jdoe", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "jdoe" + } + ], + "realmRoles": [ + "user", + "user_premium" + ] + }, + { + "username": "admin", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "admin" + } + ], + "realmRoles": [ + "user", + "admin" + ], + "clientRoles": { + "realm-management": [ + "realm-admin" + ] + } + }, + { + "username": "service-account-servlet-authz-app", + "enabled": true, + "serviceAccountClientId": "servlet-authz-app", + "clientRoles": { + "servlet-authz-app" : ["uma_protection"] + } + } + ], + "roles": { + "realm": [ + { + "name": "user", + "description": "User privileges" + }, + { + "name": "admin", + "description": "Administrator privileges" + }, + { + "name": "user_premium", + "description": "User Premium privileges" + } + ] + }, + "clients": [ + { + "clientId": "servlet-authz-app", + "enabled": true, + "baseUrl": "/servlet-authz-app", + "adminUrl": "/servlet-authz-app", + "bearerOnly": false, + "authorizationServicesEnabled": true, + "redirectUris": [ + "/servlet-authz-app/*" + ], + "secret": "secret" + } + ] +} diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/META-INF/jboss-deployment-structure.xml b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/META-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000000..515ffa5c73 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/META-INF/jboss-deployment-structure.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/WEB-INF/keycloak.json new file mode 100644 index 0000000000..7b362a782f --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/WEB-INF/keycloak.json @@ -0,0 +1,14 @@ +{ + "realm": "servlet-authz", + "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "auth-server-url" : "http://localhost:8180/auth", + "ssl-required" : "external", + "resource" : "servlet-authz-app", + "public-client" : false, + "credentials": { + "secret": "secret" + }, + "policy-enforcer": { + "on-deny-redirect-to" : "/servlet-authz-app/accessDenied.jsp" + } +} \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/WEB-INF/web.xml b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..14d0615978 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,47 @@ + + + + servlet-authz-app + + + + All Resources + /* + + + user + + + + + + All Resources + /* + + + admin + + + + + KEYCLOAK + servlet-authz + + + + admin + + + + user + + + + 403 + /accessDenied.jsp + + + diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/accessDenied.jsp b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/accessDenied.jsp new file mode 100644 index 0000000000..6f25023af0 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/accessDenied.jsp @@ -0,0 +1,6 @@ + + +

You can not access this resource.

+ <%@include file="logout-include.jsp"%> + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/index.jsp b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/index.jsp new file mode 100755 index 0000000000..3fbfca269c --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/index.jsp @@ -0,0 +1,35 @@ +<%@page import="org.keycloak.AuthorizationContext" %> +<%@ page import="org.keycloak.KeycloakSecurityContext" %> +<%@ page import="org.keycloak.representations.idm.authorization.Permission" %> + +<% + KeycloakSecurityContext keycloakSecurityContext = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); + AuthorizationContext authzContext = keycloakSecurityContext.getAuthorizationContext(); +%> + + + + <%@include file="logout-include.jsp"%> +

This is a public resource. Try to access one of these protected resources:

+ +

Dynamic Menu

+

User Premium

+

Administration

+ +

Your permissions are:

+ +
    + <% + for (Permission permission : authzContext.getPermissions()) { + %> +
  • +

    Resource: <%= permission.getResourceSetName() %>

    +

    ID: <%= permission.getResourceSetId() %>

    +

    Scopes: <%= permission.getScopes() %>

    +
  • + <% + } + %> +
+ + diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/logout-include.jsp b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/logout-include.jsp new file mode 100644 index 0000000000..21ef2edebf --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/logout-include.jsp @@ -0,0 +1,11 @@ +<%@ page import="org.keycloak.common.util.KeycloakUriBuilder" %> +<%@ page import="org.keycloak.constants.ServiceUrlConstants" %> +<% + String scheme = request.getScheme(); + String host = request.getServerName(); + int port = request.getServerPort(); + String contextPath = request.getContextPath(); + String redirectUri = scheme + "://" + host + ":" + port + contextPath; +%> +

Click here ">Sign Out

\ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp new file mode 100644 index 0000000000..5946cd660c --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/admin/onlyAdmin.jsp @@ -0,0 +1,6 @@ + + +

Only Administrators can access this page.

+ <%@include file="../../logout-include.jsp"%> + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp new file mode 100644 index 0000000000..1473d223f3 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/dynamicMenu.jsp @@ -0,0 +1,48 @@ +<%@page import="org.keycloak.AuthorizationContext" %> +<%@ page import="org.keycloak.KeycloakSecurityContext" %> + +<% + KeycloakSecurityContext keycloakSecurityContext = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); + AuthorizationContext authzContext = keycloakSecurityContext.getAuthorizationContext(); +%> + + + +

Any authenticated user can access this page.

+<%@include file="../logout-include.jsp"%> + +

Here is a dynamic menu built from the permissions returned by the server:

+ +
    + <% + if (authzContext.hasResourcePermission("Protected Resource")) { + %> +
  • + Do user thing +
  • + <% + } + %> + + <% + if (authzContext.hasResourcePermission("Premium Resource")) { + %> +
  • + Do user premium thing +
  • + <% + } + %> + + <% + if (authzContext.hasPermission("Admin Resource", "urn:servlet-authz:protected:admin:access")) { + %> +
  • + Do administration thing +
  • + <% + } + %> +
+ + \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp new file mode 100644 index 0000000000..9244f9ca5e --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/protected/premium/onlyPremium.jsp @@ -0,0 +1,6 @@ + + +

Only for premium users.

+<%@include file="../../logout-include.jsp"%> + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml index 9621ae53ef..3e905447b0 100755 --- a/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml +++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/build.xml @@ -43,5 +43,13 @@ + + + + + + + + diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java new file mode 100644 index 0000000000..2753c5e5bd --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java @@ -0,0 +1,352 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.adapter.example.authorization; + +import org.jboss.arquillian.container.test.api.Deployer; +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.admin.client.resource.RealmResource; +import org.keycloak.admin.client.resource.RoleResource; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; +import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest; +import org.keycloak.testsuite.util.WaitUtils; +import org.keycloak.util.JsonSerialization; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.keycloak.testsuite.util.IOUtil.loadJson; +import static org.keycloak.testsuite.util.IOUtil.loadRealm; +import static org.keycloak.testsuite.util.WaitUtils.pause; + +/** + * @author Pedro Igor + */ +public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAdapterTest { + + private static final String REALM_NAME = "servlet-authz"; + private static final String RESOURCE_SERVER_ID = "servlet-authz-app"; + + @ArquillianResource + private Deployer deployer; + + @Override + public void addAdapterTestRealms(List testRealms) { + testRealms.add( + loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-realm.json"))); + } + + @Deployment(name = RESOURCE_SERVER_ID, managed = false) + public static WebArchive deployment() throws IOException { + return exampleDeployment(RESOURCE_SERVER_ID); + } + + @Override + public void beforeAbstractKeycloakTest() throws Exception { + super.beforeAbstractKeycloakTest(); + importResourceServerSettings(); + } + + @Test + public void testUserPermissions() throws Exception { + try { + this.deployer.deploy(RESOURCE_SERVER_ID); + + login("alice", "alice"); + + assertFalse(wasDenied()); + + assertTrue(hasLink("User Premium")); + assertTrue(hasLink("Administration")); + assertTrue(hasText("urn:servlet-authz:page:main:actionForUser")); + assertFalse(hasText("urn:servlet-authz:page:main:actionForAdmin")); + assertFalse(hasText("urn:servlet-authz:page:main:actionForPremiumUser")); + + navigateToDynamicMenuPage(); + + assertTrue(hasText("Do user thing")); + assertFalse(hasText("Do user premium thing")); + assertFalse(hasText("Do administration thing")); + + + navigateToUserPremiumPage(); + + assertTrue(wasDenied()); + + navigateToAdminPage(); + + assertTrue(wasDenied()); + } finally { + this.deployer.undeploy(RESOURCE_SERVER_ID); + } + } + + @Test + public void testUserPremiumPermissions() throws Exception { + try { + this.deployer.deploy(RESOURCE_SERVER_ID); + + login("jdoe", "jdoe"); + + assertFalse(wasDenied()); + assertTrue(hasLink("User Premium")); + assertTrue(hasLink("Administration")); + assertTrue(hasText("urn:servlet-authz:page:main:actionForUser")); + assertTrue(hasText("urn:servlet-authz:page:main:actionForPremiumUser")); + assertFalse(hasText("urn:servlet-authz:page:main:actionForAdmin")); + + navigateToDynamicMenuPage(); + + assertTrue(hasText("Do user thing")); + assertTrue(hasText("Do user premium thing")); + assertFalse(hasText("Do administration thing")); + } finally { + this.deployer.undeploy(RESOURCE_SERVER_ID); + } + } + + @Test + public void testAdminPermissions() throws Exception { + try { + this.deployer.deploy(RESOURCE_SERVER_ID); + + login("admin", "admin"); + + assertFalse(wasDenied()); + + assertTrue(hasLink("User Premium")); + assertTrue(hasLink("Administration")); + assertTrue(hasText("urn:servlet-authz:page:main:actionForUser")); + assertTrue(hasText("urn:servlet-authz:page:main:actionForAdmin")); + assertFalse(hasText("urn:servlet-authz:page:main:actionForPremiumUser")); + + navigateToDynamicMenuPage(); + + assertTrue(hasText("Do user thing")); + assertTrue(hasText("Do administration thing")); + assertFalse(hasText("Do user premium thing")); + } finally { + this.deployer.undeploy(RESOURCE_SERVER_ID); + } + } + + @Test + public void testGrantPremiumAccess() throws Exception { + try { + this.deployer.deploy(RESOURCE_SERVER_ID); + + login("alice", "alice"); + + assertFalse(wasDenied()); + + navigateToUserPremiumPage(); + + assertTrue(wasDenied()); + + for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { + if ("Premium Resource Permission".equals(policy.getName())) { + policy.getConfig().put("applyPolicies", "[\"Any User Policy\"]"); + getAuthorizationResource().policies().policy(policy.getId()).update(policy); + } + } + + login("alice", "alice"); + + navigateToUserPremiumPage(); + + assertFalse(wasDenied()); + + for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { + if ("Premium Resource Permission".equals(policy.getName())) { + policy.getConfig().put("applyPolicies", "[\"Only Premium User Policy\"]"); + getAuthorizationResource().policies().policy(policy.getId()).update(policy); + } + } + + login("alice", "alice"); + navigateToUserPremiumPage(); + + assertTrue(wasDenied()); + + PolicyRepresentation onlyAlicePolicy = new PolicyRepresentation(); + + onlyAlicePolicy.setName("Temporary Premium Access Policy"); + onlyAlicePolicy.setType("user"); + HashMap config = new HashMap<>(); + UsersResource usersResource = realmsResouce().realm(REALM_NAME).users(); + List users = usersResource.search("alice", null, null, null, null, null); + + assertFalse(users.isEmpty()); + + config.put("users", JsonSerialization.writeValueAsString(Arrays.asList(users.get(0).getId()))); + + onlyAlicePolicy.setConfig(config); + getAuthorizationResource().policies().create(onlyAlicePolicy); + + for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { + if ("Premium Resource Permission".equals(policy.getName())) { + policy.getConfig().put("applyPolicies", "[\"Temporary Premium Access Policy\"]"); + getAuthorizationResource().policies().policy(policy.getId()).update(policy); + } + } + + logOut(); + login("alice", "alice"); + navigateToUserPremiumPage(); + + assertFalse(wasDenied()); + } finally { + this.deployer.undeploy(RESOURCE_SERVER_ID); + } + } + + @Test + public void testGrantAdministrativePermissions() throws Exception { + try { + this.deployer.deploy(RESOURCE_SERVER_ID); + + login("jdoe", "jdoe"); + + navigateToAdminPage(); + assertTrue(wasDenied()); + + RealmResource realmResource = realmsResouce().realm(REALM_NAME); + UsersResource usersResource = realmResource.users(); + List users = usersResource.search("jdoe", null, null, null, null, null); + + assertFalse(users.isEmpty()); + + UserResource userResource = usersResource.get(users.get(0).getId()); + + RoleRepresentation adminRole = realmResource.roles().get("admin").toRepresentation(); + userResource.roles().realmLevel().add(Arrays.asList(adminRole)); + + login("jdoe", "jdoe"); + + navigateToAdminPage(); + assertFalse(wasDenied()); + } finally { + this.deployer.undeploy(RESOURCE_SERVER_ID); + } + } + + private boolean hasLink(String text) { + return getLink(text) != null; + } + + private boolean hasText(String text) { + return this.driver.getPageSource().contains(text); + } + + private WebElement getLink(String text) { + return this.driver.findElement(By.xpath("//a[text() = '" + text + "']")); + } + + private void importResourceServerSettings() throws FileNotFoundException { + getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-app-authz-service.json")), ResourceServerRepresentation.class)); + } + + 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()); + } + + private void logOut() { + navigateTo(); + By by = By.xpath("//a[text() = 'Sign Out']"); + WaitUtils.waitUntilElement(by); + this.driver.findElement(by).click(); + pause(500); + } + + private void login(String username, String password) throws InterruptedException { + navigateTo(); + Thread.sleep(2000); + if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) { + Thread.sleep(2000); + logOut(); + navigateTo(); + } + + Thread.sleep(2000); + + this.loginPage.form().login(username, password); + } + + private void navigateTo() { + this.driver.navigate().to(getResourceServerUrl()); + WaitUtils.waitUntilElement(By.xpath("//a[text() = 'Dynamic Menu']")); + } + + private boolean wasDenied() { + return this.driver.getPageSource().contains("You can not access this resource."); + } + + private URL getResourceServerUrl() { + try { + return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID); + } catch (MalformedURLException e) { + throw new RuntimeException("Could not obtain resource server url.", e); + } + } + + private void navigateToDynamicMenuPage() { + navigateTo(); + getLink("Dynamic Menu").click(); + } + + private void navigateToUserPremiumPage() { + navigateTo(); + getLink("User Premium").click(); + } + + private void navigateToAdminPage() { + navigateTo(); + getLink("Administration").click(); + } +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyServletAuthzAdapterTest.java new file mode 100644 index 0000000000..9a9a49b8de --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyServletAuthzAdapterTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.adapter.example; + +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.keycloak.testsuite.adapter.example.authorization.AbstractDefaultAuthzConfigAdapterTest; +import org.keycloak.testsuite.adapter.example.authorization.AbstractServletAuthzAdapterTest; +import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; + +/** + * + * @author tkyjovsk + */ +@RunAsClient +@AppServerContainer("app-server-wildfly") +//@AdapterLibsLocationProperty("adapter.libs.wildfly") +public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzAdapterTest { + +} diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml index b8c715f229..7ae36b2c53 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml @@ -287,6 +287,12 @@ ${project.version} war + + org.keycloak.testsuite + servlet-authz-app + ${project.version} + war + ${examples.home} true From 7983ed064fa6d2910d8501e0ac6de22dcd55de83 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Fri, 29 Jul 2016 15:25:36 -0300 Subject: [PATCH 3/3] [KEYCLOAK-3372] - More tests and code cleanup --- .../page/PhotozClientAuthzTestApp.java | 5 +- ...AbstractDefaultAuthzConfigAdapterTest.java | 27 +- .../AbstractPhotozExampleAdapterTest.java | 238 +++++++++++++++--- .../AbstractServletAuthzAdapterTest.java | 39 ++- .../WildflyDefaultAuthzConfigAdapterTest.java | 2 +- .../WildflyPhotozExampleAdapterTest.java | 2 +- .../WildflyServletAuthzAdapterTest.java | 2 +- 7 files changed, 232 insertions(+), 83 deletions(-) rename testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/{ => authorization}/WildflyDefaultAuthzConfigAdapterTest.java (94%) rename testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/{ => authorization}/WildflyPhotozExampleAdapterTest.java (94%) rename testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/{ => authorization}/WildflyServletAuthzAdapterTest.java (95%) diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java index 4721737daa..0e07157fe4 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java @@ -50,7 +50,10 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl { protected ConsentPage consentPage; public void createAlbum(String name) { - this.driver.findElement(By.id("create-album")).click(); + navigateTo(); + By id = By.id("create-album"); + WaitUtils.waitUntilElement(id); + this.driver.findElement(id).click(); Form.setInputValue(this.driver.findElement(By.id("album.name")), name); this.driver.findElement(By.id("save-album")).click(); pause(500); 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 82b3ec4133..46666747ca 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 @@ -59,31 +59,28 @@ public abstract class AbstractDefaultAuthzConfigAdapterTest extends AbstractExam @Test public void testDefaultAuthzConfig() throws Exception { - configureAuthorizationServices(); - deploy(); - navigateToResourceServer(); - login(); + try { + this.deployer.deploy(RESOURCE_SERVER_ID); + configureAuthorizationServices(); - assertTrue(this.driver.getPageSource().contains("Your permissions are")); - assertTrue(this.driver.getPageSource().contains("Default Resource")); + login(); + + assertTrue(this.driver.getPageSource().contains("Your permissions are")); + assertTrue(this.driver.getPageSource().contains("Default Resource")); + } finally { + this.deployer.undeploy(RESOURCE_SERVER_ID); + } } - private void login() { - this.loginPage.form().login("alice", "alice"); - } - - private void navigateToResourceServer() throws MalformedURLException { + private void login() throws MalformedURLException { this.driver.navigate().to(getResourceServerUrl()); + this.loginPage.form().login("alice", "alice"); } private URL getResourceServerUrl() throws MalformedURLException { return this.appServerContextRootPage.getUriBuilder().path(RESOURCE_SERVER_ID).build().toURL(); } - private void deploy() { - this.deployer.deploy(RESOURCE_SERVER_ID); - } - private void configureAuthorizationServices() { ClientsResource clients = realmsResouce().realm(REALM_NAME).clients(); ClientRepresentation client = clients.findByClientId(RESOURCE_SERVER_ID).get(0); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java index 28662fa573..9a0fb262dd 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java @@ -25,6 +25,7 @@ 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.admin.client.resource.ResourcesResource; import org.keycloak.admin.client.resource.RoleResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UsersResource; @@ -43,6 +44,10 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -95,7 +100,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } @Test - public void testCreateDeleteAlbum() throws Exception { + public void testUserCanCreateAndDeleteAlbum() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); @@ -103,13 +108,11 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd this.clientPage.createAlbum("Alice Family Album"); List resources = getAuthorizationResource().resources().resources(); - assertFalse(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); this.clientPage.deleteAlbum("Alice Family Album"); resources = getAuthorizationResource().resources().resources(); - assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); @@ -122,11 +125,11 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd this.deployer.deploy(RESOURCE_SERVER_ID); this.clientPage.login("alice", "alice"); this.clientPage.createAlbum("Alice-Family-Album"); + this.clientPage.login("admin", "admin"); this.clientPage.navigateToAdminAlbum(); List resources = getAuthorizationResource().resources().resources(); - assertFalse(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { @@ -137,11 +140,11 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } this.clientPage.login("admin", "admin"); + this.clientPage.navigateToAdminAlbum(); this.clientPage.deleteAlbum("Alice-Family-Album"); - + assertTrue(this.clientPage.wasDenied()); resources = getAuthorizationResource().resources().resources(); - assertFalse(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { @@ -153,9 +156,8 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd this.clientPage.navigateToAdminAlbum(); this.clientPage.deleteAlbum("Alice-Family-Album"); - + assertFalse(this.clientPage.wasDenied()); resources = getAuthorizationResource().resources().resources(); - assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); @@ -166,9 +168,9 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd public void testRegularUserCanNotAccessAdminResources() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); + this.clientPage.login("alice", "alice"); this.clientPage.navigateToAdminAlbum(); - assertTrue(this.clientPage.wasDenied()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); @@ -179,9 +181,9 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd public void testAdminOnlyFromSpecificAddress() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); + this.clientPage.login("admin", "admin"); this.clientPage.navigateToAdminAlbum(); - assertFalse(this.clientPage.wasDenied()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { @@ -193,7 +195,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } this.clientPage.navigateToAdminAlbum(); - assertTrue(this.clientPage.wasDenied()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); @@ -204,16 +205,15 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd public void testAdminWithoutPermissionsToTypedResource() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); + this.clientPage.login("alice", "alice"); this.clientPage.createAlbum("Alice Family Album"); this.clientPage.login("admin", "admin"); this.clientPage.navigateToAdminAlbum(); - assertFalse(this.clientPage.wasDenied()); this.clientPage.viewAlbum("Alice Family Album"); - assertFalse(this.clientPage.wasDenied()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { @@ -242,7 +242,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd this.clientPage.navigateToAdminAlbum(); this.clientPage.viewAlbum("Alice Family Album"); - assertTrue(this.clientPage.wasDenied()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { @@ -254,14 +253,11 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd this.clientPage.navigateToAdminAlbum(); this.clientPage.viewAlbum("Alice Family Album"); - assertFalse(this.clientPage.wasDenied()); this.clientPage.navigateToAdminAlbum(); this.clientPage.deleteAlbum("Alice Family Album"); - List resources = getAuthorizationResource().resources().resources(); - assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); @@ -269,23 +265,20 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd } @Test - public void testAdminWithoutPermissionsToDeleteScopePermission() throws Exception { + public void testAdminWithoutPermissionsToDeleteAlbum() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); + this.clientPage.login("alice", "alice"); this.clientPage.createAlbum("Alice Family Album"); this.clientPage.login("admin", "admin"); this.clientPage.navigateToAdminAlbum(); - assertFalse(this.clientPage.wasDenied()); this.clientPage.deleteAlbum("Alice Family Album"); - assertFalse(this.clientPage.wasDenied()); - List resources = getAuthorizationResource().resources().resources(); - assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { @@ -301,14 +294,11 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd this.clientPage.login("admin", "admin"); this.clientPage.navigateToAdminAlbum(); this.clientPage.viewAlbum("Alice Family Album"); - assertFalse(this.clientPage.wasDenied()); resources = getAuthorizationResource().resources().resources(); - assertFalse(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); this.clientPage.navigateToAdminAlbum(); - this.clientPage.deleteAlbum("Alice Family Album"); assertTrue(this.clientPage.wasDenied()); @@ -321,11 +311,8 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd this.clientPage.navigateToAdminAlbum(); this.clientPage.deleteAlbum("Alice Family Album"); - assertFalse(this.clientPage.wasDenied()); - resources = getAuthorizationResource().resources().resources(); - assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); @@ -336,8 +323,8 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd public void testClientRoleRepresentingUserConsent() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); - this.clientPage.login("alice", "alice"); + this.clientPage.login("alice", "alice"); assertFalse(this.clientPage.wasDenied()); UsersResource usersResource = realmsResouce().realm(REALM_NAME).users(); @@ -361,11 +348,9 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd roleResource.update(roleRepresentation); this.clientPage.login("alice", "alice"); - assertTrue(this.clientPage.wasDenied()); this.clientPage.loginWithScopes("alice", "alice", RESOURCE_SERVER_ID + "/manage-albums"); - assertFalse(this.clientPage.wasDenied()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); @@ -376,6 +361,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd public void testClientRoleNotRequired() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); + this.clientPage.login("alice", "alice"); assertFalse(this.clientPage.wasDenied()); @@ -401,37 +387,207 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd manageAlbumRole.update(roleRepresentation); this.clientPage.login("alice", "alice"); - assertTrue(this.clientPage.wasDenied()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { if ("Any User Policy".equals(policy.getName())) { List roles = JsonSerialization.readValue(policy.getConfig().get("roles"), List.class); - roles.forEach(new Consumer() { - @Override - public void accept(Map role) { - String roleId = (String) role.get("id"); - if (roleId.equals(manageAlbumRole.toRepresentation().getId())) { - role.put("required", false); - } + roles.forEach(role -> { + String roleId = (String) role.get("id"); + if (roleId.equals(manageAlbumRole.toRepresentation().getId())) { + role.put("required", false); } }); policy.getConfig().put("roles", JsonSerialization.writeValueAsString(roles)); - getAuthorizationResource().policies().policy(policy.getId()).update(policy); } } this.clientPage.login("alice", "alice"); - assertFalse(this.clientPage.wasDenied()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); } } + @Test + public void testOverridePermissionFromResourceParent() throws Exception { + try { + this.deployer.deploy(RESOURCE_SERVER_ID); + + this.clientPage.login("alice", "alice"); + String resourceName = "My Resource Instance"; + this.clientPage.createAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.viewAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.navigateTo(); + this.clientPage.deleteAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.createAlbum(resourceName); + + this.clientPage.login("admin", "admin"); + + this.clientPage.navigateToAdminAlbum(); + this.clientPage.viewAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.navigateToAdminAlbum();; + this.clientPage.deleteAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.login("alice", "alice"); + this.clientPage.createAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + getAuthorizationResource().resources().resources().forEach(resource -> { + if (resource.getName().equals(resourceName)) { + try { + PolicyRepresentation resourceInstancePermission = new PolicyRepresentation(); + + resourceInstancePermission.setName(resourceName + "Permission"); + resourceInstancePermission.setType("resource"); + + Map config = new HashMap<>(); + + config.put("resources", JsonSerialization.writeValueAsString(Arrays.asList(resource.getId()))); + config.put("applyPolicies", JsonSerialization.writeValueAsString(Arrays.asList("Only Owner Policy"))); + + resourceInstancePermission.setConfig(config); + getAuthorizationResource().policies().create(resourceInstancePermission); + } catch (Exception e) { + throw new RuntimeException("Error creating policy.", e); + } + } + }); + + this.clientPage.login("admin", "admin"); + + this.clientPage.navigateToAdminAlbum(); + this.clientPage.viewAlbum(resourceName); + assertTrue(this.clientPage.wasDenied()); + + this.clientPage.navigateToAdminAlbum(); + this.clientPage.deleteAlbum(resourceName); + assertTrue(this.clientPage.wasDenied()); + + this.clientPage.login("alice", "alice"); + this.clientPage.deleteAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + ResourcesResource resourcesResource = getAuthorizationResource().resources(); + List resources = resourcesResource.resources(); + assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); + } finally { + this.deployer.undeploy(RESOURCE_SERVER_ID); + } + } + + @Test + public void testInheritPermissionFromResourceParent() throws Exception { + try { + this.deployer.deploy(RESOURCE_SERVER_ID); + + this.clientPage.login("alice", "alice"); + + String resourceName = "My Resource Instance"; + this.clientPage.createAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.viewAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.navigateTo(); + this.clientPage.deleteAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.createAlbum(resourceName); + + this.clientPage.login("admin", "admin"); + + this.clientPage.navigateToAdminAlbum(); + this.clientPage.viewAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.navigateToAdminAlbum();; + this.clientPage.deleteAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.login("alice", "alice"); + this.clientPage.createAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + ResourcesResource resourcesResource = getAuthorizationResource().resources(); + resourcesResource.resources().forEach(resource -> { + if (resource.getName().equals(resourceName)) { + try { + PolicyRepresentation resourceInstancePermission = new PolicyRepresentation(); + + resourceInstancePermission.setName(resourceName + "Permission"); + resourceInstancePermission.setType("resource"); + + Map config = new HashMap<>(); + + config.put("resources", JsonSerialization.writeValueAsString(Arrays.asList(resource.getId()))); + config.put("applyPolicies", JsonSerialization.writeValueAsString(Arrays.asList("Only Owner Policy"))); + + resourceInstancePermission.setConfig(config); + getAuthorizationResource().policies().create(resourceInstancePermission); + } catch (Exception e) { + throw new RuntimeException("Error creating policy.", e); + } + } + }); + + this.clientPage.login("admin", "admin"); + + this.clientPage.navigateToAdminAlbum(); + this.clientPage.viewAlbum(resourceName); + assertTrue(this.clientPage.wasDenied()); + + this.clientPage.navigateToAdminAlbum(); + this.clientPage.deleteAlbum(resourceName); + assertTrue(this.clientPage.wasDenied()); + + resourcesResource.resources().forEach(resource -> { + if (resource.getName().equals(resourceName)) { + resource.setScopes(resource.getScopes().stream().filter(scope -> !scope.getName().equals("urn:photoz.com:scopes:album:view")).collect(Collectors.toSet())); + resourcesResource.resource(resource.getId()).update(resource); + } + }); + + this.clientPage.login("admin", "admin"); + + this.clientPage.navigateToAdminAlbum(); + this.clientPage.viewAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + + this.clientPage.navigateToAdminAlbum(); + this.clientPage.deleteAlbum(resourceName); + assertTrue(this.clientPage.wasDenied()); + + this.clientPage.login("alice", "alice"); + this.clientPage.deleteAlbum(resourceName); + assertFalse(this.clientPage.wasDenied()); + List resources = resourcesResource.resources(); + assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty()); + + resourcesResource.resources().forEach(resource -> { + if (resource.getName().equals(resourceName)) { + resource.setScopes(Collections.emptySet()); + resourcesResource.resource(resource.getId()).update(resource); + } + }); + } finally { + this.deployer.undeploy(RESOURCE_SERVER_ID); + } + } + private void importResourceServerSettings() throws FileNotFoundException { getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/photoz/photoz-restful-api-authz-service.json")), ResourceServerRepresentation.class)); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java index 2753c5e5bd..7f61556de9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletAuthzAdapterTest.java @@ -25,7 +25,6 @@ import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.admin.client.resource.RealmResource; -import org.keycloak.admin.client.resource.RoleResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.representations.idm.ClientRepresentation; @@ -49,9 +48,6 @@ import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.function.Predicate; -import java.util.stream.Collectors; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -88,14 +84,12 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda } @Test - public void testUserPermissions() throws Exception { + public void testRegularUserPermissions() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); login("alice", "alice"); - assertFalse(wasDenied()); - assertTrue(hasLink("User Premium")); assertTrue(hasLink("Administration")); assertTrue(hasText("urn:servlet-authz:page:main:actionForUser")); @@ -103,18 +97,14 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda assertFalse(hasText("urn:servlet-authz:page:main:actionForPremiumUser")); navigateToDynamicMenuPage(); - assertTrue(hasText("Do user thing")); assertFalse(hasText("Do user premium thing")); assertFalse(hasText("Do administration thing")); - navigateToUserPremiumPage(); - assertTrue(wasDenied()); navigateToAdminPage(); - assertTrue(wasDenied()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); @@ -127,7 +117,6 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda this.deployer.deploy(RESOURCE_SERVER_ID); login("jdoe", "jdoe"); - assertFalse(wasDenied()); assertTrue(hasLink("User Premium")); assertTrue(hasLink("Administration")); @@ -136,10 +125,15 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda assertFalse(hasText("urn:servlet-authz:page:main:actionForAdmin")); navigateToDynamicMenuPage(); - assertTrue(hasText("Do user thing")); assertTrue(hasText("Do user premium thing")); assertFalse(hasText("Do administration thing")); + + navigateToUserPremiumPage(); + assertFalse(wasDenied()); + + navigateToAdminPage(); + assertTrue(wasDenied()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); } @@ -151,9 +145,7 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda this.deployer.deploy(RESOURCE_SERVER_ID); login("admin", "admin"); - assertFalse(wasDenied()); - assertTrue(hasLink("User Premium")); assertTrue(hasLink("Administration")); assertTrue(hasText("urn:servlet-authz:page:main:actionForUser")); @@ -161,26 +153,29 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda assertFalse(hasText("urn:servlet-authz:page:main:actionForPremiumUser")); navigateToDynamicMenuPage(); - assertTrue(hasText("Do user thing")); assertTrue(hasText("Do administration thing")); assertFalse(hasText("Do user premium thing")); + + navigateToUserPremiumPage(); + assertTrue(wasDenied()); + + navigateToAdminPage(); + assertFalse(wasDenied()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); } } @Test - public void testGrantPremiumAccess() throws Exception { + public void testGrantPremiumAccessToUser() throws Exception { try { this.deployer.deploy(RESOURCE_SERVER_ID); login("alice", "alice"); - assertFalse(wasDenied()); navigateToUserPremiumPage(); - assertTrue(wasDenied()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { @@ -193,7 +188,6 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda login("alice", "alice"); navigateToUserPremiumPage(); - assertFalse(wasDenied()); for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) { @@ -204,8 +198,8 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda } login("alice", "alice"); - navigateToUserPremiumPage(); + navigateToUserPremiumPage(); assertTrue(wasDenied()); PolicyRepresentation onlyAlicePolicy = new PolicyRepresentation(); @@ -230,10 +224,9 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda } } - logOut(); login("alice", "alice"); - navigateToUserPremiumPage(); + navigateToUserPremiumPage(); assertFalse(wasDenied()); } finally { this.deployer.undeploy(RESOURCE_SERVER_ID); diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyDefaultAuthzConfigAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyDefaultAuthzConfigAdapterTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyDefaultAuthzConfigAdapterTest.java rename to testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyDefaultAuthzConfigAdapterTest.java index 712daa06f5..7e31ead134 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyDefaultAuthzConfigAdapterTest.java +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyDefaultAuthzConfigAdapterTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.adapter.example; +package org.keycloak.testsuite.adapter.example.authorization; import org.keycloak.testsuite.adapter.example.authorization.AbstractDefaultAuthzConfigAdapterTest; import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java similarity index 94% rename from testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyPhotozExampleAdapterTest.java rename to testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java index d9e2c343fa..f35217c719 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyPhotozExampleAdapterTest.java +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyPhotozExampleAdapterTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.adapter.example; +package org.keycloak.testsuite.adapter.example.authorization; import org.keycloak.testsuite.adapter.example.authorization.AbstractPhotozExampleAdapterTest; import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyServletAuthzAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java similarity index 95% rename from testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyServletAuthzAdapterTest.java rename to testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java index 9a9a49b8de..d50cf29c54 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyServletAuthzAdapterTest.java +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/authorization/WildflyServletAuthzAdapterTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.testsuite.adapter.example; +package org.keycloak.testsuite.adapter.example.authorization; import org.jboss.arquillian.container.test.api.RunAsClient; import org.keycloak.testsuite.adapter.example.authorization.AbstractDefaultAuthzConfigAdapterTest;