[KEYCLOAK-7148] - Associate sub resources to a parent resource
This commit is contained in:
parent
fe2ae6ec68
commit
7ebcc69cb9
6 changed files with 111 additions and 18 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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": []
|
||||
|
|
|
@ -69,6 +69,10 @@
|
|||
{
|
||||
"name": "Pattern 13",
|
||||
"path": "/keycloak-6623/*"
|
||||
},
|
||||
{
|
||||
"name": "Pattern 15",
|
||||
"path": "/keycloak-7148/{id}/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
Loading…
Reference in a new issue