[KEYCLOAK-13942] - Invalidate pre-defined paths when paths are invalidated
This commit is contained in:
parent
882f5ffea4
commit
40efbb0f9c
12 changed files with 1091 additions and 586 deletions
|
@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.LockSupport;
|
import java.util.concurrent.locks.LockSupport;
|
||||||
|
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,14 +43,16 @@ public class PathCache {
|
||||||
|
|
||||||
private final long maxAge;
|
private final long maxAge;
|
||||||
private final boolean enabled;
|
private final boolean enabled;
|
||||||
|
private final Map<String, PathConfig> paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*
|
* @param maxEntries the maximum number of entries to keep in the cache
|
||||||
* @param maxEntries the maximum number of entries to keep in the cache
|
|
||||||
* @param maxAge the time in milliseconds that an entry can stay in the cache. If {@code -1}, entries never expire
|
* @param maxAge the time in milliseconds that an entry can stay in the cache. If {@code -1}, entries never expire
|
||||||
|
* @param paths the pre-configured paths
|
||||||
*/
|
*/
|
||||||
public PathCache(final int maxEntries, long maxAge) {
|
public PathCache(final int maxEntries, long maxAge,
|
||||||
|
Map<String, PathConfig> paths) {
|
||||||
cache = new LinkedHashMap<String, CacheEntry>(16, DEFAULT_LOAD_FACTOR, true) {
|
cache = new LinkedHashMap<String, CacheEntry>(16, DEFAULT_LOAD_FACTOR, true) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean removeEldestEntry(Map.Entry eldest) {
|
protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||||
|
@ -58,10 +61,15 @@ public class PathCache {
|
||||||
};
|
};
|
||||||
this.maxAge = maxAge;
|
this.maxAge = maxAge;
|
||||||
this.enabled = ! (maxAge < -1 || (maxAge > -1 && maxAge <= 0));
|
this.enabled = ! (maxAge < -1 || (maxAge > -1 && maxAge <= 0));
|
||||||
|
this.paths = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void put(String uri, PathConfig newValue) {
|
public void put(String uri, PathConfig newValue) {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
|
if (newValue != null) {
|
||||||
|
// if disabled we also remove from the pre-defined paths map
|
||||||
|
markForInvalidation(newValue);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +88,15 @@ public class PathCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void markForInvalidation(PathConfig newValue) {
|
||||||
|
PathConfig pathConfig = paths.get(newValue.getPath());
|
||||||
|
|
||||||
|
if (pathConfig != null && !pathConfig.isStatic()) {
|
||||||
|
// invalidate the configuration so that the path config is reload based on latest changes on the server
|
||||||
|
pathConfig.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean containsKey(String uri) {
|
public boolean containsKey(String uri) {
|
||||||
return cache.containsKey(uri);
|
return cache.containsKey(uri);
|
||||||
}
|
}
|
||||||
|
@ -115,12 +132,19 @@ public class PathCache {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PathConfig config = cached.value();
|
||||||
|
|
||||||
if (cached.isExpired()) {
|
if (cached.isExpired()) {
|
||||||
remove(cached.key());
|
remove(cached.key());
|
||||||
|
|
||||||
|
if (config != null && config.getPath() != null) {
|
||||||
|
// also remove from pre-defined paths map so that changes on the server are properly reflected
|
||||||
|
markForInvalidation(config);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cached.value();
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean parkForWriteAndCheckInterrupt() {
|
private boolean parkForWriteAndCheckInterrupt() {
|
||||||
|
|
|
@ -191,6 +191,10 @@ public class PolicyEnforcer {
|
||||||
|
|
||||||
if (resource != null) {
|
if (resource != null) {
|
||||||
pathConfig.setId(resource.getId());
|
pathConfig.setId(resource.getId());
|
||||||
|
// if the resource is staticly bound to a resource it means the config can not be invalidated
|
||||||
|
if (resourceName != null) {
|
||||||
|
pathConfig.setStatic(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PathConfig existingPath = null;
|
PathConfig existingPath = null;
|
||||||
|
@ -248,7 +252,7 @@ public class PolicyEnforcer {
|
||||||
cacheConfig = new PathCacheConfig();
|
cacheConfig = new PathCacheConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
pathCache = new PathCache(cacheConfig.getMaxEntries(), cacheConfig.getLifespan());
|
pathCache = new PathCache(cacheConfig.getMaxEntries(), cacheConfig.getLifespan(), paths);
|
||||||
this.authzClient = authzClient;
|
this.authzClient = authzClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,20 +267,35 @@ public class PolicyEnforcer {
|
||||||
pathConfig = super.matches(targetUri);
|
pathConfig = super.matches(targetUri);
|
||||||
|
|
||||||
if (enforcerConfig.getLazyLoadPaths() || enforcerConfig.getPathCacheConfig() != null) {
|
if (enforcerConfig.getLazyLoadPaths() || enforcerConfig.getPathCacheConfig() != null) {
|
||||||
if ((pathConfig == null || (pathConfig.getPath().contains("*")))) {
|
if ((pathConfig == null || pathConfig.isInvalidated() || 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()) {
|
||||||
|
// if this config is invalidated (e.g.: due to cache expiration) we remove and return null
|
||||||
|
if (pathConfig != null && pathConfig.isInvalidated()) {
|
||||||
|
paths.remove(targetUri);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Map<String, Map<String, Object>> cipConfig = null;
|
Map<String, Map<String, Object>> cipConfig = null;
|
||||||
PolicyEnforcerConfig.EnforcementMode enforcementMode = PolicyEnforcerConfig.EnforcementMode.ENFORCING;
|
PolicyEnforcerConfig.EnforcementMode enforcementMode = PolicyEnforcerConfig.EnforcementMode.ENFORCING;
|
||||||
|
ResourceRepresentation targetResource = matchingResources.get(0);
|
||||||
|
|
||||||
if (pathConfig != null) {
|
if (pathConfig != null) {
|
||||||
cipConfig = pathConfig.getClaimInformationPointConfig();
|
cipConfig = pathConfig.getClaimInformationPointConfig();
|
||||||
enforcementMode = pathConfig.getEnforcementMode();
|
enforcementMode = pathConfig.getEnforcementMode();
|
||||||
|
} else {
|
||||||
|
for (PathConfig existingPath : paths.values()) {
|
||||||
|
if (existingPath.getId().equals(targetResource.getId())
|
||||||
|
&& existingPath.isStatic()
|
||||||
|
&& !PolicyEnforcerConfig.EnforcementMode.DISABLED.equals(existingPath.getEnforcementMode())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pathConfig = PathConfig.createPathConfigs(matchingResources.get(0)).iterator().next();
|
pathConfig = PathConfig.createPathConfigs(targetResource).iterator().next();
|
||||||
|
|
||||||
if (cipConfig != null) {
|
if (cipConfig != null) {
|
||||||
pathConfig.setClaimInformationPointConfig(cipConfig);
|
pathConfig.setClaimInformationPointConfig(cipConfig);
|
||||||
|
|
|
@ -177,6 +177,10 @@ public class PolicyEnforcerConfig {
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private PathConfig parentConfig;
|
private PathConfig parentConfig;
|
||||||
|
|
||||||
|
private boolean invalidated;
|
||||||
|
|
||||||
|
private boolean staticPath;
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return this.path;
|
return this.path;
|
||||||
}
|
}
|
||||||
|
@ -270,6 +274,22 @@ public class PolicyEnforcerConfig {
|
||||||
public PathConfig getParentConfig() {
|
public PathConfig getParentConfig() {
|
||||||
return parentConfig;
|
return parentConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void invalidate() {
|
||||||
|
this.invalidated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInvalidated() {
|
||||||
|
return invalidated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStatic() {
|
||||||
|
return staticPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatic(boolean staticPath) {
|
||||||
|
this.staticPath = staticPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MethodConfig {
|
public static class MethodConfig {
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
{
|
||||||
|
"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": {},
|
||||||
|
"path-cache": {
|
||||||
|
"lifespan": 10000
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<%@page import="org.keycloak.common.util.Time"%>
|
||||||
|
|
||||||
|
<%
|
||||||
|
Time.setOffset(Integer.parseInt(request.getParameter("offset")));
|
||||||
|
%>
|
|
@ -0,0 +1,106 @@
|
||||||
|
{
|
||||||
|
"realm": "servlet-policy-enforcer-authz",
|
||||||
|
"auth-server-url": "http://localhost:8180/auth",
|
||||||
|
"ssl-required": "external",
|
||||||
|
"resource": "servlet-policy-enforcer",
|
||||||
|
"credentials": {
|
||||||
|
"secret": "secret"
|
||||||
|
},
|
||||||
|
"policy-enforcer": {
|
||||||
|
"on-deny-redirect-to": "/servlet-policy-enforcer/denied.jsp",
|
||||||
|
"lazy-load-paths": false,
|
||||||
|
"path-cache": {
|
||||||
|
"lifespan": 5
|
||||||
|
},
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"name": "Welcome Resource",
|
||||||
|
"path": "/index.jsp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Welcome Resource",
|
||||||
|
"path": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 1",
|
||||||
|
"path": "/resource/{pattern}/{sub-resource}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 2",
|
||||||
|
"path": "/{pattern}/resource-a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 3",
|
||||||
|
"path": "/{pattern}/resource-b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 4",
|
||||||
|
"path": "/resource-c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 5",
|
||||||
|
"path": "/a/{pattern}/resource-d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 6",
|
||||||
|
"path": "/resource/{pattern}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 7",
|
||||||
|
"path": "/resource/{pattern}/f/{resource}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 8",
|
||||||
|
"path": "/resource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 9",
|
||||||
|
"path": "/file/*.suffix"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 10",
|
||||||
|
"path": "/resource/{pattern}/i/{resource}/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 11",
|
||||||
|
"path": "/api/{version}/{resource}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 12",
|
||||||
|
"path": "/keycloak_json_uri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 14",
|
||||||
|
"path": "/keycloak-6623/sub-resource/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 13",
|
||||||
|
"path": "/keycloak-6623/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 15",
|
||||||
|
"path": "/keycloak-7148/{id}/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 16",
|
||||||
|
"path": "/keycloak-7269/sub-resource1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 16",
|
||||||
|
"path": "/keycloak-7269/sub-resource2/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 16",
|
||||||
|
"path": "/keycloak-7269/sub-resource1/{test-pattern}/specialSuffix"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 17",
|
||||||
|
"path": "/keycloak-8823/resource/{version}/subresource/{id}/{other}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pattern 17 Entities",
|
||||||
|
"path": "/keycloak-8823/resource/{version}/subresource/{id}/entities"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -178,6 +178,15 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
|
||||||
pause(WAIT_AFTER_OPERATION);
|
pause(WAIT_AFTER_OPERATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void viewProfile(ResponseValidator validator) {
|
||||||
|
testExecutor.sendXMLHttpRequest(
|
||||||
|
XMLHttpRequest.create()
|
||||||
|
.method("GET")
|
||||||
|
.addHeader("Accept", "application/json")
|
||||||
|
.url(apiUrl + "/profile")
|
||||||
|
, validator);
|
||||||
|
}
|
||||||
|
|
||||||
public void viewAlbum(String name, ResponseValidator validator) {
|
public void viewAlbum(String name, ResponseValidator validator) {
|
||||||
testExecutor.sendXMLHttpRequest(
|
testExecutor.sendXMLHttpRequest(
|
||||||
XMLHttpRequest.create()
|
XMLHttpRequest.create()
|
||||||
|
|
|
@ -80,7 +80,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractBasePhoto
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnlyOwnerCanDeleteAlbum() throws Exception {
|
public void testPathConfigInvalidation() throws Exception {
|
||||||
loginToClientPage(aliceUser);
|
loginToClientPage(aliceUser);
|
||||||
clientPage.createAlbum(ALICE_ALBUM_NAME);
|
clientPage.createAlbum(ALICE_ALBUM_NAME);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,599 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.testsuite.adapter.example.authorization;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
||||||
|
import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jboss.arquillian.container.test.api.Deployer;
|
||||||
|
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||||
|
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.ResourcePermissionsResource;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
|
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||||
|
import org.keycloak.testsuite.util.ServerURLs;
|
||||||
|
import org.keycloak.testsuite.util.UIUtils;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public class AbstractServletPolicyEnforcerTest extends AbstractExampleAdapterTest {
|
||||||
|
|
||||||
|
protected static final String REALM_NAME = "servlet-policy-enforcer-authz";
|
||||||
|
protected static final String RESOURCE_SERVER_ID = "servlet-policy-enforcer";
|
||||||
|
|
||||||
|
@ArquillianResource
|
||||||
|
private Deployer deployer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||||
|
testRealms.add(
|
||||||
|
loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern1() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/resource/a/b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 1 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource/a/b");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 1 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource/a/b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern2() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/a/resource-a");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/b/resource-a");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 2 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/a/resource-a");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/b/resource-a");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 2 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/b/resource-a");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern3() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/a/resource-b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/b/resource-b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 3 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/a/resource-b");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/b/resource-b");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 3 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/b/resource-b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 2 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/b/resource-a");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 3 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/a/resource-b");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/b/resource-a");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern4() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/resource-c");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 4 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource-c");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 4 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource-c");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern5() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/a/a/resource-d");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/resource/b/resource-d");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 5 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/a/a/resource-d");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/a/b/resource-d");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 5 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/a/b/resource-d");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern6() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/resource/a");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/resource/b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 6 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource/a");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/resource/b");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 6 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource/b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern7() throws Exception {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/resource/a/f/b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/resource/c/f/d");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 7 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource/a/f/b");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/resource/c/f/d");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 7 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource/c/f/d");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern8() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/resource");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 8 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 8 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern9() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/file/*.suffix");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 9 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/file/*.suffix");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 9 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/file/*.suffix");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern10() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
|
||||||
|
navigateTo("/resource/a/i/b/c/d/e");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/resource/a/i/b/c/");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 10 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource/a/i/b/c/d/e");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/resource/a/i/b/c/d");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 10 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/resource/a/i/b/c/d");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPattern11UsingResourceInstancePermission() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/api/v1/resource-a");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/api/v1/resource-b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
ResourceRepresentation resource = new ResourceRepresentation("/api/v1/resource-c");
|
||||||
|
|
||||||
|
resource.setUri(resource.getName());
|
||||||
|
|
||||||
|
getAuthorizationResource().resources().create(resource);
|
||||||
|
|
||||||
|
createResourcePermission(resource.getName() + " permission", resource.getName(), "Default Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo(resource.getUri());
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies(resource.getName() + " permission", "Deny Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo(resource.getUri());
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies(resource.getName() + " permission", "Default Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo(resource.getUri());
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
navigateTo("/api/v1");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/api/v1/");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/api");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/api/");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPathWithPatternSlashAllAndResourceInstance() {
|
||||||
|
performTests(() -> {
|
||||||
|
ResourceRepresentation resource = new ResourceRepresentation("Pattern 15 Instance");
|
||||||
|
|
||||||
|
resource.setType("pattern-15");
|
||||||
|
resource.setUri("/keycloak-7148/1");
|
||||||
|
resource.setOwner("alice");
|
||||||
|
|
||||||
|
getAuthorizationResource().resources().create(resource).close();
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-7148/1");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7148/1/sub-a/2");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7148/1/sub-a");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7148/1/sub-a/2/sub-b");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 15 Permission", "Deny Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-7148/1");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-7148/1/sub-a/2");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-7148/1/sub-a");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-7148/1/sub-a/2/sub-b");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
// does not exist
|
||||||
|
navigateTo("/keycloak-7148/2");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPriorityOfURIForResource() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/realm_uri");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak_json_uri");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 12 Permission", "Deny Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/realm_uri");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak_json_uri");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 12 Permission", "Default Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/realm_uri");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak_json_uri");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPathOrderWithAllPaths() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-6623");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-6623/sub-resource");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 13 Permission", "Deny Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-6623");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-6623/sub-resource");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 14 Permission", "Deny Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-6623");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-6623/sub-resource/resource");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleUriForResourceJSONConfig() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-7269/sub-resource1");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource1/whatever/specialSuffix");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource2");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource2/w/h/a/t/e/v/e/r");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 16 Permission", "Deny Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-7269/sub-resource1");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource1/whatever/specialSuffix");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource2");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource2/w/h/a/t/e/v/e/r");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 16 Permission", "Default Policy");
|
||||||
|
navigateTo("/keycloak-7269/sub-resource1");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource1/whatever/specialSuffix");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource2");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-7269/sub-resource2/w/h/a/t/e/v/e/r");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverloadedTemplateUri() {
|
||||||
|
performTests(() -> {
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-8823/resource/v1/subresource/123/entities");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-8823/resource/v1/subresource/123/someother");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 17 Entities Permission", "Deny Policy");
|
||||||
|
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-8823/resource/v1/subresource/123/entities");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
navigateTo("/keycloak-8823/resource/v1/subresource/123/someother");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 17 Entities Permission", "Default Policy");
|
||||||
|
updatePermissionPolicies("Pattern 17 Permission", "Deny Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-8823/resource/v1/subresource/123/entities");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-8823/resource/v1/subresource/123/someother");
|
||||||
|
assertTrue(wasDenied());
|
||||||
|
|
||||||
|
updatePermissionPolicies("Pattern 17 Entities Permission", "Default Policy");
|
||||||
|
updatePermissionPolicies("Pattern 17 Permission", "Default Policy");
|
||||||
|
login("alice", "alice");
|
||||||
|
navigateTo("/keycloak-8823/resource/v1/subresource/123/entities");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
navigateTo("/keycloak-8823/resource/v1/subresource/123/someother");
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateTo(String path) {
|
||||||
|
this.driver.navigate().to(getResourceServerUrl() + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performTests(ExceptionRunnable assertion) {
|
||||||
|
performTests(() -> {}, assertion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthorizationResource getAuthorizationResource() {
|
||||||
|
return getClientResource(RESOURCE_SERVER_ID).authorization();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientResource getClientResource(String clientId) {
|
||||||
|
ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
|
||||||
|
ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
|
||||||
|
return clients.get(resourceServer.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logOut() {
|
||||||
|
navigateTo();
|
||||||
|
UIUtils.clickLink(driver.findElement(By.xpath("//a[text() = 'Sign Out']")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void login(String username, String password) {
|
||||||
|
try {
|
||||||
|
navigateTo();
|
||||||
|
if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
|
||||||
|
logOut();
|
||||||
|
navigateTo();
|
||||||
|
}
|
||||||
|
this.loginPage.form().login(username, password);
|
||||||
|
navigateTo();
|
||||||
|
assertFalse(wasDenied());
|
||||||
|
} catch (Exception cause) {
|
||||||
|
throw new RuntimeException("Login failed", cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateTo() {
|
||||||
|
this.driver.navigate().to(getResourceServerUrl() + "/");
|
||||||
|
waitForPageToLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean wasDenied() {
|
||||||
|
return this.driver.getPageSource().contains("You can not access this resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL getResourceServerUrl() {
|
||||||
|
try {
|
||||||
|
return new URL(ServerURLs.getAppServerContextRoot() + "/" + RESOURCE_SERVER_ID);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException("Could not obtain resource server url.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePermissionPolicies(String permissionName, String... policyNames) {
|
||||||
|
ResourcePermissionsResource permissions = getAuthorizationResource().permissions().resource();
|
||||||
|
ResourcePermissionRepresentation permission = permissions.findByName(permissionName);
|
||||||
|
|
||||||
|
permission.addPolicy(policyNames);
|
||||||
|
|
||||||
|
permissions.findById(permission.getId()).update(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createResourcePermission(String name, String resourceName, String... policyNames) {
|
||||||
|
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
|
||||||
|
|
||||||
|
permission.setName(name);
|
||||||
|
permission.addResource(resourceName);
|
||||||
|
permission.addPolicy(policyNames);
|
||||||
|
|
||||||
|
getAuthorizationResource().permissions().resource().create(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface ExceptionRunnable {
|
||||||
|
void run() throws Exception;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* 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.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
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.AuthorizationResource;
|
||||||
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
|
import org.keycloak.testsuite.adapter.page.PhotozClientAuthzTestApp;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||||
|
import org.keycloak.testsuite.util.javascript.ResponseValidator;
|
||||||
|
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY_DEPRECATED)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_EAP6)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_EAP71)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
|
||||||
|
public class LifespanAdapterTest 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,
|
||||||
|
webArchive -> webArchive.addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/photoz/keycloak-cache-lifespan-authz-service.json"), "keycloak.json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPathConfigInvalidation() throws Exception {
|
||||||
|
loginToClientPage(aliceUser);
|
||||||
|
assertSuccess();
|
||||||
|
|
||||||
|
ResourceRepresentation resource = getAuthorizationResource().resources().findByName("Profile Resource").get(0);
|
||||||
|
AuthorizationResource authorizationResource = getAuthorizationResource();
|
||||||
|
|
||||||
|
authorizationResource.resources().resource(resource.getId()).remove();
|
||||||
|
|
||||||
|
loginToClientPage(aliceUser);
|
||||||
|
|
||||||
|
// should throw an error because the resource was removed and cache entry did not expire yet
|
||||||
|
clientPage.viewProfile(new ResponseValidator() {
|
||||||
|
@Override
|
||||||
|
public void validate(Map<String, Object> response) {
|
||||||
|
Object res = response.get("res");
|
||||||
|
assertThat(res, Matchers.notNullValue());
|
||||||
|
assertThat(res.toString(), Matchers.not(Matchers.containsString("userName")));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeOffsetOfAdapter(20);
|
||||||
|
|
||||||
|
loginToClientPage(aliceUser);
|
||||||
|
assertSuccess();
|
||||||
|
setTimeOffsetOfAdapter(0);
|
||||||
|
|
||||||
|
try (Response response = authorizationResource.resources().create(resource)) {
|
||||||
|
resource = response.readEntity(ResourceRepresentation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
loginToClientPage(aliceUser);
|
||||||
|
assertSuccess();
|
||||||
|
|
||||||
|
RealmResource realm = this.realmsResouce().realm(REALM_NAME);
|
||||||
|
UserRepresentation userRepresentation = realm.users().search(aliceUser.getUsername()).get(0);
|
||||||
|
UserResource userResource = realm.users().get(userRepresentation.getId());
|
||||||
|
|
||||||
|
userRepresentation.setEmail("alice@anotherdomain.org");
|
||||||
|
|
||||||
|
userResource.update(userRepresentation);
|
||||||
|
loginToClientPage(aliceUser);
|
||||||
|
assertTicket();
|
||||||
|
|
||||||
|
try {
|
||||||
|
PolicyRepresentation resourceInstancePermission = new PolicyRepresentation();
|
||||||
|
|
||||||
|
resourceInstancePermission.setName("View User Permission");
|
||||||
|
resourceInstancePermission.setType("resource");
|
||||||
|
|
||||||
|
Map<String, String> config = new HashMap<>();
|
||||||
|
|
||||||
|
config.put("resources", JsonSerialization.writeValueAsString(Arrays.asList(resource.getId())));
|
||||||
|
config.put("applyPolicies", JsonSerialization.writeValueAsString(Arrays.asList("Only From @keycloak.org or Admin")));
|
||||||
|
|
||||||
|
resourceInstancePermission.setConfig(config);
|
||||||
|
authorizationResource.policies().create(resourceInstancePermission);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Error creating policy.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
loginToClientPage(aliceUser);
|
||||||
|
// should throw an error because the resource was removed and cache entry did not expire yet
|
||||||
|
clientPage.viewProfile(new ResponseValidator() {
|
||||||
|
@Override
|
||||||
|
public void validate(Map<String, Object> response) {
|
||||||
|
Object res = response.get("res");
|
||||||
|
assertThat(res, Matchers.notNullValue());
|
||||||
|
assertThat(res.toString(), Matchers.not(Matchers.containsString("userName")));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
userRepresentation.setEmail("alice@keycloak.org");
|
||||||
|
|
||||||
|
userResource.update(userRepresentation);
|
||||||
|
loginToClientPage(aliceUser);
|
||||||
|
assertSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertSuccess() {
|
||||||
|
clientPage.viewProfile((ResponseValidator) response -> {
|
||||||
|
Object res = response.get("res");
|
||||||
|
assertThat(res, Matchers.notNullValue());
|
||||||
|
assertThat(res.toString(), Matchers.containsString("userName"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTicket() {
|
||||||
|
clientPage.viewProfile((ResponseValidator) response -> {
|
||||||
|
Object headers = response.get("responseHeaders");
|
||||||
|
assertThat(headers, Matchers.notNullValue());
|
||||||
|
assertThat(headers.toString(), Matchers.containsString("WWW-Authenticate: UMA"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeOffsetOfAdapter(int offset) {
|
||||||
|
this.driver.navigate().to(clientPage.getInjectedUrl() + "/timeOffset.jsp?offset=" + String.valueOf(offset));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.testsuite.adapter.example.authorization;
|
||||||
|
|
||||||
|
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.jboss.arquillian.container.test.api.Deployment;
|
||||||
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_WILDFLY_DEPRECATED)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_EAP)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_EAP6)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_UNDERTOW)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_EAP71)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT7)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT8)
|
||||||
|
@AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT9)
|
||||||
|
@EnableFeature(value = UPLOAD_SCRIPTS, skipRestart = true)
|
||||||
|
public class ServletPolicyEnforcerLifespanTest extends AbstractServletPolicyEnforcerTest {
|
||||||
|
|
||||||
|
@Deployment(name = RESOURCE_SERVER_ID, managed = false)
|
||||||
|
public static WebArchive deployment() {
|
||||||
|
return exampleDeployment(RESOURCE_SERVER_ID,
|
||||||
|
webArchive -> webArchive.addAsWebInfResource(
|
||||||
|
new File(TEST_APPS_HOME_DIR
|
||||||
|
+ "/servlet-policy-enforcer/servlet-policy-enforcer-lifespan-authz-service.json"),
|
||||||
|
"keycloak.json"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,41 +16,16 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.adapter.example.authorization;
|
package org.keycloak.testsuite.adapter.example.authorization;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||||
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
|
||||||
import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.jboss.arquillian.container.test.api.Deployer;
|
|
||||||
import org.jboss.arquillian.container.test.api.Deployment;
|
import org.jboss.arquillian.container.test.api.Deployment;
|
||||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
|
||||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
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.ResourcePermissionsResource;
|
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
|
||||||
import org.keycloak.testsuite.ProfileAssume;
|
|
||||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
import org.keycloak.testsuite.util.ServerURLs;
|
import org.keycloak.testsuite.util.ServerURLs;
|
||||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||||
import org.keycloak.testsuite.util.UIUtils;
|
|
||||||
import org.openqa.selenium.By;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -65,559 +40,10 @@ import org.openqa.selenium.By;
|
||||||
@AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT8)
|
@AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT8)
|
||||||
@AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT9)
|
@AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT9)
|
||||||
@EnableFeature(value = UPLOAD_SCRIPTS, skipRestart = true)
|
@EnableFeature(value = UPLOAD_SCRIPTS, skipRestart = true)
|
||||||
public class ServletPolicyEnforcerTest extends AbstractExampleAdapterTest {
|
public class ServletPolicyEnforcerTest extends AbstractServletPolicyEnforcerTest {
|
||||||
|
|
||||||
protected static final String REALM_NAME = "servlet-policy-enforcer-authz";
|
|
||||||
protected static final String RESOURCE_SERVER_ID = "servlet-policy-enforcer";
|
|
||||||
|
|
||||||
@ArquillianResource
|
|
||||||
private Deployer deployer;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
|
||||||
testRealms.add(
|
|
||||||
loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deployment(name = RESOURCE_SERVER_ID, managed = false)
|
@Deployment(name = RESOURCE_SERVER_ID, managed = false)
|
||||||
public static WebArchive deployment() throws IOException {
|
public static WebArchive deployment() {
|
||||||
return exampleDeployment(RESOURCE_SERVER_ID);
|
return exampleDeployment(RESOURCE_SERVER_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern1() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/resource/a/b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 1 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource/a/b");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 1 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource/a/b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern2() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/a/resource-a");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/b/resource-a");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 2 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/a/resource-a");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/b/resource-a");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 2 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/b/resource-a");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern3() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/a/resource-b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/b/resource-b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 3 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/a/resource-b");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/b/resource-b");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 3 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/b/resource-b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 2 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/b/resource-a");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 3 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/a/resource-b");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/b/resource-a");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern4() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/resource-c");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 4 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource-c");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 4 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource-c");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern5() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/a/a/resource-d");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/resource/b/resource-d");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 5 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/a/a/resource-d");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/a/b/resource-d");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 5 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/a/b/resource-d");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern6() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/resource/a");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/resource/b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 6 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource/a");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/resource/b");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 6 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource/b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern7() throws Exception {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/resource/a/f/b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/resource/c/f/d");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 7 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource/a/f/b");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/resource/c/f/d");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 7 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource/c/f/d");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern8() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/resource");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 8 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 8 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern9() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/file/*.suffix");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 9 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/file/*.suffix");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 9 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/file/*.suffix");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern10() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
|
|
||||||
navigateTo("/resource/a/i/b/c/d/e");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/resource/a/i/b/c/");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 10 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource/a/i/b/c/d/e");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/resource/a/i/b/c/d");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 10 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/resource/a/i/b/c/d");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPattern11UsingResourceInstancePermission() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/api/v1/resource-a");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/api/v1/resource-b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
ResourceRepresentation resource = new ResourceRepresentation("/api/v1/resource-c");
|
|
||||||
|
|
||||||
resource.setUri(resource.getName());
|
|
||||||
|
|
||||||
getAuthorizationResource().resources().create(resource);
|
|
||||||
|
|
||||||
createResourcePermission(resource.getName() + " permission", resource.getName(), "Default Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo(resource.getUri());
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies(resource.getName() + " permission", "Deny Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo(resource.getUri());
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies(resource.getName() + " permission", "Default Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo(resource.getUri());
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
navigateTo("/api/v1");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/api/v1/");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/api");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/api/");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPathWithPatternSlashAllAndResourceInstance() {
|
|
||||||
performTests(() -> {
|
|
||||||
ResourceRepresentation resource = new ResourceRepresentation("Pattern 15 Instance");
|
|
||||||
|
|
||||||
resource.setType("pattern-15");
|
|
||||||
resource.setUri("/keycloak-7148/1");
|
|
||||||
resource.setOwner("alice");
|
|
||||||
|
|
||||||
getAuthorizationResource().resources().create(resource).close();
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-7148/1");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7148/1/sub-a/2");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7148/1/sub-a");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7148/1/sub-a/2/sub-b");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 15 Permission", "Deny Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-7148/1");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-7148/1/sub-a/2");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-7148/1/sub-a");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-7148/1/sub-a/2/sub-b");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
// does not exist
|
|
||||||
navigateTo("/keycloak-7148/2");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPriorityOfURIForResource() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/realm_uri");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak_json_uri");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 12 Permission", "Deny Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/realm_uri");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak_json_uri");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 12 Permission", "Default Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/realm_uri");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak_json_uri");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testPathOrderWithAllPaths() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-6623");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-6623/sub-resource");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 13 Permission", "Deny Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-6623");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-6623/sub-resource");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 14 Permission", "Deny Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-6623");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-6623/sub-resource/resource");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMultipleUriForResourceJSONConfig() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-7269/sub-resource1");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource1/whatever/specialSuffix");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource2");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource2/w/h/a/t/e/v/e/r");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 16 Permission", "Deny Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-7269/sub-resource1");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource1/whatever/specialSuffix");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource2");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource2/w/h/a/t/e/v/e/r");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 16 Permission", "Default Policy");
|
|
||||||
navigateTo("/keycloak-7269/sub-resource1");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource1/whatever/specialSuffix");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource2");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-7269/sub-resource2/w/h/a/t/e/v/e/r");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOverloadedTemplateUri() {
|
|
||||||
performTests(() -> {
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-8823/resource/v1/subresource/123/entities");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-8823/resource/v1/subresource/123/someother");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 17 Entities Permission", "Deny Policy");
|
|
||||||
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-8823/resource/v1/subresource/123/entities");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
navigateTo("/keycloak-8823/resource/v1/subresource/123/someother");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 17 Entities Permission", "Default Policy");
|
|
||||||
updatePermissionPolicies("Pattern 17 Permission", "Deny Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-8823/resource/v1/subresource/123/entities");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-8823/resource/v1/subresource/123/someother");
|
|
||||||
assertTrue(wasDenied());
|
|
||||||
|
|
||||||
updatePermissionPolicies("Pattern 17 Entities Permission", "Default Policy");
|
|
||||||
updatePermissionPolicies("Pattern 17 Permission", "Default Policy");
|
|
||||||
login("alice", "alice");
|
|
||||||
navigateTo("/keycloak-8823/resource/v1/subresource/123/entities");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
navigateTo("/keycloak-8823/resource/v1/subresource/123/someother");
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void navigateTo(String path) {
|
|
||||||
this.driver.navigate().to(getResourceServerUrl() + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performTests(ExceptionRunnable assertion) {
|
|
||||||
performTests(() -> {}, assertion);
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private AuthorizationResource getAuthorizationResource() {
|
|
||||||
return getClientResource(RESOURCE_SERVER_ID).authorization();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClientResource getClientResource(String clientId) {
|
|
||||||
ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
|
|
||||||
ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
|
|
||||||
return clients.get(resourceServer.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logOut() {
|
|
||||||
navigateTo();
|
|
||||||
UIUtils.clickLink(driver.findElement(By.xpath("//a[text() = 'Sign Out']")));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void login(String username, String password) {
|
|
||||||
try {
|
|
||||||
navigateTo();
|
|
||||||
if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
|
|
||||||
logOut();
|
|
||||||
navigateTo();
|
|
||||||
}
|
|
||||||
this.loginPage.form().login(username, password);
|
|
||||||
navigateTo();
|
|
||||||
assertFalse(wasDenied());
|
|
||||||
} catch (Exception cause) {
|
|
||||||
throw new RuntimeException("Login failed", cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void navigateTo() {
|
|
||||||
this.driver.navigate().to(getResourceServerUrl() + "/");
|
|
||||||
waitForPageToLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean wasDenied() {
|
|
||||||
return this.driver.getPageSource().contains("You can not access this resource");
|
|
||||||
}
|
|
||||||
|
|
||||||
private URL getResourceServerUrl() {
|
|
||||||
try {
|
|
||||||
return new URL(ServerURLs.getAppServerContextRoot() + "/" + RESOURCE_SERVER_ID);
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
throw new RuntimeException("Could not obtain resource server url.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updatePermissionPolicies(String permissionName, String... policyNames) {
|
|
||||||
ResourcePermissionsResource permissions = getAuthorizationResource().permissions().resource();
|
|
||||||
ResourcePermissionRepresentation permission = permissions.findByName(permissionName);
|
|
||||||
|
|
||||||
permission.addPolicy(policyNames);
|
|
||||||
|
|
||||||
permissions.findById(permission.getId()).update(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createResourcePermission(String name, String resourceName, String... policyNames) {
|
|
||||||
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
|
|
||||||
|
|
||||||
permission.setName(name);
|
|
||||||
permission.addResource(resourceName);
|
|
||||||
permission.addPolicy(policyNames);
|
|
||||||
|
|
||||||
getAuthorizationResource().permissions().resource().create(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface ExceptionRunnable {
|
|
||||||
void run() throws Exception;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue