[KEYCLOAK-7029] - Configuration of cache policies for cached resources/path
This commit is contained in:
parent
28d0ab9da8
commit
a939c45d58
13 changed files with 402 additions and 56 deletions
|
@ -159,7 +159,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||||
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, grantedPermissions);
|
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, grantedPermissions);
|
||||||
}
|
}
|
||||||
if (HTTP_METHOD_DELETE.equalsIgnoreCase(request.getMethod()) && actualPathConfig.isInstance()) {
|
if (HTTP_METHOD_DELETE.equalsIgnoreCase(request.getMethod()) && actualPathConfig.isInstance()) {
|
||||||
policyEnforcer.getPaths().remove(actualPathConfig);
|
policyEnforcer.getPathMatcher().removeFromCache(getPath(request));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -281,7 +281,7 @@ public abstract class AbstractPolicyEnforcer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
|
private AuthorizationContext createAuthorizationContext(AccessToken accessToken, PathConfig pathConfig) {
|
||||||
return new ClientAuthorizationContext(accessToken, pathConfig, policyEnforcer.getPaths(), getAuthzClient());
|
return new ClientAuthorizationContext(accessToken, pathConfig, getAuthzClient());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
|
private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ public class PathCache {
|
||||||
private final AtomicBoolean writing = new AtomicBoolean(false);
|
private final AtomicBoolean writing = new AtomicBoolean(false);
|
||||||
|
|
||||||
private final long maxAge;
|
private final long maxAge;
|
||||||
|
private final boolean enabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
|
@ -55,9 +56,14 @@ public class PathCache {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.maxAge = maxAge;
|
this.maxAge = maxAge;
|
||||||
|
this.enabled = maxAge > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void put(String uri, PathConfig newValue) {
|
public void put(String uri, PathConfig newValue) {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (parkForWriteAndCheckInterrupt()) {
|
if (parkForWriteAndCheckInterrupt()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.keycloak.authorization.client.resource.ProtectedResource;
|
||||||
import org.keycloak.common.util.PathMatcher;
|
import org.keycloak.common.util.PathMatcher;
|
||||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||||
|
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathCacheConfig;
|
||||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||||
import org.keycloak.representations.idm.authorization.Permission;
|
import org.keycloak.representations.idm.authorization.Permission;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
|
@ -52,8 +53,8 @@ public class PolicyEnforcer {
|
||||||
private final KeycloakDeployment deployment;
|
private final KeycloakDeployment deployment;
|
||||||
private final AuthzClient authzClient;
|
private final AuthzClient authzClient;
|
||||||
private final PolicyEnforcerConfig enforcerConfig;
|
private final PolicyEnforcerConfig enforcerConfig;
|
||||||
|
private final PathConfigMatcher pathMatcher;
|
||||||
private final Map<String, PathConfig> paths;
|
private final Map<String, PathConfig> paths;
|
||||||
private final PathMatcher pathMatcher;
|
|
||||||
|
|
||||||
public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) {
|
public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) {
|
||||||
this.deployment = deployment;
|
this.deployment = deployment;
|
||||||
|
@ -70,8 +71,8 @@ public class PolicyEnforcer {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
|
paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
|
||||||
this.pathMatcher = createPathMatcher(authzClient);
|
pathMatcher = new PathConfigMatcher(paths, enforcerConfig, authzClient);
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER.debug("Initialization complete. Path configurations:");
|
LOGGER.debug("Initialization complete. Path configurations:");
|
||||||
|
@ -117,7 +118,7 @@ public class PolicyEnforcer {
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathMatcher<PathConfig> getPathMatcher() {
|
public PathConfigMatcher getPathMatcher() {
|
||||||
return pathMatcher;
|
return pathMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,71 +217,90 @@ public class PolicyEnforcer {
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PathMatcher<PathConfig> createPathMatcher(final AuthzClient authzClient) {
|
public class PathConfigMatcher extends PathMatcher<PathConfig> {
|
||||||
final PathCache pathCache = new PathCache(100, 30000);
|
|
||||||
|
|
||||||
return new PathMatcher<PathConfig>() {
|
private final Map<String, PathConfig> paths;
|
||||||
@Override
|
private final PathCache pathCache;
|
||||||
public PathConfig matches(String targetUri) {
|
private final AuthzClient authzClient;
|
||||||
PathConfig pathConfig = pathCache.get(targetUri);
|
private final PolicyEnforcerConfig enforcerConfig;
|
||||||
|
|
||||||
if (pathCache.containsKey(targetUri) || pathConfig != null) {
|
public PathConfigMatcher(Map<String, PathConfig> paths, PolicyEnforcerConfig enforcerConfig, AuthzClient authzClient) {
|
||||||
return pathConfig;
|
this.paths = paths;
|
||||||
}
|
this.enforcerConfig = enforcerConfig;
|
||||||
|
PathCacheConfig cacheConfig = enforcerConfig.getPathCacheConfig();
|
||||||
|
|
||||||
pathConfig = super.matches(targetUri);
|
if (cacheConfig == null) {
|
||||||
|
cacheConfig = new PathCacheConfig();
|
||||||
|
}
|
||||||
|
|
||||||
if (enforcerConfig.getLazyLoadPaths() && (pathConfig == null || pathConfig.getPath().contains("*"))) {
|
pathCache = new PathCache(cacheConfig.getMaxEntries(), cacheConfig.getLifespan());
|
||||||
|
this.authzClient = authzClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathConfig matches(String targetUri) {
|
||||||
|
PathConfig pathConfig = pathCache.get(targetUri);
|
||||||
|
|
||||||
|
if (pathCache.containsKey(targetUri) || pathConfig != null) {
|
||||||
|
return pathConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathConfig = super.matches(targetUri);
|
||||||
|
|
||||||
|
if (enforcerConfig.getLazyLoadPaths() || enforcerConfig.getPathCacheConfig() != null) {
|
||||||
|
if ((pathConfig == null || (pathConfig.getPath().contains("*")))) {
|
||||||
try {
|
try {
|
||||||
List<ResourceRepresentation> matchingResources = authzClient.protection().resource().findByMatchingUri(targetUri);
|
List<ResourceRepresentation> matchingResources = authzClient.protection().resource().findByMatchingUri(targetUri);
|
||||||
|
|
||||||
if (!matchingResources.isEmpty()) {
|
if (!matchingResources.isEmpty()) {
|
||||||
pathConfig = PathConfig.createPathConfig(matchingResources.get(0));
|
pathConfig = PathConfig.createPathConfig(matchingResources.get(0));
|
||||||
paths.put(pathConfig.getPath(), pathConfig);
|
|
||||||
}
|
}
|
||||||
} catch (Exception cause) {
|
} catch (Exception cause) {
|
||||||
LOGGER.errorf(cause, "Could not lazy load paths from server");
|
LOGGER.errorf(cause, "Could not lazy load resource with path [" + targetUri + "] from server");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pathCache.put(targetUri, pathConfig);
|
|
||||||
|
|
||||||
return pathConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
pathCache.put(targetUri, pathConfig);
|
||||||
protected String getPath(PathConfig entry) {
|
|
||||||
return entry.getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
return pathConfig;
|
||||||
protected Collection<PathConfig> getPaths() {
|
}
|
||||||
return paths.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
|
protected String getPath(PathConfig entry) {
|
||||||
if (originalConfig.hasPattern()) {
|
return entry.getPath();
|
||||||
ProtectedResource resource = authzClient.protection().resource();
|
}
|
||||||
List<ResourceRepresentation> search = resource.findByUri(path);
|
|
||||||
|
|
||||||
if (!search.isEmpty()) {
|
@Override
|
||||||
// resource does exist on the server, cache it
|
protected Collection<PathConfig> getPaths() {
|
||||||
ResourceRepresentation targetResource = search.get(0);
|
return paths.values();
|
||||||
PathConfig config = PathConfig.createPathConfig(targetResource);
|
}
|
||||||
|
|
||||||
config.setScopes(originalConfig.getScopes());
|
@Override
|
||||||
config.setMethods(originalConfig.getMethods());
|
protected PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
|
||||||
config.setParentConfig(originalConfig);
|
if (originalConfig.hasPattern()) {
|
||||||
config.setEnforcementMode(originalConfig.getEnforcementMode());
|
ProtectedResource resource = authzClient.protection().resource();
|
||||||
|
List<ResourceRepresentation> search = resource.findByUri(path);
|
||||||
|
|
||||||
return config;
|
if (!search.isEmpty()) {
|
||||||
}
|
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;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeFromCache(String pathConfig) {
|
||||||
|
pathCache.remove(pathConfig);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@ public class ClientAuthorizationContext extends AuthorizationContext {
|
||||||
|
|
||||||
private final AuthzClient client;
|
private final AuthzClient client;
|
||||||
|
|
||||||
public ClientAuthorizationContext(AccessToken authzToken, PolicyEnforcerConfig.PathConfig current, Map<String, PolicyEnforcerConfig.PathConfig> paths, AuthzClient client) {
|
public ClientAuthorizationContext(AccessToken authzToken, PolicyEnforcerConfig.PathConfig current, AuthzClient client) {
|
||||||
super(authzToken, current, paths);
|
super(authzToken, current);
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,18 +33,16 @@ public class AuthorizationContext {
|
||||||
|
|
||||||
private final AccessToken authzToken;
|
private final AccessToken authzToken;
|
||||||
private final PathConfig current;
|
private final PathConfig current;
|
||||||
private final Map<String, PathConfig> paths;
|
|
||||||
private boolean granted;
|
private boolean granted;
|
||||||
|
|
||||||
public AuthorizationContext(AccessToken authzToken, PathConfig current, Map<String, PathConfig> paths) {
|
public AuthorizationContext(AccessToken authzToken, PathConfig current) {
|
||||||
this.authzToken = authzToken;
|
this.authzToken = authzToken;
|
||||||
this.current = current;
|
this.current = current;
|
||||||
this.paths = paths;
|
|
||||||
this.granted = true;
|
this.granted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthorizationContext() {
|
public AuthorizationContext() {
|
||||||
this(null, null, null);
|
this(null, null);
|
||||||
this.granted = false;
|
this.granted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,10 @@ public class PolicyEnforcerConfig {
|
||||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
private List<PathConfig> paths = new ArrayList<>();
|
private List<PathConfig> paths = new ArrayList<>();
|
||||||
|
|
||||||
|
@JsonProperty("path-cache")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
|
private PathCacheConfig pathCacheConfig;
|
||||||
|
|
||||||
@JsonProperty("lazy-load-paths")
|
@JsonProperty("lazy-load-paths")
|
||||||
private Boolean lazyLoadPaths = Boolean.FALSE;
|
private Boolean lazyLoadPaths = Boolean.FALSE;
|
||||||
|
|
||||||
|
@ -53,6 +57,10 @@ public class PolicyEnforcerConfig {
|
||||||
return this.paths;
|
return this.paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PathCacheConfig getPathCacheConfig() {
|
||||||
|
return pathCacheConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean getLazyLoadPaths() {
|
public Boolean getLazyLoadPaths() {
|
||||||
return lazyLoadPaths;
|
return lazyLoadPaths;
|
||||||
}
|
}
|
||||||
|
@ -77,6 +85,10 @@ public class PolicyEnforcerConfig {
|
||||||
this.paths = paths;
|
this.paths = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPathCacheConfig(PathCacheConfig pathCacheConfig) {
|
||||||
|
this.pathCacheConfig = pathCacheConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public String getOnDenyRedirectTo() {
|
public String getOnDenyRedirectTo() {
|
||||||
return onDenyRedirectTo;
|
return onDenyRedirectTo;
|
||||||
}
|
}
|
||||||
|
@ -250,6 +262,29 @@ public class PolicyEnforcerConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class PathCacheConfig {
|
||||||
|
|
||||||
|
@JsonProperty("max-entries")
|
||||||
|
int maxEntries = 1000;
|
||||||
|
long lifespan = 30000;
|
||||||
|
|
||||||
|
public int getMaxEntries() {
|
||||||
|
return maxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxEntries(int maxEntries) {
|
||||||
|
this.maxEntries = maxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLifespan() {
|
||||||
|
return lifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLifespan(long lifespan) {
|
||||||
|
this.lifespan = lifespan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum EnforcementMode {
|
public enum EnforcementMode {
|
||||||
PERMISSIVE,
|
PERMISSIVE,
|
||||||
ENFORCING,
|
ENFORCING,
|
||||||
|
|
|
@ -99,6 +99,12 @@ public class AbstractPolicyRepresentation {
|
||||||
this.policies.addAll(Arrays.asList(id));
|
this.policies.addAll(Arrays.asList(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removePolicy(String policy) {
|
||||||
|
if (policies != null) {
|
||||||
|
policies.remove(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Set<String> getResources() {
|
public Set<String> getResources() {
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"path-cache": {
|
||||||
|
"lifespan": 0,
|
||||||
|
"max-entries": 1000
|
||||||
|
},
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"name": "Premium Resource",
|
||||||
|
"path": "/protected/premium/pep-disabled.jsp",
|
||||||
|
"enforcement-mode": "DISABLED"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"path-cache": {
|
||||||
|
"lifespan": 5000,
|
||||||
|
"max-entries": 1000
|
||||||
|
},
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"name": "Premium Resource",
|
||||||
|
"path": "/protected/premium/pep-disabled.jsp",
|
||||||
|
"enforcement-mode": "DISABLED"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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 org.jboss.arquillian.container.test.api.Deployment;
|
||||||
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public abstract class AbstractServletCacheDisabledAdapterTest 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-cache-disabled-authz-service.json"), "keycloak.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateNewResource() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
this.driver.navigate().to(getResourceServerUrl() + "/new-resource");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
ResourceRepresentation resource = new ResourceRepresentation();
|
||||||
|
|
||||||
|
resource.setName("New Resource");
|
||||||
|
resource.setUri("/new-resource");
|
||||||
|
|
||||||
|
getAuthorizationResource().resources().create(resource);
|
||||||
|
|
||||||
|
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
|
||||||
|
|
||||||
|
permission.setName(resource.getName() + " Permission");
|
||||||
|
permission.addResource(resource.getName());
|
||||||
|
permission.addPolicy("Deny Policy");
|
||||||
|
|
||||||
|
permission = getAuthorizationResource().permissions().resource().create(permission).readEntity(ResourcePermissionRepresentation.class);
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
this.driver.navigate().to(getResourceServerUrl() + "/new-resource");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
permission = getAuthorizationResource().permissions().resource().findById(permission.getId()).toRepresentation();
|
||||||
|
|
||||||
|
permission.removePolicy("Deny Policy");
|
||||||
|
permission.addPolicy("Any User Policy");
|
||||||
|
|
||||||
|
getAuthorizationResource().permissions().resource().findById(permission.getId()).update(permission);
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
this.driver.navigate().to(getResourceServerUrl() + "/new-resource");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* 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 org.jboss.arquillian.container.test.api.Deployment;
|
||||||
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public abstract class AbstractServletCacheLifespanAdapterTest 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-cache-lifespan-authz-service.json"), "keycloak.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateNewResourceWaitExpiration() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
this.driver.navigate().to(getResourceServerUrl() + "/new-resource");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
ResourceRepresentation resource = new ResourceRepresentation();
|
||||||
|
|
||||||
|
resource.setName("New Resource");
|
||||||
|
resource.setUri("/new-resource");
|
||||||
|
|
||||||
|
getAuthorizationResource().resources().create(resource);
|
||||||
|
|
||||||
|
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
|
||||||
|
|
||||||
|
permission.setName(resource.getName() + " Permission");
|
||||||
|
permission.addResource(resource.getName());
|
||||||
|
permission.addPolicy("Deny Policy");
|
||||||
|
|
||||||
|
permission = getAuthorizationResource().permissions().resource().create(permission).readEntity(ResourcePermissionRepresentation.class);
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
this.driver.navigate().to(getResourceServerUrl() + "/new-resource");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
Thread.sleep(5000);
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
this.driver.navigate().to(getResourceServerUrl() + "/new-resource");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
@RunAsClient
|
||||||
|
@AppServerContainer("app-server-wildfly")
|
||||||
|
//@AdapterLibsLocationProperty("adapter.libs.wildfly")
|
||||||
|
public class WildflyServletCacheDisabledAdapterTest extends AbstractServletCacheDisabledAdapterTest {
|
||||||
|
|
||||||
|
}
|
|
@ -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 <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
@RunAsClient
|
||||||
|
@AppServerContainer("app-server-wildfly")
|
||||||
|
//@AdapterLibsLocationProperty("adapter.libs.wildfly")
|
||||||
|
public class WildflyServletCacheLifespanAdapterTest extends AbstractServletCacheLifespanAdapterTest {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue