[KEYCLOAK-7148] - Associate sub resources to a parent resource

This commit is contained in:
pedroigor 2018-04-25 18:16:37 -03:00
parent fe2ae6ec68
commit 7ebcc69cb9
6 changed files with 111 additions and 18 deletions

View file

@ -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);

View file

@ -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;
}

View file

@ -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": []

View file

@ -69,6 +69,10 @@
{
"name": "Pattern 13",
"path": "/keycloak-6623/*"
},
{
"name": "Pattern 15",
"path": "/keycloak-7148/{id}/*"
}
]
}

View file

@ -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);
}

View file

@ -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/{pattern}/*", resources.get(0).getUri());
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-c/{pattern}/*", resources.get(0).getUri());
resources = authzClient.protection().resource().findByMatchingUri("/resources/a/b/c");