Merge pull request #3082 from pedroigor/master
[KEYCLOAK-3372] - Code cleanup and more testing
This commit is contained in:
commit
26f0f6509a
41 changed files with 1336 additions and 190 deletions
|
@ -126,11 +126,13 @@ public abstract class AbstractPolicyEnforcer {
|
|||
List<Permission> permissions = authorization.getPermissions();
|
||||
|
||||
for (Permission permission : permissions) {
|
||||
Set<String> 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<String> requiredScopes, Permission permission, PathConfig actualPathConfig) {
|
||||
Set<String> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<PathConfig> 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()) {
|
||||
|
|
|
@ -109,7 +109,6 @@ public class PolicyEnforcerConfig {
|
|||
private List<MethodConfig> methods = new ArrayList<>();
|
||||
private List<String> 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) {
|
||||
|
|
|
@ -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<String, String> config = new HashMap();
|
||||
private List<PolicyRepresentation> dependentPolicies;
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private List<PolicyRepresentation> associatedPolicies = new ArrayList<>();
|
||||
|
||||
public String getId() {
|
||||
|
|
|
@ -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}"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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<ScopeRepresentation> 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());
|
||||
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -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\"}]"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
String contextPath = request.getContextPath();
|
||||
String redirectUri = scheme + "://" + host + ":" + port + contextPath;
|
||||
%>
|
||||
<h2>Click <a href="<%= KeycloakUriBuilder.fromUri("http://localhost:8080/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
|
||||
.queryParam("redirect_uri", redirectUri).build("servlet-authz").toString()%>">here</a> to logout.</h2>
|
||||
<h2>Click here <a href="<%= KeycloakUriBuilder.fromUri("http://localhost:8080/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
|
||||
.queryParam("redirect_uri", redirectUri).build("servlet-authz").toString()%>">Sign Out</a></h2>
|
|
@ -67,7 +67,8 @@ public interface Attributes {
|
|||
* @return true if any attribute with <code>name</code> and <code>value</code> exist. Otherwise, returns false.
|
||||
*/
|
||||
default boolean containsValue(String name, String value) {
|
||||
return toMap().getOrDefault(name, emptyList()).stream().anyMatch(value::equals);
|
||||
Collection<String> values = toMap().get(name);
|
||||
return values != null && values.stream().anyMatch(value::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<Scope> scopes = new HashSet<>(policy.getScopes());
|
||||
|
||||
boolean hasScope = true;
|
||||
if (scopes.isEmpty()) {
|
||||
Set<Resource> 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<Resource> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Permission> entitlements = Permissions.allPermits(results);
|
||||
|
||||
if (entitlements.isEmpty()) {
|
||||
HashMap<Object, Object> 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<Entry<String, Set<String>>, Stream<ResourcePermission>>) 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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Permission> entitlements = Permissions.allPermits(results);
|
||||
|
||||
if (entitlements.isEmpty()) {
|
||||
HashMap<Object, Object> 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<Map.Entry<String, Set<String>>, Stream<ResourcePermission>>) 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ResourcePermission> createResourcePermissions(Resource resource, ResourceServer resourceServer, AuthorizationProvider authorization) {
|
||||
public static List<ResourcePermission> createResourcePermissions(Resource resource, Set<String> requestedScopes, AuthorizationProvider authorization) {
|
||||
List<ResourcePermission> permissions = new ArrayList<>();
|
||||
List<Scope> scopes = resource.getScopes();
|
||||
|
||||
if (scopes.isEmpty()) {
|
||||
String type = resource.getType();
|
||||
String type = resource.getType();
|
||||
ResourceServer resourceServer = resource.getResourceServer();
|
||||
List<Scope> 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<Permission> allPermits(List<Result> evaluation) {
|
||||
List<Permission> permissions = evaluation.stream()
|
||||
.filter(evaluationResult -> evaluationResult.getEffect().equals(Effect.PERMIT))
|
||||
.map(evaluationResult -> {
|
||||
ResourcePermission permission = evaluationResult.getPermission();
|
||||
String resourceId = null;
|
||||
String resourceName = null;
|
||||
Map<String, Permission> permissions = new HashMap<>();
|
||||
|
||||
Resource resource = permission.getResource();
|
||||
|
||||
if (resource != null) {
|
||||
resourceId = resource.getId();
|
||||
resourceName = resource.getName();
|
||||
}
|
||||
|
||||
Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
|
||||
|
||||
return new Permission(resourceId, resourceName, scopes);
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
Map<String, Permission> 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<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
|
||||
if (evaluationResult.getEffect().equals(Effect.DENY)) {
|
||||
continue;
|
||||
}
|
||||
Resource resource = permission.getResource();
|
||||
|
||||
Set<String> 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<String> 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<String> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ScopeRepresentation> 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());
|
||||
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -20,5 +20,6 @@
|
|||
<module>js-database</module>
|
||||
<module>photoz</module>
|
||||
<module>hello-world-authz-service</module>
|
||||
<module>servlet-authz</module>
|
||||
</modules>
|
||||
</project>
|
|
@ -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
|
53
testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml
Executable file
53
testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml
Executable file
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-test-apps</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>servlet-authz-app</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>Keycloak Authz: Servlet Authorization Test</name>
|
||||
<description>Servlet Authorization Test</description>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-authz-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jboss.as.plugins</groupId>
|
||||
<artifactId>jboss-as-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.wildfly.plugins</groupId>
|
||||
<artifactId>wildfly-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -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\"]"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
~ 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<jboss-deployment-structure>
|
||||
<deployment>
|
||||
<dependencies>
|
||||
<module name="org.keycloak.keycloak-authz-client" services="import"/>
|
||||
</dependencies>
|
||||
</deployment>
|
||||
</jboss-deployment-structure>
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
version="3.0">
|
||||
|
||||
<module-name>servlet-authz-app</module-name>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>All Resources</web-resource-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>user</role-name>
|
||||
</auth-constraint>
|
||||
</security-constraint>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>All Resources</web-resource-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>admin</role-name>
|
||||
</auth-constraint>
|
||||
</security-constraint>
|
||||
|
||||
<login-config>
|
||||
<auth-method>KEYCLOAK</auth-method>
|
||||
<realm-name>servlet-authz</realm-name>
|
||||
</login-config>
|
||||
|
||||
<security-role>
|
||||
<role-name>admin</role-name>
|
||||
</security-role>
|
||||
|
||||
<security-role>
|
||||
<role-name>user</role-name>
|
||||
</security-role>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/accessDenied.jsp</location>
|
||||
</error-page>
|
||||
|
||||
</web-app>
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2 style="color: red">You can not access this resource.</h2>
|
||||
<%@include file="logout-include.jsp"%>
|
||||
</body>
|
||||
</html>
|
|
@ -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();
|
||||
%>
|
||||
|
||||
<html>
|
||||
<body>
|
||||
<%@include file="logout-include.jsp"%>
|
||||
<h2>This is a public resource. Try to access one of these <i>protected</i> resources:</h2>
|
||||
|
||||
<p><a href="protected/dynamicMenu.jsp">Dynamic Menu</a></p>
|
||||
<p><a href="protected/premium/onlyPremium.jsp">User Premium</a></p>
|
||||
<p><a href="protected/admin/onlyAdmin.jsp">Administration</a></p>
|
||||
|
||||
<h3>Your permissions are:</h3>
|
||||
|
||||
<ul>
|
||||
<%
|
||||
for (Permission permission : authzContext.getPermissions()) {
|
||||
%>
|
||||
<li>
|
||||
<p>Resource: <%= permission.getResourceSetName() %></p>
|
||||
<p>ID: <%= permission.getResourceSetId() %></p>
|
||||
<p>Scopes: <%= permission.getScopes() %></p>
|
||||
</li>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -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;
|
||||
%>
|
||||
<h2>Click here <a href="<%= KeycloakUriBuilder.fromUri("http://localhost:8180/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
|
||||
.queryParam("redirect_uri", redirectUri).build("servlet-authz").toString()%>">Sign Out</a></h2>
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2>Only Administrators can access this page.</h2>
|
||||
<%@include file="../../logout-include.jsp"%>
|
||||
</body>
|
||||
</html>
|
|
@ -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();
|
||||
%>
|
||||
|
||||
<html>
|
||||
<body>
|
||||
<h2>Any authenticated user can access this page.</h2>
|
||||
<%@include file="../logout-include.jsp"%>
|
||||
|
||||
<p>Here is a dynamic menu built from the permissions returned by the server:</p>
|
||||
|
||||
<ul>
|
||||
<%
|
||||
if (authzContext.hasResourcePermission("Protected Resource")) {
|
||||
%>
|
||||
<li>
|
||||
Do user thing
|
||||
</li>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
<%
|
||||
if (authzContext.hasResourcePermission("Premium Resource")) {
|
||||
%>
|
||||
<li>
|
||||
Do user premium thing
|
||||
</li>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
||||
<%
|
||||
if (authzContext.hasPermission("Admin Resource", "urn:servlet-authz:protected:admin:access")) {
|
||||
%>
|
||||
<li>
|
||||
Do administration thing
|
||||
</li>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2>Only for premium users.</h2>
|
||||
<%@include file="../../logout-include.jsp"%>
|
||||
</body>
|
||||
</html>
|
|
@ -43,5 +43,13 @@
|
|||
<exclude name="**/subsystem-config.xml"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<copy todir="target/test-apps/servlet-authz-app" overwrite="true">
|
||||
<fileset dir="../servlet-authz">
|
||||
<exclude name="**/target/**"/>
|
||||
<exclude name="**/*.iml"/>
|
||||
<exclude name="**/*.unconfigured"/>
|
||||
<exclude name="**/subsystem-config.xml"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
</target>
|
||||
</project>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
@ -94,7 +99,8 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
|||
importResourceServerSettings();
|
||||
}
|
||||
|
||||
public void testCreateDeleteAlbum() throws Exception {
|
||||
@Test
|
||||
public void testUserCanCreateAndDeleteAlbum() throws Exception {
|
||||
try {
|
||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||
|
||||
|
@ -102,29 +108,28 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
|||
this.clientPage.createAlbum("Alice Family Album");
|
||||
|
||||
List<ResourceRepresentation> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyOwnerCanDeleteAlbum() 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();
|
||||
|
||||
List<ResourceRepresentation> resources = getAuthorizationResource().resources().resources();
|
||||
|
||||
assertFalse(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty());
|
||||
|
||||
for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
|
||||
|
@ -135,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()) {
|
||||
|
@ -151,33 +156,34 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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()) {
|
||||
|
@ -189,7 +195,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
|||
}
|
||||
|
||||
this.clientPage.navigateToAdminAlbum();
|
||||
|
||||
assertTrue(this.clientPage.wasDenied());
|
||||
} finally {
|
||||
this.deployer.undeploy(RESOURCE_SERVER_ID);
|
||||
|
@ -200,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()) {
|
||||
|
@ -238,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()) {
|
||||
|
@ -250,37 +253,32 @@ 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<ResourceRepresentation> 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);
|
||||
}
|
||||
}
|
||||
|
||||
public void testAdminWithoutPermissionsToDeleteScopePermission() throws Exception {
|
||||
@Test
|
||||
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<ResourceRepresentation> resources = getAuthorizationResource().resources().resources();
|
||||
|
||||
assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty());
|
||||
|
||||
for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
|
||||
|
@ -296,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());
|
||||
|
||||
|
@ -316,22 +311,20 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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();
|
||||
|
@ -355,20 +348,20 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientRoleNotRequired() throws Exception {
|
||||
try {
|
||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||
|
||||
this.clientPage.login("alice", "alice");
|
||||
|
||||
assertFalse(this.clientPage.wasDenied());
|
||||
|
@ -394,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<Map> roles = JsonSerialization.readValue(policy.getConfig().get("roles"), List.class);
|
||||
|
||||
roles.forEach(new Consumer<Map>() {
|
||||
@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<String, String> 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<ResourceRepresentation> 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<String, String> 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<ResourceRepresentation> 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));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* 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.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 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 <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
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<RealmRepresentation> 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 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"));
|
||||
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"));
|
||||
|
||||
navigateToUserPremiumPage();
|
||||
assertFalse(wasDenied());
|
||||
|
||||
navigateToAdminPage();
|
||||
assertTrue(wasDenied());
|
||||
} 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"));
|
||||
|
||||
navigateToUserPremiumPage();
|
||||
assertTrue(wasDenied());
|
||||
|
||||
navigateToAdminPage();
|
||||
assertFalse(wasDenied());
|
||||
} finally {
|
||||
this.deployer.undeploy(RESOURCE_SERVER_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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()) {
|
||||
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<String, String> config = new HashMap<>();
|
||||
UsersResource usersResource = realmsResouce().realm(REALM_NAME).users();
|
||||
List<UserRepresentation> 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);
|
||||
}
|
||||
}
|
||||
|
||||
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<UserRepresentation> 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();
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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.authorization;
|
||||
|
||||
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 {
|
||||
|
||||
}
|
|
@ -287,6 +287,12 @@
|
|||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>servlet-authz-app</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<outputDirectory>${examples.home}</outputDirectory>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
|
|
Loading…
Reference in a new issue