[KEYCLOAK-4102] - Support lazy loading of paths via policy enforcer config
This commit is contained in:
parent
e9e376419d
commit
4a425c2674
44 changed files with 1132 additions and 1257 deletions
|
@ -19,7 +19,6 @@ package org.keycloak.adapters.authorization;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -30,10 +29,12 @@ import org.keycloak.adapters.spi.HttpFacade.Request;
|
|||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.ClientAuthorizationContext;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessToken.Authorization;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.MethodConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.ScopeEnforcementMode;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
|
||||
/**
|
||||
|
@ -42,31 +43,23 @@ import org.keycloak.representations.idm.authorization.Permission;
|
|||
public abstract class AbstractPolicyEnforcer {
|
||||
|
||||
private static Logger LOGGER = Logger.getLogger(AbstractPolicyEnforcer.class);
|
||||
private final PolicyEnforcerConfig enforcerConfig;
|
||||
private static final String HTTP_METHOD_DELETE = "DELETE";
|
||||
|
||||
private final PolicyEnforcer policyEnforcer;
|
||||
|
||||
private Map<String, PathConfig> paths;
|
||||
private AuthzClient authzClient;
|
||||
private PathMatcher pathMatcher;
|
||||
|
||||
public AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
|
||||
protected AbstractPolicyEnforcer(PolicyEnforcer policyEnforcer) {
|
||||
this.policyEnforcer = policyEnforcer;
|
||||
this.enforcerConfig = policyEnforcer.getEnforcerConfig();
|
||||
this.authzClient = policyEnforcer.getClient();
|
||||
this.pathMatcher = policyEnforcer.getPathMatcher();
|
||||
this.paths = policyEnforcer.getPaths();
|
||||
}
|
||||
|
||||
public AuthorizationContext authorize(OIDCHttpFacade httpFacade) {
|
||||
EnforcementMode enforcementMode = this.enforcerConfig.getEnforcementMode();
|
||||
EnforcementMode enforcementMode = getEnforcerConfig().getEnforcementMode();
|
||||
|
||||
if (EnforcementMode.DISABLED.equals(enforcementMode)) {
|
||||
return createEmptyAuthorizationContext(true);
|
||||
}
|
||||
|
||||
Request request = httpFacade.getRequest();
|
||||
String path = getPath(request);
|
||||
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
|
||||
PathConfig pathConfig = getPathConfig(request);
|
||||
KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
|
||||
|
||||
if (securityContext == null) {
|
||||
|
@ -79,16 +72,20 @@ public abstract class AbstractPolicyEnforcer {
|
|||
AccessToken accessToken = securityContext.getToken();
|
||||
|
||||
if (accessToken != null) {
|
||||
LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
|
||||
}
|
||||
|
||||
if (pathConfig == null) {
|
||||
if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
|
||||
return createAuthorizationContext(accessToken, null);
|
||||
}
|
||||
|
||||
LOGGER.debugf("Could not find a configuration for path [%s]", path);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debugf("Could not find a configuration for path [%s]", getPath(request));
|
||||
}
|
||||
|
||||
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
|
||||
if (isDefaultAccessDeniedUri(request)) {
|
||||
return createAuthorizationContext(accessToken, null);
|
||||
}
|
||||
|
||||
|
@ -111,10 +108,18 @@ public abstract class AbstractPolicyEnforcer {
|
|||
}
|
||||
}
|
||||
|
||||
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
|
||||
if (methodConfig != null && ScopeEnforcementMode.DISABLED.equals(methodConfig.getScopesEnforcementMode())) {
|
||||
return createEmptyAuthorizationContext(true);
|
||||
}
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
|
||||
}
|
||||
|
||||
if (!challenge(pathConfig, methodConfig, httpFacade)) {
|
||||
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
|
||||
}
|
||||
handleAccessDenied(httpFacade);
|
||||
}
|
||||
}
|
||||
|
@ -126,22 +131,21 @@ public abstract class AbstractPolicyEnforcer {
|
|||
|
||||
protected boolean isAuthorized(PathConfig actualPathConfig, MethodConfig methodConfig, AccessToken accessToken, OIDCHttpFacade httpFacade) {
|
||||
Request request = httpFacade.getRequest();
|
||||
PolicyEnforcerConfig enforcerConfig = getEnforcerConfig();
|
||||
|
||||
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
|
||||
if (isDefaultAccessDeniedUri(request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
AccessToken.Authorization authorization = accessToken.getAuthorization();
|
||||
Authorization authorization = accessToken.getAuthorization();
|
||||
|
||||
if (authorization == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Permission> permissions = authorization.getPermissions();
|
||||
boolean hasPermission = false;
|
||||
List<Permission> grantedPermissions = authorization.getPermissions();
|
||||
|
||||
for (Permission permission : permissions) {
|
||||
for (Permission permission : grantedPermissions) {
|
||||
if (permission.getResourceId() != null) {
|
||||
if (isResourcePermission(actualPathConfig, permission)) {
|
||||
hasPermission = true;
|
||||
|
@ -151,9 +155,11 @@ public abstract class AbstractPolicyEnforcer {
|
|||
}
|
||||
|
||||
if (hasResourceScopePermission(methodConfig, permission)) {
|
||||
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
|
||||
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
|
||||
this.paths.remove(actualPathConfig);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, grantedPermissions);
|
||||
}
|
||||
if (HTTP_METHOD_DELETE.equalsIgnoreCase(request.getMethod()) && actualPathConfig.isInstance()) {
|
||||
policyEnforcer.getPaths().remove(actualPathConfig);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -170,7 +176,9 @@ public abstract class AbstractPolicyEnforcer {
|
|||
return true;
|
||||
}
|
||||
|
||||
LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, permissions);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debugf("Authorization FAILED for path [%s]. Not enough permissions [%s].", actualPathConfig, grantedPermissions);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -179,15 +187,21 @@ public abstract class AbstractPolicyEnforcer {
|
|||
httpFacade.getResponse().sendError(403);
|
||||
}
|
||||
|
||||
private boolean isDefaultAccessDeniedUri(Request request, PolicyEnforcerConfig enforcerConfig) {
|
||||
String accessDeniedPath = enforcerConfig.getOnDenyRedirectTo();
|
||||
protected AuthzClient getAuthzClient() {
|
||||
return policyEnforcer.getClient();
|
||||
}
|
||||
|
||||
if (accessDeniedPath != null) {
|
||||
if (request.getURI().contains(accessDeniedPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
protected PolicyEnforcerConfig getEnforcerConfig() {
|
||||
return policyEnforcer.getEnforcerConfig();
|
||||
}
|
||||
|
||||
protected PolicyEnforcer getPolicyEnforcer() {
|
||||
return policyEnforcer;
|
||||
}
|
||||
|
||||
private boolean isDefaultAccessDeniedUri(Request request) {
|
||||
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
|
||||
return accessDeniedPath != null && request.getURI().contains(accessDeniedPath);
|
||||
}
|
||||
|
||||
private boolean hasResourceScopePermission(MethodConfig methodConfig, Permission permission) {
|
||||
|
@ -215,20 +229,8 @@ public abstract class AbstractPolicyEnforcer {
|
|||
return requiredScopes.isEmpty();
|
||||
}
|
||||
|
||||
protected AuthzClient getAuthzClient() {
|
||||
return this.authzClient;
|
||||
}
|
||||
|
||||
protected PolicyEnforcerConfig getEnforcerConfig() {
|
||||
return enforcerConfig;
|
||||
}
|
||||
|
||||
protected PolicyEnforcer getPolicyEnforcer() {
|
||||
return policyEnforcer;
|
||||
}
|
||||
|
||||
private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
|
||||
return new ClientAuthorizationContext(authzClient) {
|
||||
return new ClientAuthorizationContext(getAuthzClient()) {
|
||||
@Override
|
||||
public boolean hasPermission(String resourceName, String scopeName) {
|
||||
return granted;
|
||||
|
@ -279,7 +281,7 @@ public abstract class AbstractPolicyEnforcer {
|
|||
}
|
||||
|
||||
private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
|
||||
return new ClientAuthorizationContext(accessToken, pathConfig, this.paths, authzClient);
|
||||
return new ClientAuthorizationContext(accessToken, pathConfig, policyEnforcer.getPaths(), getAuthzClient());
|
||||
}
|
||||
|
||||
private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
|
||||
|
@ -297,4 +299,8 @@ public abstract class AbstractPolicyEnforcer {
|
|||
private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
|
||||
return permission.getResourceId().equals(actualPathConfig.getId());
|
||||
}
|
||||
|
||||
private PathConfig getPathConfig(Request request) {
|
||||
return isDefaultAccessDeniedUri(request) ? null : policyEnforcer.getPathMatcher().matches(getPath(request));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.util.concurrent.locks.LockSupport;
|
|||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||
|
||||
/**
|
||||
* A simple LRU cache implementation supporting expiration and maximum number of entries.
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class PathCache {
|
||||
|
@ -39,15 +41,6 @@ public class PathCache {
|
|||
|
||||
private final long maxAge;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param maxEntries the maximum number of entries to keep in the cache
|
||||
*/
|
||||
public PathCache(int maxEntries) {
|
||||
this(maxEntries, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
|
@ -80,6 +73,10 @@ public class PathCache {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean containsKey(String uri) {
|
||||
return cache.containsKey(uri);
|
||||
}
|
||||
|
||||
public PathConfig get(String uri) {
|
||||
if (parkForReadAndCheckInterrupt()) {
|
||||
return null;
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
*/
|
||||
package org.keycloak.adapters.authorization;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -34,13 +34,13 @@ import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
|
|||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.ClientAuthenticator;
|
||||
import org.keycloak.authorization.client.Configuration;
|
||||
import org.keycloak.authorization.client.representation.ResourceRepresentation;
|
||||
import org.keycloak.authorization.client.representation.ScopeRepresentation;
|
||||
import org.keycloak.authorization.client.resource.ProtectedResource;
|
||||
import org.keycloak.common.util.PathMatcher;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -69,8 +69,9 @@ public class PolicyEnforcer {
|
|||
}
|
||||
}
|
||||
});
|
||||
this.pathMatcher = new PathMatcher(this.authzClient);
|
||||
|
||||
this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
|
||||
this.pathMatcher = createPathMatcher(authzClient);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Initialization complete. Path configurations:");
|
||||
|
@ -104,11 +105,11 @@ public class PolicyEnforcer {
|
|||
return context;
|
||||
}
|
||||
|
||||
PolicyEnforcerConfig getEnforcerConfig() {
|
||||
public PolicyEnforcerConfig getEnforcerConfig() {
|
||||
return enforcerConfig;
|
||||
}
|
||||
|
||||
AuthzClient getClient() {
|
||||
public AuthzClient getClient() {
|
||||
return authzClient;
|
||||
}
|
||||
|
||||
|
@ -116,11 +117,11 @@ public class PolicyEnforcer {
|
|||
return paths;
|
||||
}
|
||||
|
||||
void addPath(PathConfig pathConfig) {
|
||||
paths.put(pathConfig.getPath(), pathConfig);
|
||||
public PathMatcher<PathConfig> getPathMatcher() {
|
||||
return pathMatcher;
|
||||
}
|
||||
|
||||
KeycloakDeployment getDeployment() {
|
||||
public KeycloakDeployment getDeployment() {
|
||||
return deployment;
|
||||
}
|
||||
|
||||
|
@ -144,7 +145,7 @@ public class PolicyEnforcer {
|
|||
}
|
||||
|
||||
private Map<String, PathConfig> configureDefinedPaths(ProtectedResource protectedResource, PolicyEnforcerConfig enforcerConfig) {
|
||||
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
|
||||
Map<String, PathConfig> paths = Collections.synchronizedMap(new LinkedHashMap<String, PathConfig>());
|
||||
|
||||
for (PathConfig pathConfig : enforcerConfig.getPaths()) {
|
||||
ResourceRepresentation resource;
|
||||
|
@ -197,45 +198,85 @@ public class PolicyEnforcer {
|
|||
LOGGER.info("Querying the server for all resources associated with this application.");
|
||||
Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
|
||||
|
||||
for (String id : protectedResource.findAll()) {
|
||||
ResourceRepresentation resourceDescription = protectedResource.findById(id);
|
||||
if (!enforcerConfig.getLazyLoadPaths()) {
|
||||
for (String id : protectedResource.findAll()) {
|
||||
ResourceRepresentation resourceDescription = protectedResource.findById(id);
|
||||
|
||||
if (resourceDescription.getUri() != null) {
|
||||
PathConfig pathConfig = createPathConfig(resourceDescription);
|
||||
paths.put(pathConfig.getPath(), pathConfig);
|
||||
if (resourceDescription.getUri() != null) {
|
||||
PathConfig pathConfig = PathConfig.createPathConfig(resourceDescription);
|
||||
paths.put(pathConfig.getPath(), pathConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
|
||||
PathConfig pathConfig = new PathConfig();
|
||||
private PathMatcher<PathConfig> createPathMatcher(final AuthzClient authzClient) {
|
||||
final PathCache pathCache = new PathCache(100, 30000);
|
||||
|
||||
pathConfig.setId(resourceDescription.getId());
|
||||
pathConfig.setName(resourceDescription.getName());
|
||||
return new PathMatcher<PathConfig>() {
|
||||
@Override
|
||||
public PathConfig matches(String targetUri) {
|
||||
PathConfig pathConfig = pathCache.get(targetUri);
|
||||
|
||||
String uri = resourceDescription.getUri();
|
||||
if (pathCache.containsKey(targetUri) || pathConfig != null) {
|
||||
return pathConfig;
|
||||
}
|
||||
|
||||
if (uri == null || "".equals(uri.trim())) {
|
||||
throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
|
||||
}
|
||||
pathConfig = super.matches(targetUri);
|
||||
|
||||
pathConfig.setPath(uri);
|
||||
if (enforcerConfig.getLazyLoadPaths() && (pathConfig == null || pathConfig.getPath().contains("*"))) {
|
||||
try {
|
||||
List<ResourceRepresentation> matchingResources = authzClient.protection().resource().findByMatchingUri(targetUri);
|
||||
|
||||
List<String> scopeNames = new ArrayList<>();
|
||||
if (!matchingResources.isEmpty()) {
|
||||
pathConfig = PathConfig.createPathConfig(matchingResources.get(0));
|
||||
paths.put(pathConfig.getPath(), pathConfig);
|
||||
}
|
||||
} catch (Exception cause) {
|
||||
LOGGER.errorf(cause, "Could not lazy load paths from server");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
for (ScopeRepresentation scope : resourceDescription.getScopes()) {
|
||||
scopeNames.add(scope.getName());
|
||||
}
|
||||
pathCache.put(targetUri, pathConfig);
|
||||
|
||||
pathConfig.setScopes(scopeNames);
|
||||
pathConfig.setType(resourceDescription.getType());
|
||||
return pathConfig;
|
||||
}
|
||||
|
||||
return pathConfig;
|
||||
}
|
||||
@Override
|
||||
protected String getPath(PathConfig entry) {
|
||||
return entry.getPath();
|
||||
}
|
||||
|
||||
public PathMatcher getPathMatcher() {
|
||||
return pathMatcher;
|
||||
@Override
|
||||
protected Collection<PathConfig> getPaths() {
|
||||
return paths.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
|
||||
if (originalConfig.hasPattern()) {
|
||||
ProtectedResource resource = authzClient.protection().resource();
|
||||
List<ResourceRepresentation> search = resource.findByUri(path);
|
||||
|
||||
if (!search.isEmpty()) {
|
||||
// resource does exist on the server, cache it
|
||||
ResourceRepresentation targetResource = search.get(0);
|
||||
PathConfig config = PathConfig.createPathConfig(targetResource);
|
||||
|
||||
config.setScopes(originalConfig.getScopes());
|
||||
config.setMethods(originalConfig.getMethods());
|
||||
config.setParentConfig(originalConfig);
|
||||
config.setEnforcementMode(originalConfig.getEnforcementMode());
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
*
|
||||
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.authorization.client.representation;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* <p>One or more resources that the resource server manages as a set of protected resources.
|
||||
*
|
||||
* <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.2">OAuth-resource-reg</a>.
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ResourceRepresentation {
|
||||
|
||||
@JsonProperty("_id")
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
private String displayName;
|
||||
private String uri;
|
||||
private String type;
|
||||
|
||||
@JsonProperty("resource_scopes")
|
||||
private Set<ScopeRepresentation> scopes;
|
||||
|
||||
@JsonProperty("icon_uri")
|
||||
private String iconUri;
|
||||
private String owner;
|
||||
private Boolean ownerManagedAccess;
|
||||
private Map<String, List<String>> attributes;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param name a human-readable string describing a set of one or more resources
|
||||
* @param uri a {@link URI} that provides the network location for the resource set being registered
|
||||
* @param type a string uniquely identifying the semantics of the resource set
|
||||
* @param scopes the available scopes for this resource set
|
||||
* @param iconUri a {@link URI} for a graphic icon representing the resource set
|
||||
*/
|
||||
public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes, String uri, String type, String iconUri) {
|
||||
this.name = name;
|
||||
this.scopes = scopes;
|
||||
this.uri = uri;
|
||||
this.type = type;
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param name a human-readable string describing a set of one or more resources
|
||||
* @param uri a {@link URI} that provides the network location for the resource set being registered
|
||||
* @param type a string uniquely identifying the semantics of the resource set
|
||||
* @param scopes the available scopes for this resource set
|
||||
*/
|
||||
public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes, String uri, String type) {
|
||||
this(name, scopes, uri, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param name a human-readable string describing a set of one or more resources
|
||||
* @param serverUri a {@link URI} that identifies this resource server
|
||||
* @param scopes the available scopes for this resource set
|
||||
*/
|
||||
public ResourceRepresentation(String name, Set<ScopeRepresentation> scopes) {
|
||||
this(name, scopes, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
*/
|
||||
public ResourceRepresentation() {
|
||||
this(null, null, null, null, null);
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public Set<ScopeRepresentation> getScopes() {
|
||||
if (this.scopes == null) {
|
||||
this.scopes = Collections.emptySet();
|
||||
}
|
||||
return Collections.unmodifiableSet(this.scopes);
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return this.iconUri;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setScopes(Set<ScopeRepresentation> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public void setOwnerManagedAccess(Boolean ownerManagedAccess) {
|
||||
this.ownerManagedAccess = ownerManagedAccess;
|
||||
}
|
||||
|
||||
public Boolean getOwnerManagedAccess() {
|
||||
return ownerManagedAccess;
|
||||
}
|
||||
|
||||
public void addScope(ScopeRepresentation scopeRepresentation) {
|
||||
if (this.scopes == null) {
|
||||
this.scopes = new HashSet<>();
|
||||
}
|
||||
this.scopes.add(scopeRepresentation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ResourceRepresentation that = (ResourceRepresentation) o;
|
||||
return Objects.equals(id, that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResourceRepresentation{" +
|
||||
"id='" + id + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", uri='" + uri + '\'' +
|
||||
", type='" + type + '\'' +
|
||||
", owner='" + owner + '\'' +
|
||||
", scopes=" + scopes +
|
||||
'}';
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
*
|
||||
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.authorization.client.representation;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>A bounded extent of access that is possible to perform on a resource set. In authorization policy terminology,
|
||||
* a scope is one of the potentially many "verbs" that can logically apply to a resource set ("object").
|
||||
*
|
||||
* <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.1">OAuth-resource-reg</a>.
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ScopeRepresentation {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String iconUri;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param name the a human-readable string describing some scope (extent) of access
|
||||
* @param iconUri a {@link URI} for a graphic icon representing the scope
|
||||
*/
|
||||
public ScopeRepresentation(String name, String iconUri) {
|
||||
this.name = name;
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param name the a human-readable string describing some scope (extent) of access
|
||||
*/
|
||||
public ScopeRepresentation(String name) {
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*/
|
||||
public ScopeRepresentation() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return this.iconUri;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ScopeRepresentation scope = (ScopeRepresentation) o;
|
||||
return Objects.equals(getName(), scope.getName());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName());
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
}
|
|
@ -23,11 +23,11 @@ import java.util.List;
|
|||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.keycloak.authorization.client.Configuration;
|
||||
import org.keycloak.authorization.client.representation.ResourceRepresentation;
|
||||
import org.keycloak.authorization.client.representation.ServerConfiguration;
|
||||
import org.keycloak.authorization.client.util.Http;
|
||||
import org.keycloak.authorization.client.util.Throwables;
|
||||
import org.keycloak.authorization.client.util.TokenCallable;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
|
@ -124,11 +124,11 @@ public class ProtectedResource {
|
|||
/**
|
||||
* Query the server for a resource given its <code>name</code> where the owner is the resource server itself.
|
||||
*
|
||||
* @param id the resource name
|
||||
* @param name the resource name
|
||||
* @return a {@link ResourceRepresentation}
|
||||
*/
|
||||
public ResourceRepresentation findByName(String name) {
|
||||
String[] representations = find(null, name, null, configuration.getResource(), null, null, null, null);
|
||||
String[] representations = find(null, name, null, configuration.getResource(), null, null, false, null, null);
|
||||
|
||||
if (representations.length == 0) {
|
||||
return null;
|
||||
|
@ -145,7 +145,7 @@ public class ProtectedResource {
|
|||
* @return a {@link ResourceRepresentation}
|
||||
*/
|
||||
public ResourceRepresentation findByName(String name, String ownerId) {
|
||||
String[] representations = find(null, name, null, ownerId, null, null, null, null);
|
||||
String[] representations = find(null, name, null, ownerId, null, null, false, null, null);
|
||||
|
||||
if (representations.length == 0) {
|
||||
return null;
|
||||
|
@ -163,11 +163,12 @@ public class ProtectedResource {
|
|||
* @param owner the resource owner
|
||||
* @param type the resource type
|
||||
* @param scope the resource scope
|
||||
* @param matchingUri the resource uri. Use this parameter to lookup a resource that best match the given uri
|
||||
* @param firstResult the position of the first resource to retrieve
|
||||
* @param maxResult the maximum number of resources to retrieve
|
||||
* @return an array of strings with the resource ids
|
||||
*/
|
||||
public String[] find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final Integer firstResult, final Integer maxResult) {
|
||||
public String[] find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final boolean matchingUri, final Integer firstResult, final Integer maxResult) {
|
||||
Callable<String[]> callable = new Callable<String[]>() {
|
||||
@Override
|
||||
public String[] call() throws Exception {
|
||||
|
@ -179,6 +180,7 @@ public class ProtectedResource {
|
|||
.param("owner", owner)
|
||||
.param("type", type)
|
||||
.param("scope", scope)
|
||||
.param("matchingUri", Boolean.valueOf(matchingUri).toString())
|
||||
.param("deep", Boolean.FALSE.toString())
|
||||
.param("first", firstResult != null ? firstResult.toString() : null)
|
||||
.param("max", maxResult != null ? maxResult.toString() : null)
|
||||
|
@ -199,7 +201,7 @@ public class ProtectedResource {
|
|||
*/
|
||||
public String[] findAll() {
|
||||
try {
|
||||
return find(null,null , null, null, null, null, null, null);
|
||||
return find(null,null , null, null, null, null, false, null, null);
|
||||
} catch (Exception cause) {
|
||||
throw Throwables.handleWrapException("Could not find resource", cause);
|
||||
}
|
||||
|
@ -233,7 +235,30 @@ public class ProtectedResource {
|
|||
* @param uri the resource uri
|
||||
*/
|
||||
public List<ResourceRepresentation> findByUri(String uri) {
|
||||
String[] ids = find(null, null, uri, null, null, null, null, null);
|
||||
String[] ids = find(null, null, uri, null, null, null, false, null, null);
|
||||
|
||||
if (ids.length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<ResourceRepresentation> representations = new ArrayList<>();
|
||||
|
||||
for (String id : ids) {
|
||||
representations.add(findById(id));
|
||||
}
|
||||
|
||||
return representations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of resources that best matches the given {@code uri}. This method queries the server for resources whose
|
||||
* {@link ResourceRepresentation#uri} best matches the given {@code uri}.
|
||||
*
|
||||
* @param uri the resource uri to match
|
||||
* @return a list of resources
|
||||
*/
|
||||
public List<ResourceRepresentation> findByMatchingUri(String uri) {
|
||||
String[] ids = find(null, null, uri, null, null, null, true, null, null);
|
||||
|
||||
if (ids.length == 0) {
|
||||
return Collections.emptyList();
|
||||
|
|
|
@ -1,57 +1,44 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
* Copyright 2018 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
|
||||
* 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.
|
||||
* 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.adapters.authorization;
|
||||
package org.keycloak.common.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.representation.ResourceRepresentation;
|
||||
import org.keycloak.authorization.client.resource.ProtectedResource;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
class PathMatcher {
|
||||
public abstract class PathMatcher<P> {
|
||||
|
||||
private static final char WILDCARD = '*';
|
||||
private final AuthzClient authzClient;
|
||||
// TODO: make this configurable
|
||||
private PathCache cache = new PathCache(100, 30000);
|
||||
|
||||
public PathMatcher(AuthzClient authzClient) {
|
||||
this.authzClient = authzClient;
|
||||
}
|
||||
public P matches(final String targetUri) {
|
||||
int patternCount = 0;
|
||||
P matchingPath = null;
|
||||
P matchingAnyPath = null;
|
||||
P matchingAnySuffixPath = null;
|
||||
|
||||
public PathConfig matches(final String targetUri, Map<String, PathConfig> paths) {
|
||||
PathConfig pathConfig = paths.get(targetUri) == null ? cache.get(targetUri) : paths.get(targetUri);
|
||||
for (P entry : getPaths()) {
|
||||
String expectedUri = getPath(entry);
|
||||
|
||||
if (pathConfig != null) {
|
||||
return pathConfig;
|
||||
}
|
||||
if (expectedUri == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PathConfig matchingAnyPath = null;
|
||||
PathConfig matchingAnySuffixPath = null;
|
||||
|
||||
for (PathConfig entry : paths.values()) {
|
||||
String expectedUri = entry.getPath();
|
||||
String matchingUri = null;
|
||||
|
||||
if (exactMatch(expectedUri, targetUri, expectedUri)) {
|
||||
|
@ -62,9 +49,17 @@ class PathMatcher {
|
|||
String templateUri = buildUriFromTemplate(expectedUri, targetUri);
|
||||
|
||||
if (templateUri != null) {
|
||||
if (exactMatch(expectedUri, targetUri, templateUri)) {
|
||||
int length = expectedUri.split("\\/").length;
|
||||
|
||||
if (exactMatch(expectedUri, targetUri, templateUri) && (patternCount == 0 || length > patternCount)) {
|
||||
matchingUri = templateUri;
|
||||
entry = resolvePathConfig(entry, targetUri);
|
||||
P resolved = resolvePathConfig(entry, targetUri);
|
||||
|
||||
if (resolved != null) {
|
||||
entry = resolved;
|
||||
}
|
||||
|
||||
patternCount = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,12 +85,15 @@ class PathMatcher {
|
|||
}
|
||||
|
||||
if (matchingUri.equals(targetUri) || pathString.equals(targetUri)) {
|
||||
cache.put(targetUri, entry);
|
||||
return entry;
|
||||
if (patternCount == 0) {
|
||||
return entry;
|
||||
} else {
|
||||
matchingPath = entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (WILDCARD == expectedUri.charAt(expectedUri.length() - 1)) {
|
||||
if (matchingAnyPath == null || matchingAnyPath.getPath().length() < matchingUri.length()) {
|
||||
if (matchingAnyPath == null || getPath(matchingAnyPath).length() < matchingUri.length()) {
|
||||
matchingAnyPath = entry;
|
||||
}
|
||||
} else {
|
||||
|
@ -112,18 +110,21 @@ class PathMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
if (matchingAnySuffixPath != null) {
|
||||
cache.put(targetUri, matchingAnySuffixPath);
|
||||
return matchingAnySuffixPath;
|
||||
if (matchingPath != null) {
|
||||
return matchingPath;
|
||||
}
|
||||
|
||||
if (matchingAnyPath != null) {
|
||||
cache.put(targetUri, matchingAnyPath);
|
||||
if (matchingAnySuffixPath != null) {
|
||||
return matchingAnySuffixPath;
|
||||
}
|
||||
|
||||
return matchingAnyPath;
|
||||
}
|
||||
|
||||
protected abstract String getPath(P entry);
|
||||
|
||||
protected abstract Collection<P> getPaths();
|
||||
|
||||
private boolean exactMatch(String expectedUri, String targetUri, String value) {
|
||||
if (targetUri.equals(value)) {
|
||||
return value.equals(targetUri);
|
||||
|
@ -213,32 +214,16 @@ class PathMatcher {
|
|||
}
|
||||
|
||||
public boolean endsWithWildcard(String expectedUri) {
|
||||
return WILDCARD == expectedUri.charAt(expectedUri.length() - 1);
|
||||
int length = expectedUri.length();
|
||||
return length > 0 && WILDCARD == expectedUri.charAt(length - 1);
|
||||
}
|
||||
|
||||
private boolean isTemplate(String uri) {
|
||||
return uri.indexOf("{") != -1;
|
||||
}
|
||||
|
||||
private PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
|
||||
if (originalConfig.hasPattern()) {
|
||||
ProtectedResource resource = this.authzClient.protection().resource();
|
||||
List<ResourceRepresentation> search = resource.findByUri(path);
|
||||
|
||||
if (!search.isEmpty()) {
|
||||
// resource does exist on the server, cache it
|
||||
ResourceRepresentation targetResource = search.get(0);
|
||||
PathConfig config = PolicyEnforcer.createPathConfig(targetResource);
|
||||
|
||||
config.setScopes(originalConfig.getScopes());
|
||||
config.setMethods(originalConfig.getMethods());
|
||||
config.setParentConfig(originalConfig);
|
||||
config.setEnforcementMode(originalConfig.getEnforcementMode());
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
return originalConfig;
|
||||
protected P resolvePathConfig(P entry, String path) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
|
@ -59,21 +59,21 @@ public class AuthorizationContext {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (current != null) {
|
||||
if (current.getName().equals(resourceName)) {
|
||||
return true;
|
||||
for (Permission permission : authorization.getPermissions()) {
|
||||
if (resourceName.equalsIgnoreCase(permission.getResourceName()) || resourceName.equalsIgnoreCase(permission.getResourceId())) {
|
||||
if (scopeName == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (permission.getScopes().contains(scopeName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasResourcePermission(resourceName)) {
|
||||
for (Permission permission : authorization.getPermissions()) {
|
||||
for (PathConfig pathHolder : paths.values()) {
|
||||
if (pathHolder.getId().equals(permission.getResourceId())) {
|
||||
if (permission.getScopes().contains(scopeName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (current != null) {
|
||||
if (current.getName().equals(resourceName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,29 +81,7 @@ public class AuthorizationContext {
|
|||
}
|
||||
|
||||
public boolean hasResourcePermission(String resourceName) {
|
||||
if (this.authzToken == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Authorization authorization = this.authzToken.getAuthorization();
|
||||
|
||||
if (authorization == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current != null) {
|
||||
if (current.getName().equals(resourceName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (Permission permission : authorization.getPermissions()) {
|
||||
if (permission.getResourceName().equals(resourceName) || permission.getResourceId().equals(resourceName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return hasPermission(resourceName, null);
|
||||
}
|
||||
|
||||
public boolean hasScopePermission(String scopeName) {
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.util.List;
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -36,6 +38,9 @@ public class PolicyEnforcerConfig {
|
|||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private List<PathConfig> paths = new ArrayList<>();
|
||||
|
||||
@JsonProperty("lazy-load-paths")
|
||||
private Boolean lazyLoadPaths = Boolean.FALSE;
|
||||
|
||||
@JsonProperty("on-deny-redirect-to")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private String onDenyRedirectTo;
|
||||
|
@ -48,6 +53,14 @@ public class PolicyEnforcerConfig {
|
|||
return this.paths;
|
||||
}
|
||||
|
||||
public Boolean getLazyLoadPaths() {
|
||||
return lazyLoadPaths;
|
||||
}
|
||||
|
||||
public void setLazyLoadPaths(Boolean lazyLoadPaths) {
|
||||
this.lazyLoadPaths = lazyLoadPaths;
|
||||
}
|
||||
|
||||
public EnforcementMode getEnforcementMode() {
|
||||
return this.enforcementMode;
|
||||
}
|
||||
|
@ -78,6 +91,32 @@ public class PolicyEnforcerConfig {
|
|||
|
||||
public static class PathConfig {
|
||||
|
||||
public static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
|
||||
PathConfig pathConfig = new PathConfig();
|
||||
|
||||
pathConfig.setId(resourceDescription.getId());
|
||||
pathConfig.setName(resourceDescription.getName());
|
||||
|
||||
String uri = resourceDescription.getUri();
|
||||
|
||||
if (uri == null || "".equals(uri.trim())) {
|
||||
throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
|
||||
}
|
||||
|
||||
pathConfig.setPath(uri);
|
||||
|
||||
List<String> scopeNames = new ArrayList<>();
|
||||
|
||||
for (ScopeRepresentation scope : resourceDescription.getScopes()) {
|
||||
scopeNames.add(scope.getName());
|
||||
}
|
||||
|
||||
pathConfig.setScopes(scopeNames);
|
||||
pathConfig.setType(resourceDescription.getType());
|
||||
|
||||
return pathConfig;
|
||||
}
|
||||
|
||||
private String name;
|
||||
private String type;
|
||||
private String path;
|
||||
|
@ -219,7 +258,8 @@ public class PolicyEnforcerConfig {
|
|||
|
||||
public enum ScopeEnforcementMode {
|
||||
ALL,
|
||||
ANY
|
||||
ANY,
|
||||
DISABLED
|
||||
}
|
||||
|
||||
public static class UserManagedAccessConfig {
|
||||
|
|
|
@ -24,8 +24,10 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import org.keycloak.json.StringListMapDeserializer;
|
||||
|
||||
|
@ -45,6 +47,7 @@ public class ResourceRepresentation {
|
|||
private String uri;
|
||||
private String type;
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@JsonProperty("scopes")
|
||||
private Set<ScopeRepresentation> scopes;
|
||||
|
||||
@JsonProperty("icon_uri")
|
||||
|
@ -52,9 +55,6 @@ public class ResourceRepresentation {
|
|||
private ResourceOwnerRepresentation owner;
|
||||
private Boolean ownerManagedAccess;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private List<PolicyRepresentation> policies;
|
||||
|
||||
private String displayName;
|
||||
|
||||
@JsonDeserialize(using = StringListMapDeserializer.class)
|
||||
|
@ -162,17 +162,31 @@ public class ResourceRepresentation {
|
|||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
if (uri != null && !"".equalsIgnoreCase(uri.trim())) {
|
||||
this.uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
if (type != null && !"".equalsIgnoreCase(type.trim())) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public void setScopes(Set<ScopeRepresentation> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: This is a workaround to allow deserialization of UMA resource representation. Jackson 2.19+ support aliases, once we upgrade, change this.
|
||||
*
|
||||
* @param scopes
|
||||
*/
|
||||
@JsonSetter("resource_scopes")
|
||||
private void setScopesUma(Set<ScopeRepresentation> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
@ -181,10 +195,25 @@ public class ResourceRepresentation {
|
|||
return this.owner;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public void setOwner(ResourceOwnerRepresentation owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public void setOwner(String ownerId) {
|
||||
if (ownerId == null) {
|
||||
owner = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner == null) {
|
||||
owner = new ResourceOwnerRepresentation();
|
||||
}
|
||||
|
||||
owner.setId(ownerId);
|
||||
}
|
||||
|
||||
public Boolean getOwnerManagedAccess() {
|
||||
return ownerManagedAccess;
|
||||
}
|
||||
|
|
|
@ -181,6 +181,10 @@ public class JPAResourceStore implements ResourceStore {
|
|||
predicates.add(root.join("scopes").get("id").in(value));
|
||||
} else if ("ownerManagedAccess".equals(name)) {
|
||||
predicates.add(builder.equal(root.get(name), Boolean.valueOf(value[0])));
|
||||
} else if ("uri".equals(name)) {
|
||||
predicates.add(builder.equal(builder.lower(root.get(name)), value[0].toLowerCase()));
|
||||
} else if ("uri_not_null".equals(name)) {
|
||||
predicates.add(builder.isNotNull(root.get("uri")));
|
||||
} else {
|
||||
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
|
||||
}
|
||||
|
|
|
@ -2314,7 +2314,7 @@ public class RepresentationToModel {
|
|||
String ownerId = owner.getId();
|
||||
|
||||
if (ownerId == null) {
|
||||
throw new RuntimeException("No owner specified for resource [" + resource.getName() + "].");
|
||||
ownerId = resourceServer.getId();
|
||||
}
|
||||
|
||||
if (!resourceServer.getId().equals(ownerId)) {
|
||||
|
|
|
@ -21,6 +21,8 @@ import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
|
|||
import static org.keycloak.models.utils.RepresentationToModel.toModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -46,6 +48,7 @@ import javax.ws.rs.core.Response.Status;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
|
@ -54,6 +57,7 @@ import org.keycloak.authorization.model.Scope;
|
|||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.common.util.PathMatcher;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -64,7 +68,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||
|
||||
|
@ -90,32 +94,18 @@ public class ResourceSetService {
|
|||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource) {
|
||||
return create(uriInfo, resource, (Function<Resource, ResourceRepresentation>) resource1 -> {
|
||||
ResourceRepresentation representation = new ResourceRepresentation();
|
||||
if (resource == null) {
|
||||
return Response.status(Status.BAD_REQUEST).build();
|
||||
}
|
||||
|
||||
representation.setId(resource1.getId());
|
||||
ResourceRepresentation newResource = create(resource);
|
||||
|
||||
return representation;
|
||||
});
|
||||
}
|
||||
|
||||
public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
|
||||
Response response = create(resource, toRepresentation);
|
||||
audit(uriInfo, resource, resource.getId(), OperationType.CREATE);
|
||||
return response;
|
||||
|
||||
return Response.status(Status.CREATED).entity(newResource).build();
|
||||
}
|
||||
|
||||
public Response create(ResourceRepresentation resource) {
|
||||
return create(resource, (Function<Resource, ResourceRepresentation>) resource1 -> {
|
||||
ResourceRepresentation representation = new ResourceRepresentation();
|
||||
|
||||
representation.setId(resource1.getId());
|
||||
|
||||
return representation;
|
||||
});
|
||||
}
|
||||
|
||||
public Response create(ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
|
||||
public ResourceRepresentation create(ResourceRepresentation resource) {
|
||||
requireManage();
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
ResourceOwnerRepresentation owner = resource.getOwner();
|
||||
|
@ -123,21 +113,22 @@ public class ResourceSetService {
|
|||
if (owner == null) {
|
||||
owner = new ResourceOwnerRepresentation();
|
||||
owner.setId(resourceServer.getId());
|
||||
resource.setOwner(owner);
|
||||
}
|
||||
|
||||
String ownerId = owner.getId();
|
||||
|
||||
if (ownerId == null) {
|
||||
return ErrorResponse.error("You must specify the resource owner.", Status.BAD_REQUEST);
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "You must specify the resource owner.", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), ownerId, this.resourceServer.getId());
|
||||
|
||||
if (existingResource != null) {
|
||||
return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists.");
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Resource with name [" + resource.getName() + "] already exists.", Status.CONFLICT);
|
||||
}
|
||||
|
||||
return Response.status(Status.CREATED).entity(toRepresentation.apply(toModel(resource, this.resourceServer, authorization))).build();
|
||||
return toRepresentation(toModel(resource, this.resourceServer, authorization), resourceServer, authorization);
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
|
@ -198,10 +189,10 @@ public class ResourceSetService {
|
|||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response findById(@PathParam("id") String id) {
|
||||
return findById(id, (Function<Resource, ResourceRepresentation>) resource -> toRepresentation(resource, resourceServer, authorization, true));
|
||||
return findById(id, resource -> toRepresentation(resource, resourceServer, authorization, true));
|
||||
}
|
||||
|
||||
public Response findById(@PathParam("id") String id, Function<Resource, ?> toRepresentation) {
|
||||
public Response findById(String id, Function<Resource, ? extends ResourceRepresentation> toRepresentation) {
|
||||
requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
|
||||
|
@ -340,10 +331,11 @@ public class ResourceSetService {
|
|||
@QueryParam("owner") String owner,
|
||||
@QueryParam("type") String type,
|
||||
@QueryParam("scope") String scope,
|
||||
@QueryParam("matchingUri") Boolean matchingUri,
|
||||
@QueryParam("deep") Boolean deep,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResult) {
|
||||
return find(id, name, uri, owner, type, scope, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1));
|
||||
return find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1));
|
||||
}
|
||||
|
||||
public Response find(@QueryParam("_id") String id,
|
||||
|
@ -352,6 +344,7 @@ public class ResourceSetService {
|
|||
@QueryParam("owner") String owner,
|
||||
@QueryParam("type") String type,
|
||||
@QueryParam("scope") String scope,
|
||||
@QueryParam("matchingUri") Boolean matchingUri,
|
||||
@QueryParam("deep") Boolean deep,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResult,
|
||||
|
@ -413,9 +406,38 @@ public class ResourceSetService {
|
|||
search.put("scope", scopes.stream().map(Scope::getId).toArray(String[]::new));
|
||||
}
|
||||
|
||||
List<Resource> resources = storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS);
|
||||
|
||||
if (matchingUri != null && matchingUri && resources.isEmpty()) {
|
||||
HashMap<String, String[]> attributes = new HashMap<>();
|
||||
|
||||
attributes.put("uri_not_null", new String[] {"true"});
|
||||
attributes.put("owner", new String[] {resourceServer.getId()});
|
||||
|
||||
List<Resource> serverResources = storeFactory.getResourceStore().findByResourceServer(attributes, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS);
|
||||
PathMatcher<Resource> pathMatcher = new PathMatcher<Resource>() {
|
||||
@Override
|
||||
protected String getPath(Resource entry) {
|
||||
return entry.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Resource> getPaths() {
|
||||
return serverResources;
|
||||
}
|
||||
};
|
||||
|
||||
Resource matches = pathMatcher.matches(uri);
|
||||
|
||||
if (matches != null) {
|
||||
resources = Arrays.asList(matches);
|
||||
}
|
||||
}
|
||||
|
||||
Boolean finalDeep = deep;
|
||||
|
||||
return Response.ok(
|
||||
storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
||||
resources.stream()
|
||||
.map(resource -> toRepresentation.apply(resource, finalDeep))
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
|
@ -437,7 +459,7 @@ public class ResourceSetService {
|
|||
audit(uriInfo, resource, null, operation);
|
||||
}
|
||||
|
||||
private void audit(@Context UriInfo uriInfo, ResourceRepresentation resource, String id, OperationType operation) {
|
||||
public void audit(@Context UriInfo uriInfo, ResourceRepresentation resource, String id, OperationType operation) {
|
||||
if (authorization.getRealm().isAdminEventsEnabled()) {
|
||||
if (id != null) {
|
||||
adminEvent.operation(operation).resourcePath(uriInfo, id).representation(resource).success();
|
||||
|
|
|
@ -65,7 +65,7 @@ public class ProtectionService {
|
|||
|
||||
ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
|
||||
|
||||
ResourceService resource = new ResourceService(resourceServer, identity, resourceManager, this.authorization);
|
||||
ResourceService resource = new ResourceService(resourceServer, identity, resourceManager);
|
||||
|
||||
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2016 Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.authorization.protection.resource;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
import org.keycloak.authorization.protection.resource.representation.UmaResourceRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class RegistrationResponse {
|
||||
|
||||
private final UmaResourceRepresentation resourceDescription;
|
||||
|
||||
public RegistrationResponse(UmaResourceRepresentation resourceDescription) {
|
||||
this.resourceDescription = resourceDescription;
|
||||
}
|
||||
|
||||
public RegistrationResponse() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
@JsonUnwrapped
|
||||
public UmaResourceRepresentation getResourceDescription() {
|
||||
return this.resourceDescription;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
if (this.resourceDescription != null) {
|
||||
return this.resourceDescription.getId();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -18,8 +18,6 @@
|
|||
package org.keycloak.authorization.protection.resource;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
|
@ -36,16 +34,13 @@ import javax.ws.rs.core.Response.Status;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.admin.ResourceSetService;
|
||||
import org.keycloak.authorization.identity.Identity;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.protection.resource.representation.UmaResourceRepresentation;
|
||||
import org.keycloak.authorization.protection.resource.representation.UmaScopeRepresentation;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
|
||||
/**
|
||||
|
@ -56,39 +51,51 @@ public class ResourceService {
|
|||
private final ResourceServer resourceServer;
|
||||
private final ResourceSetService resourceManager;
|
||||
private final Identity identity;
|
||||
private final AuthorizationProvider authorization;
|
||||
|
||||
public ResourceService(ResourceServer resourceServer, Identity identity, ResourceSetService resourceManager, AuthorizationProvider authorization) {
|
||||
public ResourceService(ResourceServer resourceServer, Identity identity, ResourceSetService resourceManager) {
|
||||
this.identity = identity;
|
||||
this.resourceServer = resourceServer;
|
||||
this.resourceManager = resourceManager;
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
public Response create(@Context UriInfo uriInfo, UmaResourceRepresentation umaResource) {
|
||||
public Response create(@Context UriInfo uriInfo, UmaResourceRepresentation resource) {
|
||||
checkResourceServerSettings();
|
||||
if (umaResource == null) {
|
||||
|
||||
if (resource == null) {
|
||||
return Response.status(Status.BAD_REQUEST).build();
|
||||
}
|
||||
return this.resourceManager.create(uriInfo, toResourceRepresentation(umaResource), (Function<Resource, UmaResourceRepresentation>) this::toUmaRepresentation);
|
||||
|
||||
ResourceOwnerRepresentation owner = resource.getOwner();
|
||||
|
||||
if (owner == null) {
|
||||
owner = new ResourceOwnerRepresentation();
|
||||
resource.setOwner(owner);
|
||||
}
|
||||
|
||||
String ownerId = owner.getId();
|
||||
|
||||
if (ownerId == null) {
|
||||
ownerId = this.identity.getId();
|
||||
}
|
||||
|
||||
owner.setId(ownerId);
|
||||
|
||||
ResourceRepresentation newResource = resourceManager.create(resource);
|
||||
|
||||
resourceManager.audit(uriInfo, resource, resource.getId(), OperationType.CREATE);
|
||||
|
||||
return Response.status(Status.CREATED).entity(new UmaResourceRepresentation(newResource)).build();
|
||||
}
|
||||
|
||||
@Path("{id}")
|
||||
@PUT
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, UmaResourceRepresentation representation) {
|
||||
ResourceRepresentation resource = toResourceRepresentation(representation);
|
||||
Response response = this.resourceManager.update(uriInfo, id, resource);
|
||||
|
||||
if (response.getEntity() instanceof ResourceRepresentation) {
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
return response;
|
||||
public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, ResourceRepresentation resource) {
|
||||
return this.resourceManager.update(uriInfo, id, resource);
|
||||
}
|
||||
|
||||
@Path("/{id}")
|
||||
|
@ -102,7 +109,7 @@ public class ResourceService {
|
|||
@GET
|
||||
@Produces("application/json")
|
||||
public Response findById(@PathParam("id") String id) {
|
||||
return this.resourceManager.findById(id, (Function<Resource, UmaResourceRepresentation>) resource -> toUmaRepresentation(resource));
|
||||
return this.resourceManager.findById(id, UmaResourceRepresentation::new);
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -114,75 +121,11 @@ public class ResourceService {
|
|||
@QueryParam("owner") String owner,
|
||||
@QueryParam("type") String type,
|
||||
@QueryParam("scope") String scope,
|
||||
@QueryParam("matchingUri") Boolean matchingUri,
|
||||
@QueryParam("deep") Boolean deep,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResult) {
|
||||
return resourceManager.find(id, name, uri, owner, type, scope, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId());
|
||||
}
|
||||
|
||||
private ResourceRepresentation toResourceRepresentation(UmaResourceRepresentation umaResource) {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setId(umaResource.getId());
|
||||
resource.setIconUri(umaResource.getIconUri());
|
||||
resource.setName(umaResource.getName());
|
||||
resource.setUri(umaResource.getUri());
|
||||
resource.setType(umaResource.getType());
|
||||
resource.setOwnerManagedAccess(umaResource.getOwnerManagedAccess());
|
||||
|
||||
ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
|
||||
String ownerId = umaResource.getOwner();
|
||||
|
||||
if (ownerId == null) {
|
||||
ownerId = this.identity.getId();
|
||||
}
|
||||
|
||||
owner.setId(ownerId);
|
||||
resource.setOwner(owner);
|
||||
|
||||
resource.setScopes(umaResource.getScopes().stream().map(representation -> {
|
||||
ScopeRepresentation scopeRepresentation = new ScopeRepresentation();
|
||||
|
||||
scopeRepresentation.setId(representation.getId());
|
||||
scopeRepresentation.setName(representation.getName());
|
||||
scopeRepresentation.setIconUri(representation.getIconUri());
|
||||
|
||||
return scopeRepresentation;
|
||||
}).collect(Collectors.toSet()));
|
||||
|
||||
resource.setAttributes(umaResource.getAttributes());
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
private UmaResourceRepresentation toUmaRepresentation(Resource model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UmaResourceRepresentation resource = new UmaResourceRepresentation();
|
||||
|
||||
resource.setId(model.getId());
|
||||
resource.setIconUri(model.getIconUri());
|
||||
resource.setName(model.getName());
|
||||
resource.setUri(model.getUri());
|
||||
resource.setType(model.getType());
|
||||
|
||||
if (model.getOwner() != null) {
|
||||
resource.setOwner(model.getOwner());
|
||||
}
|
||||
|
||||
resource.setScopes(model.getScopes().stream().map(scopeRepresentation -> {
|
||||
UmaScopeRepresentation umaScopeRep = new UmaScopeRepresentation();
|
||||
umaScopeRep.setId(scopeRepresentation.getId());
|
||||
umaScopeRep.setName(scopeRepresentation.getName());
|
||||
umaScopeRep.setIconUri(scopeRepresentation.getIconUri());
|
||||
return umaScopeRep;
|
||||
}).collect(Collectors.toSet()));
|
||||
|
||||
resource.setAttributes(model.getAttributes());
|
||||
|
||||
return resource;
|
||||
return resourceManager.find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId());
|
||||
}
|
||||
|
||||
private void checkResourceServerSettings() {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2018 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.authorization.protection.resource;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class UmaResourceRepresentation extends ResourceRepresentation {
|
||||
|
||||
public UmaResourceRepresentation() {
|
||||
|
||||
}
|
||||
|
||||
public UmaResourceRepresentation(ResourceRepresentation resource) {
|
||||
setId(resource.getId());
|
||||
setName(resource.getName());
|
||||
setType(resource.getType());
|
||||
setUri(resource.getUri());
|
||||
setIconUri(resource.getIconUri());
|
||||
setOwner(resource.getOwner());
|
||||
setScopes(resource.getScopes());
|
||||
setDisplayName(resource.getDisplayName());
|
||||
setOwnerManagedAccess(resource.getOwnerManagedAccess());
|
||||
}
|
||||
|
||||
public UmaResourceRepresentation(Resource resource) {
|
||||
setId(resource.getId());
|
||||
setName(resource.getName());
|
||||
setType(resource.getType());
|
||||
setUri(resource.getUri());
|
||||
setIconUri(resource.getIconUri());
|
||||
setOwner(resource.getOwner());
|
||||
setScopes(resource.getScopes().stream().map(scope -> new ScopeRepresentation(scope.getName())).collect(Collectors.toSet()));
|
||||
setDisplayName(resource.getDisplayName());
|
||||
setOwnerManagedAccess(resource.isOwnerManagedAccess());
|
||||
setAttributes(resource.getAttributes());
|
||||
}
|
||||
|
||||
@JsonProperty("resource_scopes")
|
||||
@Override
|
||||
public Set<ScopeRepresentation> getScopes() {
|
||||
return super.getScopes();
|
||||
}
|
||||
|
||||
@JsonProperty("resource_scopes")
|
||||
@Override
|
||||
public void setScopes(Set<ScopeRepresentation> scopes) {
|
||||
super.setScopes(scopes);
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2016 Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.authorization.protection.resource.representation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class RegistrationResponse {
|
||||
|
||||
private final UmaResourceRepresentation resourceDescription;
|
||||
|
||||
public RegistrationResponse(UmaResourceRepresentation resourceDescription) {
|
||||
this.resourceDescription = resourceDescription;
|
||||
}
|
||||
|
||||
public RegistrationResponse() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
@JsonUnwrapped
|
||||
public UmaResourceRepresentation getResourceDescription() {
|
||||
return this.resourceDescription;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
if (this.resourceDescription != null) {
|
||||
return this.resourceDescription.getId();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2016 Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.authorization.protection.resource.representation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>One or more resources that the resource server manages as a set of protected resources.
|
||||
*
|
||||
* <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.2">OAuth-resource-reg</a>.
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class UmaResourceRepresentation {
|
||||
|
||||
@JsonProperty("_id")
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
private String uri;
|
||||
private String type;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@JsonProperty("resource_scopes")
|
||||
private Set<UmaScopeRepresentation> scopes;
|
||||
|
||||
@JsonProperty("icon_uri")
|
||||
private String iconUri;
|
||||
private String owner;
|
||||
private Boolean ownerManagedAccess;
|
||||
|
||||
private Map<String, List<String>> attributes;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param name a human-readable string describing a set of one or more resources
|
||||
* @param uri a {@link URI} that provides the network location for the resource set being registered
|
||||
* @param type a string uniquely identifying the semantics of the resource set
|
||||
* @param scopes the available scopes for this resource set
|
||||
* @param iconUri a {@link URI} for a graphic icon representing the resource set
|
||||
*/
|
||||
public UmaResourceRepresentation(String name, Set<UmaScopeRepresentation> scopes, String uri, String type, String iconUri) {
|
||||
this.name = name;
|
||||
this.scopes = scopes;
|
||||
this.uri = uri;
|
||||
this.type = type;
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param name a human-readable string describing a set of one or more resources
|
||||
* @param uri a {@link URI} that provides the network location for the resource set being registered
|
||||
* @param type a string uniquely identifying the semantics of the resource set
|
||||
* @param scopes the available scopes for this resource set
|
||||
*/
|
||||
public UmaResourceRepresentation(String name, Set<UmaScopeRepresentation> scopes, String uri, String type) {
|
||||
this(name, scopes, uri, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param name a human-readable string describing a set of one or more resources
|
||||
* @param serverUri a {@link URI} that identifies this resource server
|
||||
* @param scopes the available scopes for this resource set
|
||||
*/
|
||||
public UmaResourceRepresentation(String name, Set<UmaScopeRepresentation> scopes) {
|
||||
this(name, scopes, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
*/
|
||||
public UmaResourceRepresentation() {
|
||||
this(null, null, null, null, null);
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public Set<UmaScopeRepresentation> getScopes() {
|
||||
return Collections.unmodifiableSet(this.scopes);
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return this.iconUri;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setScopes(Set<UmaScopeRepresentation> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public void setOwnerManagedAccess(Boolean ownerManagedAccess) {
|
||||
this.ownerManagedAccess = ownerManagedAccess;
|
||||
}
|
||||
|
||||
public Boolean getOwnerManagedAccess() {
|
||||
return ownerManagedAccess;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, List<String>> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2016 Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.authorization.protection.resource.representation;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>A bounded extent of access that is possible to perform on a resource set. In authorization policy terminology,
|
||||
* a scope is one of the potentially many "verbs" that can logically apply to a resource set ("object").
|
||||
*
|
||||
* <p>For more details, <a href="https://docs.kantarainitiative.org/uma/draft-oauth-resource-reg.html#rfc.section.2.1">OAuth-resource-reg</a>.
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class UmaScopeRepresentation {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String iconUri;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param name the a human-readable string describing some scope (extent) of access
|
||||
* @param iconUri a {@link URI} for a graphic icon representing the scope
|
||||
*/
|
||||
public UmaScopeRepresentation(String name, String iconUri) {
|
||||
this.name = name;
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param name the a human-readable string describing some scope (extent) of access
|
||||
*/
|
||||
public UmaScopeRepresentation(String name) {
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*/
|
||||
public UmaScopeRepresentation() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return this.iconUri;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UmaScopeRepresentation scope = (UmaScopeRepresentation) o;
|
||||
return Objects.equals(getName(), scope.getName());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName());
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
}
|
|
@ -68,6 +68,7 @@ import org.keycloak.representations.idm.ScopeMappingRepresentation;
|
|||
import org.keycloak.representations.idm.UserConsentRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
@ -315,7 +316,7 @@ public class ExportUtils {
|
|||
ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization);
|
||||
|
||||
if (rep.getOwner().getId().equals(settingsModel.getId())) {
|
||||
rep.setOwner(null);
|
||||
rep.setOwner((ResourceOwnerRepresentation) null);
|
||||
} else {
|
||||
rep.getOwner().setId(null);
|
||||
}
|
||||
|
|
|
@ -155,6 +155,7 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
|
|||
PolicyEnforcerConfig enforcerConfig = new PolicyEnforcerConfig();
|
||||
|
||||
enforcerConfig.setEnforcementMode(null);
|
||||
enforcerConfig.setLazyLoadPaths(null);
|
||||
|
||||
rep.setEnforcerConfig(enforcerConfig);
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"realm": "photoz",
|
||||
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url": "http://localhost:8180/auth",
|
||||
"ssl-required": "external",
|
||||
"resource": "photoz-restful-api",
|
||||
"bearer-only" : true,
|
||||
"credentials": {
|
||||
"jwt": {
|
||||
"client-key-password": "password",
|
||||
"client-keystore-file": "classpath:keystore.jks",
|
||||
"client-keystore-password": "password",
|
||||
"client-key-alias": "secure-portal",
|
||||
"token-timeout": 10,
|
||||
"client-keystore-type": "jks"
|
||||
}
|
||||
},
|
||||
"policy-enforcer": {
|
||||
"enforcement-mode": "PERMISSIVE",
|
||||
"user-managed-access": {},
|
||||
"lazy-load-paths": true,
|
||||
"paths": [
|
||||
{
|
||||
"name" : "Album Resource",
|
||||
"path" : "/album",
|
||||
"methods" : [
|
||||
{
|
||||
"method": "GET",
|
||||
"scopes-enforcement-mode" : "DISABLED"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "Album Resource",
|
||||
"path" : "/album/{id}",
|
||||
"methods" : [
|
||||
{
|
||||
"method": "DELETE",
|
||||
"scopes" : ["album:delete"]
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"scopes" : ["album:view"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path" : "/profile"
|
||||
},
|
||||
{
|
||||
"name" : "Admin Resources",
|
||||
"path" : "/admin/*"
|
||||
},
|
||||
{
|
||||
"name" : "Scope Protected Resource",
|
||||
"path" : "/scope-any",
|
||||
"methods": [
|
||||
{
|
||||
"method": "GET",
|
||||
"scopes": ["scope-a", "scope-b"],
|
||||
"scopes-enforcement-mode": "ANY"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "Scope Protected Resource",
|
||||
"path" : "/scope-all",
|
||||
"methods": [
|
||||
{
|
||||
"method": "GET",
|
||||
"scopes": ["scope-a", "scope-b"],
|
||||
"scopes-enforcement-mode": "ALL"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -3,8 +3,8 @@ package org.keycloak.example.photoz.album;
|
|||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.ClientAuthorizationContext;
|
||||
import org.keycloak.authorization.client.representation.ResourceRepresentation;
|
||||
import org.keycloak.authorization.client.representation.ScopeRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.authorization.client.resource.ProtectionResource;
|
||||
import org.keycloak.example.photoz.entity.Album;
|
||||
import org.keycloak.example.photoz.util.Transaction;
|
||||
|
|
|
@ -19,6 +19,16 @@
|
|||
"enforcement-mode": "PERMISSIVE",
|
||||
"user-managed-access": {},
|
||||
"paths": [
|
||||
{
|
||||
"name" : "Album Resource",
|
||||
"path" : "/album",
|
||||
"methods" : [
|
||||
{
|
||||
"method": "GET",
|
||||
"scopes-enforcement-mode" : "DISABLED"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "Album Resource",
|
||||
"path" : "/album/{id}",
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"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",
|
||||
"lazy-load-paths": true
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
},
|
||||
"policy-enforcer": {
|
||||
"on-deny-redirect-to": "/servlet-policy-enforcer/denied.jsp",
|
||||
"lazy-load-paths": false,
|
||||
"paths": [
|
||||
{
|
||||
"name": "Welcome Resource",
|
||||
|
@ -31,7 +32,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Pattern 5",
|
||||
"path": "/resource/{pattern}/resource-d"
|
||||
"path": "/a/{pattern}/resource-d"
|
||||
},
|
||||
{
|
||||
"name": "Pattern 6",
|
||||
|
|
|
@ -203,10 +203,16 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
|
|||
|
||||
this.loginPage.form().login(username, password);
|
||||
waitForPageToLoad();//guess
|
||||
|
||||
// simple check if we are at the consent page, if so just click 'Yes'
|
||||
if (this.consentPage.isCurrent()) {
|
||||
consentPage.confirm();
|
||||
|
||||
try {
|
||||
if (!isCurrent()) {
|
||||
// simple check if we are at the consent page, if so just click 'Yes'
|
||||
if (this.consentPage.isCurrent()) {
|
||||
consentPage.confirm();
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// ignore errors when checking consent page, if an error tests will also fail
|
||||
}
|
||||
|
||||
pause(WAIT_AFTER_OPERATION);
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright 2018 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 static org.junit.Assert.assertFalse;
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadJson;
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployer;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.junit.BeforeClass;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractBaseServletAuthzAdapterTest extends AbstractExampleAdapterTest {
|
||||
|
||||
protected static final String REALM_NAME = "servlet-authz";
|
||||
protected static final String RESOURCE_SERVER_ID = "servlet-authz-app";
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() { ProfileAssume.assumePreview(); }
|
||||
|
||||
@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")));
|
||||
}
|
||||
|
||||
protected void performTests(ExceptionRunnable assertion) {
|
||||
performTests(() -> importResourceServerSettings(), assertion);
|
||||
}
|
||||
|
||||
protected void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) {
|
||||
try {
|
||||
beforeDeploy.run();
|
||||
deployer.deploy(RESOURCE_SERVER_ID);
|
||||
assertion.run();
|
||||
} catch (FileNotFoundException cause) {
|
||||
throw new RuntimeException("Failed to import authorization settings", cause);
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Error while executing tests", cause);
|
||||
} finally {
|
||||
deployer.undeploy(RESOURCE_SERVER_ID);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasLink(String text) {
|
||||
return getLink(text) != null;
|
||||
}
|
||||
|
||||
protected boolean hasText(String text) {
|
||||
return this.driver.getPageSource().contains(text);
|
||||
}
|
||||
|
||||
private WebElement getLink(String text) {
|
||||
return this.driver.findElement(By.xpath("//a[text() = '" + text + "']"));
|
||||
}
|
||||
|
||||
protected 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));
|
||||
}
|
||||
|
||||
protected AuthorizationResource getAuthorizationResource() {
|
||||
return getClientResource(RESOURCE_SERVER_ID).authorization();
|
||||
}
|
||||
|
||||
protected 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);
|
||||
}
|
||||
|
||||
protected void login(String username, String password) {
|
||||
try {
|
||||
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);
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Login failed", cause);
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateTo() {
|
||||
this.driver.navigate().to(getResourceServerUrl());
|
||||
WaitUtils.waitUntilElement(By.xpath("//a[text() = 'Dynamic Menu']"));
|
||||
}
|
||||
|
||||
protected boolean wasDenied() {
|
||||
return this.driver.getPageSource().contains("You can not access this resource.");
|
||||
}
|
||||
|
||||
protected URL getResourceServerUrl() {
|
||||
try {
|
||||
return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException("Could not obtain resource server url.", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void navigateToDynamicMenuPage() {
|
||||
navigateTo();
|
||||
getLink("Dynamic Menu").click();
|
||||
}
|
||||
|
||||
protected void navigateToUserPremiumPage() {
|
||||
navigateTo();
|
||||
getLink("User Premium").click();
|
||||
}
|
||||
|
||||
protected void navigateToAdminPage() {
|
||||
navigateTo();
|
||||
getLink("Administration").click();
|
||||
}
|
||||
|
||||
protected void updatePermissionPolicies(String permissionName, String... policyNames) {
|
||||
for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
|
||||
if (permissionName.equalsIgnoreCase(policy.getName())) {
|
||||
StringBuilder policies = new StringBuilder("[");
|
||||
|
||||
for (String policyName : policyNames) {
|
||||
if (policies.length() > 1) {
|
||||
policies.append(",");
|
||||
}
|
||||
policies.append("\"").append(policyName).append("\"");
|
||||
|
||||
}
|
||||
|
||||
policies.append("]");
|
||||
|
||||
policy.getConfig().put("applyPolicies", policies.toString());
|
||||
getAuthorizationResource().policies().policy(policy.getId()).update(policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void createUserPolicy(String name, String... userNames) {
|
||||
UserPolicyRepresentation policy = new UserPolicyRepresentation();
|
||||
|
||||
policy.setName(name);
|
||||
|
||||
for (String userName : userNames) {
|
||||
policy.addUser(userName);
|
||||
}
|
||||
|
||||
assertFalse(policy.getUsers().isEmpty());
|
||||
|
||||
Response response = getAuthorizationResource().policies().user().create(policy);
|
||||
response.close();
|
||||
}
|
||||
|
||||
protected interface ExceptionRunnable {
|
||||
void run() throws Exception;
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ import org.junit.Test;
|
|||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractPermissiveModeAdapterTest extends AbstractServletAuthzAdapterTest {
|
||||
public abstract class AbstractPermissiveModeAdapterTest extends AbstractBaseServletAuthzAdapterTest {
|
||||
|
||||
@Deployment(name = RESOURCE_SERVER_ID, managed = false)
|
||||
public static WebArchive deployment() throws IOException {
|
||||
|
|
|
@ -75,7 +75,7 @@ import org.keycloak.util.JsonSerialization;
|
|||
public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAdapterTest {
|
||||
|
||||
private static final String REALM_NAME = "photoz";
|
||||
private static final String RESOURCE_SERVER_ID = "photoz-restful-api";
|
||||
protected static final String RESOURCE_SERVER_ID = "photoz-restful-api";
|
||||
private static final int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
|
||||
|
||||
@ArquillianResource
|
||||
|
@ -118,16 +118,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
|||
testRealms.add(realm);
|
||||
}
|
||||
|
||||
@Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
|
||||
public static WebArchive deploymentClient() throws IOException {
|
||||
return exampleDeployment(PhotozClientAuthzTestApp.DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
@Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
|
||||
public static WebArchive deploymentResourceServer() throws IOException {
|
||||
return exampleDeployment(RESOURCE_SERVER_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeAbstractKeycloakTest() throws Exception {
|
||||
super.beforeAbstractKeycloakTest();
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2018 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 static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
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.waitUntilElement;
|
||||
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.LaxRedirectStrategy;
|
||||
import org.jboss.arquillian.container.test.api.Deployer;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
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;
|
||||
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.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.adapter.page.PhotozClientAuthzTestApp;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractPhotozExampleLazyLoadPathsAdapterTest extends AbstractPhotozExampleAdapterTest {
|
||||
|
||||
@Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
|
||||
public static WebArchive deploymentClient() throws IOException {
|
||||
return exampleDeployment(PhotozClientAuthzTestApp.DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
@Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
|
||||
public static WebArchive deploymentResourceServer() throws IOException {
|
||||
return exampleDeployment(RESOURCE_SERVER_ID)
|
||||
.addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/photoz/keycloak-lazy-load-path-authz-service.json"), "keycloak.json");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2018 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 java.io.IOException;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.keycloak.testsuite.adapter.page.PhotozClientAuthzTestApp;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractPhotozExampleNoLazyLoadPathsAdapterTest extends AbstractPhotozExampleAdapterTest {
|
||||
|
||||
@Deployment(name = PhotozClientAuthzTestApp.DEPLOYMENT_NAME)
|
||||
public static WebArchive deploymentClient() throws IOException {
|
||||
return exampleDeployment(PhotozClientAuthzTestApp.DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
@Deployment(name = RESOURCE_SERVER_ID, managed = false, testable = false)
|
||||
public static WebArchive deploymentResourceServer() throws IOException {
|
||||
return exampleDeployment(RESOURCE_SERVER_ID);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* Copyright 2018 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");
|
||||
|
@ -16,196 +16,19 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.adapter.example.authorization;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadJson;
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployer;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.junit.BeforeClass;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAdapterTest {
|
||||
public abstract class AbstractServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
|
||||
|
||||
protected static final String REALM_NAME = "servlet-authz";
|
||||
protected static final String RESOURCE_SERVER_ID = "servlet-authz-app";
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() { ProfileAssume.assumePreview(); }
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
protected void performTests(ExceptionRunnable assertion) {
|
||||
performTests(() -> importResourceServerSettings(), assertion);
|
||||
}
|
||||
|
||||
protected void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) {
|
||||
try {
|
||||
beforeDeploy.run();
|
||||
deployer.deploy(RESOURCE_SERVER_ID);
|
||||
assertion.run();
|
||||
} catch (FileNotFoundException cause) {
|
||||
throw new RuntimeException("Failed to import authorization settings", cause);
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Error while executing tests", cause);
|
||||
} finally {
|
||||
deployer.undeploy(RESOURCE_SERVER_ID);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasLink(String text) {
|
||||
return getLink(text) != null;
|
||||
}
|
||||
|
||||
protected boolean hasText(String text) {
|
||||
return this.driver.getPageSource().contains(text);
|
||||
}
|
||||
|
||||
private WebElement getLink(String text) {
|
||||
return this.driver.findElement(By.xpath("//a[text() = '" + text + "']"));
|
||||
}
|
||||
|
||||
protected 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));
|
||||
}
|
||||
|
||||
protected AuthorizationResource getAuthorizationResource() {
|
||||
return getClientResource(RESOURCE_SERVER_ID).authorization();
|
||||
}
|
||||
|
||||
protected 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);
|
||||
}
|
||||
|
||||
protected void login(String username, String password) {
|
||||
try {
|
||||
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);
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Login failed", cause);
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateTo() {
|
||||
this.driver.navigate().to(getResourceServerUrl());
|
||||
WaitUtils.waitUntilElement(By.xpath("//a[text() = 'Dynamic Menu']"));
|
||||
}
|
||||
|
||||
protected boolean wasDenied() {
|
||||
return this.driver.getPageSource().contains("You can not access this resource.");
|
||||
}
|
||||
|
||||
protected URL getResourceServerUrl() {
|
||||
try {
|
||||
return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException("Could not obtain resource server url.", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void navigateToDynamicMenuPage() {
|
||||
navigateTo();
|
||||
getLink("Dynamic Menu").click();
|
||||
}
|
||||
|
||||
protected void navigateToUserPremiumPage() {
|
||||
navigateTo();
|
||||
getLink("User Premium").click();
|
||||
}
|
||||
|
||||
protected void navigateToAdminPage() {
|
||||
navigateTo();
|
||||
getLink("Administration").click();
|
||||
}
|
||||
|
||||
protected void updatePermissionPolicies(String permissionName, String... policyNames) {
|
||||
for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
|
||||
if (permissionName.equalsIgnoreCase(policy.getName())) {
|
||||
StringBuilder policies = new StringBuilder("[");
|
||||
|
||||
for (String policyName : policyNames) {
|
||||
if (policies.length() > 1) {
|
||||
policies.append(",");
|
||||
}
|
||||
policies.append("\"").append(policyName).append("\"");
|
||||
|
||||
}
|
||||
|
||||
policies.append("]");
|
||||
|
||||
policy.getConfig().put("applyPolicies", policies.toString());
|
||||
getAuthorizationResource().policies().policy(policy.getId()).update(policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void createUserPolicy(String name, String... userNames) {
|
||||
UserPolicyRepresentation policy = new UserPolicyRepresentation();
|
||||
|
||||
policy.setName(name);
|
||||
|
||||
for (String userName : userNames) {
|
||||
policy.addUser(userName);
|
||||
}
|
||||
|
||||
assertFalse(policy.getUsers().isEmpty());
|
||||
|
||||
Response response = getAuthorizationResource().policies().user().create(policy);
|
||||
response.close();
|
||||
}
|
||||
|
||||
protected interface ExceptionRunnable {
|
||||
void run() throws Exception;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,14 +19,11 @@ package org.keycloak.testsuite.adapter.example.authorization;
|
|||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.ClientPoliciesResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
|
@ -46,12 +43,7 @@ import org.keycloak.testsuite.util.WaitUtils;
|
|||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractServletAuthzFunctionalAdapterTest extends AbstractServletAuthzAdapterTest {
|
||||
|
||||
@Deployment(name = RESOURCE_SERVER_ID, managed = false)
|
||||
public static WebArchive deployment() throws IOException {
|
||||
return exampleDeployment(RESOURCE_SERVER_ID);
|
||||
}
|
||||
public abstract class AbstractServletAuthzFunctionalAdapterTest extends AbstractBaseServletAuthzAdapterTest {
|
||||
|
||||
@Test
|
||||
public void testCanNotAccessWhenEnforcing() throws Exception {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2018 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 static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.ClientPoliciesResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.ResourcesResource;
|
||||
import org.keycloak.admin.client.resource.RolePoliciesResource;
|
||||
import org.keycloak.admin.client.resource.RoleScopeResource;
|
||||
import org.keycloak.admin.client.resource.RolesResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractServletAuthzLazyLoadPathsAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
|
||||
|
||||
@Deployment(name = RESOURCE_SERVER_ID, managed = false)
|
||||
public static WebArchive deployment() throws IOException {
|
||||
return exampleDeployment(RESOURCE_SERVER_ID)
|
||||
.addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/keycloak-lazy-load-authz-service.json"), "keycloak.json");
|
||||
}
|
||||
|
||||
}
|
|
@ -177,21 +177,21 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
|
|||
performTests(() -> {
|
||||
login("alice", "alice");
|
||||
|
||||
navigateTo("/resource/a/resource-d");
|
||||
navigateTo("/a/a/resource-d");
|
||||
assertFalse(wasDenied());
|
||||
navigateTo("/resource/b/resource-d");
|
||||
assertFalse(wasDenied());
|
||||
|
||||
updatePermissionPolicies("Pattern 5 Permission", "Deny Policy");
|
||||
login("alice", "alice");
|
||||
navigateTo("/resource/a/resource-d");
|
||||
navigateTo("/a/a/resource-d");
|
||||
assertTrue(wasDenied());
|
||||
navigateTo("/resource/b/resource-d");
|
||||
navigateTo("/a/b/resource-d");
|
||||
assertTrue(wasDenied());
|
||||
|
||||
updatePermissionPolicies("Pattern 5 Permission", "Default Policy");
|
||||
login("alice", "alice");
|
||||
navigateTo("/resource/b/resource-d");
|
||||
navigateTo("/a/b/resource-d");
|
||||
assertFalse(wasDenied());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,12 +17,17 @@
|
|||
|
||||
package org.keycloak.testsuite.admin.client.authorization;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.Configuration;
|
||||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -35,12 +40,60 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
|
|||
|
||||
private AuthzClient authzClient;
|
||||
|
||||
@Override
|
||||
protected ResourceRepresentation doCreateResource(ResourceRepresentation newResource) {
|
||||
org.keycloak.authorization.client.representation.ResourceRepresentation resource = toResourceRepresentation(newResource);
|
||||
@Test
|
||||
public void testFindMatchingUri() {
|
||||
doCreateResource(new ResourceRepresentation("/*", Collections.emptySet(), "/*", null));
|
||||
doCreateResource(new ResourceRepresentation("/resources/*", Collections.emptySet(), "/resources/*", null));
|
||||
doCreateResource(new ResourceRepresentation("/resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/*", null));
|
||||
doCreateResource(new ResourceRepresentation("/resources/{pattern}/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/{pattern}/*", null));
|
||||
doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
|
||||
doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resource", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
|
||||
|
||||
AuthzClient authzClient = getAuthzClient();
|
||||
org.keycloak.authorization.client.representation.ResourceRepresentation response = authzClient.protection().resource().create(resource);
|
||||
|
||||
List<ResourceRepresentation> resources = authzClient.protection().resource().findByMatchingUri("/test");
|
||||
|
||||
assertNotNull(resources);
|
||||
assertEquals(1, resources.size());
|
||||
assertEquals("/*", resources.get(0).getUri());
|
||||
|
||||
resources = authzClient.protection().resource().findByMatchingUri("/resources/test");
|
||||
|
||||
assertNotNull(resources);
|
||||
assertEquals(1, resources.size());
|
||||
assertEquals("/resources/*", resources.get(0).getUri());
|
||||
|
||||
resources = authzClient.protection().resource().findByMatchingUri("/resources");
|
||||
|
||||
assertNotNull(resources);
|
||||
assertEquals(1, resources.size());
|
||||
assertEquals("/resources/*", resources.get(0).getUri());
|
||||
|
||||
resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b");
|
||||
|
||||
assertNotNull(resources);
|
||||
assertEquals(1, resources.size());
|
||||
assertEquals("/resources/{pattern}/*", resources.get(0).getUri());
|
||||
|
||||
resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b/c");
|
||||
|
||||
assertNotNull(resources);
|
||||
assertEquals(1, resources.size());
|
||||
assertEquals("/resources/{pattern}/{pattern}/*", resources.get(0).getUri());
|
||||
|
||||
resources = authzClient.protection().resource().findByMatchingUri("/resources/a/sub-resources/c/d");
|
||||
|
||||
assertNotNull(resources);
|
||||
assertEquals(1, resources.size());
|
||||
assertEquals("/resources/{pattern}/sub-resources/{pattern}/*", resources.get(0).getUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceRepresentation doCreateResource(ResourceRepresentation newResource) {
|
||||
ResourceRepresentation resource = toResourceRepresentation(newResource);
|
||||
|
||||
AuthzClient authzClient = getAuthzClient();
|
||||
ResourceRepresentation response = authzClient.protection().resource().create(resource);
|
||||
|
||||
return toResourceRepresentation(authzClient, response.getId());
|
||||
}
|
||||
|
@ -60,7 +113,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
|
|||
}
|
||||
|
||||
private ResourceRepresentation toResourceRepresentation(AuthzClient authzClient, String id) {
|
||||
org.keycloak.authorization.client.representation.ResourceRepresentation created = authzClient.protection().resource().findById(id);
|
||||
ResourceRepresentation created = authzClient.protection().resource().findById(id);
|
||||
ResourceRepresentation resourceRepresentation = new ResourceRepresentation();
|
||||
|
||||
resourceRepresentation.setId(created.getId());
|
||||
|
@ -68,11 +121,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
|
|||
resourceRepresentation.setIconUri(created.getIconUri());
|
||||
resourceRepresentation.setUri(created.getUri());
|
||||
resourceRepresentation.setType(created.getType());
|
||||
ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
|
||||
|
||||
owner.setId(created.getOwner());
|
||||
|
||||
resourceRepresentation.setOwner(owner);
|
||||
resourceRepresentation.setOwner(created.getOwner());
|
||||
resourceRepresentation.setScopes(created.getScopes().stream().map(scopeRepresentation -> {
|
||||
ScopeRepresentation scope = new ScopeRepresentation();
|
||||
|
||||
|
@ -88,8 +137,8 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
|
|||
return resourceRepresentation;
|
||||
}
|
||||
|
||||
private org.keycloak.authorization.client.representation.ResourceRepresentation toResourceRepresentation(ResourceRepresentation newResource) {
|
||||
org.keycloak.authorization.client.representation.ResourceRepresentation resource = new org.keycloak.authorization.client.representation.ResourceRepresentation();
|
||||
private ResourceRepresentation toResourceRepresentation(ResourceRepresentation newResource) {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setId(newResource.getId());
|
||||
resource.setName(newResource.getName());
|
||||
|
@ -102,7 +151,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
|
|||
}
|
||||
|
||||
resource.setScopes(newResource.getScopes().stream().map(scopeRepresentation -> {
|
||||
org.keycloak.authorization.client.representation.ScopeRepresentation scope = new org.keycloak.authorization.client.representation.ScopeRepresentation();
|
||||
ScopeRepresentation scope = new ScopeRepresentation();
|
||||
|
||||
scope.setName(scopeRepresentation.getName());
|
||||
scope.setIconUri(scopeRepresentation.getIconUri());
|
||||
|
|
|
@ -42,7 +42,6 @@ import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
|
|||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.ClientAuthenticator;
|
||||
import org.keycloak.authorization.client.Configuration;
|
||||
import org.keycloak.authorization.client.representation.ResourceRepresentation;
|
||||
import org.keycloak.authorization.client.resource.ProtectionResource;
|
||||
import org.keycloak.authorization.client.util.HttpResponseException;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
|
@ -56,6 +55,7 @@ import org.keycloak.representations.idm.authorization.AuthorizationResponse;
|
|||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
import org.keycloak.representations.idm.authorization.PermissionRequest;
|
||||
import org.keycloak.representations.idm.authorization.PermissionResponse;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
|
|
|
@ -38,8 +38,6 @@ import org.keycloak.admin.client.resource.ClientsResource;
|
|||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.Configuration;
|
||||
import org.keycloak.authorization.client.representation.ResourceRepresentation;
|
||||
import org.keycloak.authorization.client.representation.ScopeRepresentation;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
@ -48,7 +46,9 @@ import org.keycloak.representations.idm.authorization.AuthorizationResponse;
|
|||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
|
|
@ -24,6 +24,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
|||
*/
|
||||
@AppServerContainer("app-server-wildfly")
|
||||
//@AdapterLibsLocationProperty("adapter.libs.wildfly")
|
||||
public class WildflyPhotozExampleAdapterTest extends AbstractPhotozExampleAdapterTest {
|
||||
public class WildflyPhotozExampleAdapterTest extends AbstractPhotozExampleNoLazyLoadPathsAdapterTest {
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2018 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.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@AppServerContainer("app-server-wildfly")
|
||||
//@AdapterLibsLocationProperty("adapter.libs.wildfly")
|
||||
public class WildflyPhotozExampleLazyLoadPathsAdapterTest extends AbstractPhotozExampleLazyLoadPathsAdapterTest {
|
||||
|
||||
}
|
|
@ -26,6 +26,6 @@ import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
|||
@RunAsClient
|
||||
@AppServerContainer("app-server-wildfly")
|
||||
//@AdapterLibsLocationProperty("adapter.libs.wildfly")
|
||||
public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzFunctionalAdapterTest {
|
||||
public class WildflyServletAuthzAdapterTest extends AbstractServletAuthzAdapterTest {
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2018 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.arquillian.annotation.AppServerContainer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
@RunAsClient
|
||||
@AppServerContainer("app-server-wildfly")
|
||||
//@AdapterLibsLocationProperty("adapter.libs.wildfly")
|
||||
public class WildflyServletAuthzLazyLoadPathsAdapterTest extends AbstractServletAuthzLazyLoadPathsAdapterTest {
|
||||
|
||||
}
|
Loading…
Reference in a new issue