keycloak-uncached
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java 10(+10 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json 16(+16 -0)
testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json 4(+4 -0)
Details
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
index 2ccdf28..d5e46ad 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -281,8 +281,18 @@ public class PolicyEnforcer {
protected PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
if (originalConfig.hasPattern()) {
ProtectedResource resource = authzClient.protection().resource();
+
+ // search by an exact match
List<ResourceRepresentation> search = resource.findByUri(path);
+ // if exact match not found, try to obtain from current path the parent path.
+ // if path is /resource/1/test and pattern from pathConfig is /resource/{id}/*, parent path is /resource/1
+ // this logic allows to match sub resources of a resource instance (/resource/1) to the parent resource,
+ // so any permission granted to parent also applies to sub resources
+ if (search.isEmpty()) {
+ search = resource.findByUri(buildUriFromTemplate(originalConfig.getPath(), path, true));
+ }
+
if (!search.isEmpty()) {
ResourceRepresentation targetResource = search.get(0);
PathConfig config = PathConfig.createPathConfig(targetResource);
diff --git a/common/src/main/java/org/keycloak/common/util/PathMatcher.java b/common/src/main/java/org/keycloak/common/util/PathMatcher.java
index d56c408..6e0423f 100644
--- a/common/src/main/java/org/keycloak/common/util/PathMatcher.java
+++ b/common/src/main/java/org/keycloak/common/util/PathMatcher.java
@@ -46,7 +46,7 @@ public abstract class PathMatcher<P> {
}
if (isTemplate(expectedUri)) {
- String templateUri = buildUriFromTemplate(expectedUri, targetUri);
+ String templateUri = buildUriFromTemplate(expectedUri, targetUri, false);
if (templateUri != null) {
int length = expectedUri.split("\\/").length;
@@ -144,9 +144,14 @@ public abstract class PathMatcher<P> {
return false;
}
- public String buildUriFromTemplate(String expectedUri, String targetUri) {
+ protected String buildUriFromTemplate(String template, String targetUri, boolean onlyFirstParam) {
+ String expectedUri = template;
int patternStartIndex = expectedUri.indexOf("{");
+ if (expectedUri.endsWith("/*")) {
+ expectedUri = expectedUri.substring(0, expectedUri.length() - 2);
+ }
+
if (patternStartIndex == -1 || patternStartIndex >= targetUri.length()) {
return null;
}
@@ -195,6 +200,10 @@ public abstract class PathMatcher<P> {
}
i = expectedUri.indexOf('}', i);
+
+ if (i == expectedUri.lastIndexOf('}') && onlyFirstParam) {
+ return String.valueOf(matchingUri).substring(0, matchingUriLastIndex);
+ }
} else {
if (c == '/') {
paramIndex++;
@@ -204,6 +213,13 @@ public abstract class PathMatcher<P> {
}
if (matchingUri[matchingUri.length - 1] == '\u0000') {
+ if (template.endsWith("*")) {
+ StringBuilder firstParam = new StringBuilder(String.valueOf(matchingUri).substring(0, matchingUriLastIndex));
+
+ firstParam.append(targetUri.substring(firstParam.length()));
+
+ return firstParam.toString();
+ }
return null;
}
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
index bad1b25..9e984b5 100644
--- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json
@@ -115,6 +115,11 @@
{
"name": "Pattern 14",
"uri": "/keycloak-6623/sub-resource/*"
+ },
+ {
+ "name": "Pattern 15",
+ "type": "pattern-15",
+ "uri": "/keycloak-7148/{id}"
}
],
"policies": [
@@ -286,6 +291,17 @@
"resources": "[\"Pattern 14\"]",
"applyPolicies": "[\"Default Policy\"]"
}
+ },
+ {
+ "name": "Pattern 15 Permission",
+ "type": "resource",
+ "logic": "POSITIVE",
+ "decisionStrategy": "UNANIMOUS",
+ "config": {
+ "defaultResourceType": "pattern-15",
+ "default": "true",
+ "applyPolicies": "[\"Default Policy\"]"
+ }
}
],
"scopes": []
diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
index d6e7f00..43735e0 100644
--- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/src/main/webapp/WEB-INF/keycloak.json
@@ -69,6 +69,10 @@
{
"name": "Pattern 13",
"path": "/keycloak-6623/*"
+ },
+ {
+ "name": "Pattern 15",
+ "path": "/keycloak-7148/{id}/*"
}
]
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
index 9afed01..648f36c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractServletPolicyEnforcerTest.java
@@ -73,7 +73,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern1() throws Exception {
+ public void testPattern1() {
performTests(() -> {
login("alice", "alice");
@@ -93,7 +93,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern2() throws Exception {
+ public void testPattern2() {
performTests(() -> {
login("alice", "alice");
@@ -117,7 +117,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern3() throws Exception {
+ public void testPattern3() {
performTests(() -> {
login("alice", "alice");
@@ -153,7 +153,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern4() throws Exception {
+ public void testPattern4() {
performTests(() -> {
login("alice", "alice");
@@ -173,7 +173,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern5() throws Exception {
+ public void testPattern5() {
performTests(() -> {
login("alice", "alice");
@@ -197,7 +197,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern6() throws Exception {
+ public void testPattern6() {
performTests(() -> {
login("alice", "alice");
@@ -221,7 +221,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern7() throws Exception {
+ public void testPattern7() {
performTests(() -> {
login("alice", "alice");
@@ -245,7 +245,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern8() throws Exception {
+ public void testPattern8() {
performTests(() -> {
login("alice", "alice");
@@ -265,7 +265,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern9() throws Exception {
+ public void testPattern9() {
performTests(() -> {
login("alice", "alice");
@@ -285,7 +285,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern10() throws Exception {
+ public void testPattern10() {
performTests(() -> {
login("alice", "alice");
@@ -309,7 +309,7 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
}
@Test
- public void testPattern11UsingResourceInstancePermission() throws Exception {
+ public void testPattern11UsingResourceInstancePermission() {
performTests(() -> {
login("alice", "alice");
navigateTo("/api/v1/resource-a");
@@ -406,6 +406,45 @@ public abstract class AbstractServletPolicyEnforcerTest extends AbstractExampleA
});
}
+ @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());
+ });
+ }
+
private void navigateTo(String path) {
this.driver.navigate().to(getResourceServerUrl() + path);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
index bbff687..5800071 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
@@ -44,7 +44,9 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
public void testFindMatchingUri() {
doCreateResource(new ResourceRepresentation("/*", Collections.emptySet(), "/*", null));
doCreateResource(new ResourceRepresentation("/resources/*", Collections.emptySet(), "/resources/*", null));
- doCreateResource(new ResourceRepresentation("/resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/*", null));
+ doCreateResource(new ResourceRepresentation("/resources-a/*", Collections.emptySet(), "/resources-a/*", null));
+ doCreateResource(new ResourceRepresentation("/resources-b/{pattern}", Collections.emptySet(), "/resources-b/{pattern}", null));
+ doCreateResource(new ResourceRepresentation("/resources-c/{pattern}/*", Collections.emptySet(), "/resources-c/{pattern}/*", null));
doCreateResource(new ResourceRepresentation("/resources/{pattern}/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/{pattern}/*", null));
doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resources/{pattern}/*", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
doCreateResource(new ResourceRepresentation("/resources/{pattern}/sub-resource", Collections.emptySet(), "/resources/{pattern}/sub-resources/{pattern}/*", null));
@@ -57,11 +59,11 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
assertEquals(1, resources.size());
assertEquals("/*", resources.get(0).getUri());
- resources = authzClient.protection().resource().findByMatchingUri("/resources/test");
+ resources = authzClient.protection().resource().findByMatchingUri("/resources-a/test");
assertNotNull(resources);
assertEquals(1, resources.size());
- assertEquals("/resources/*", resources.get(0).getUri());
+ assertEquals("/resources-a/*", resources.get(0).getUri());
resources = authzClient.protection().resource().findByMatchingUri("/resources");
@@ -69,11 +71,17 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
assertEquals(1, resources.size());
assertEquals("/resources/*", resources.get(0).getUri());
- resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b");
+ resources = authzClient.protection().resource().findByMatchingUri("/resources-b/a");
+
+ assertNotNull(resources);
+ assertEquals(1, resources.size());
+ assertEquals("/resources-b/{pattern}", resources.get(0).getUri());
+
+ resources = authzClient.protection().resource().findByMatchingUri("/resources-c/a/b");
assertNotNull(resources);
assertEquals(1, resources.size());
- assertEquals("/resources/{pattern}/*", resources.get(0).getUri());
+ assertEquals("/resources-c/{pattern}/*", resources.get(0).getUri());
resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b/c");