KEYCLOAK-13066 Include resourceType in ScopePermissionRepresentation

This commit is contained in:
Álvaro Gómez Giménez 2020-02-21 10:46:10 +01:00 committed by Pedro Igor
parent 82d3251ab4
commit 666832d1be
5 changed files with 199 additions and 33 deletions

View file

@ -79,19 +79,14 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory<Reso
private void updateResourceType(Policy policy, ResourcePermissionRepresentation representation) { private void updateResourceType(Policy policy, ResourcePermissionRepresentation representation) {
if (representation != null) { if (representation != null) {
//TODO: remove this check once we migrate to new API
if (ResourcePermissionRepresentation.class.equals(representation.getClass())) {
ResourcePermissionRepresentation resourcePermission = ResourcePermissionRepresentation.class.cast(representation);
Map<String, String> config = new HashMap(policy.getConfig()); Map<String, String> config = new HashMap(policy.getConfig());
config.compute("defaultResourceType", (key, value) -> { config.compute("defaultResourceType", (key, value) -> {
String resourceType = resourcePermission.getResourceType(); String resourceType = representation.getResourceType();
return resourceType != null ? resourcePermission.getResourceType() : null; return resourceType != null ? representation.getResourceType() : null;
}); });
policy.setConfig(config); policy.setConfig(config);
}
} }
} }

View file

@ -25,6 +25,9 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation; import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
import java.util.HashMap;
import java.util.Map;
/** /**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/ */
@ -59,7 +62,32 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory<ScopePe
@Override @Override
public ScopePermissionRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) { public ScopePermissionRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
return new ScopePermissionRepresentation(); ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
representation.setResourceType(policy.getConfig().get("defaultResourceType"));
return representation;
}
@Override
public void onCreate(Policy policy, ScopePermissionRepresentation representation, AuthorizationProvider authorization) {
updateResourceType(policy, representation);
}
@Override
public void onUpdate(Policy policy, ScopePermissionRepresentation representation, AuthorizationProvider authorization) {
updateResourceType(policy, representation);
}
private void updateResourceType(Policy policy, ScopePermissionRepresentation representation) {
if (representation != null) {
Map<String, String> config = new HashMap(policy.getConfig());
config.compute("defaultResourceType", (key, value) -> {
String resourceType = representation.getResourceType();
return resourceType != null ? representation.getResourceType() : null;
});
policy.setConfig(config);
}
} }
@Override @Override

View file

@ -21,8 +21,18 @@ package org.keycloak.representations.idm.authorization;
*/ */
public class ScopePermissionRepresentation extends AbstractPolicyRepresentation { public class ScopePermissionRepresentation extends AbstractPolicyRepresentation {
private String resourceType;
@Override @Override
public String getType() { public String getType() {
return "scope"; return "scope";
} }
public void setResourceType(String resourceType) {
this.resourceType = resourceType;
}
public String getResourceType() {
return resourceType;
}
} }

View file

@ -18,10 +18,7 @@
package org.keycloak.authorization.jpa.entities; package org.keycloak.authorization.jpa.entities;
import java.util.HashMap; import java.util.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.persistence.Access; import javax.persistence.Access;
import javax.persistence.AccessType; import javax.persistence.AccessType;
@ -59,7 +56,7 @@ import org.keycloak.representations.idm.authorization.Logic;
@NamedQuery(name="findPolicyIdByResource", query="select p from PolicyEntity p inner join p.resources r inner join fetch p.associatedPolicies a where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"), @NamedQuery(name="findPolicyIdByResource", query="select p from PolicyEntity p inner join p.resources r inner join fetch p.associatedPolicies a where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"),
@NamedQuery(name="findPolicyIdByScope", query="select pe from PolicyEntity pe inner join pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds) and p.id = pe.id))"), @NamedQuery(name="findPolicyIdByScope", query="select pe from PolicyEntity pe inner join pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds) and p.id = pe.id))"),
@NamedQuery(name="findPolicyIdByResourceScope", query="select pe from PolicyEntity pe inner join pe.resources r inner join pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds) and p.id = pe.id)) and exists (select p.id from ResourceEntity r inner join r.policies p where r.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.id = pe.id and p.type = 'scope' and r.id in (:resourceId)))"), @NamedQuery(name="findPolicyIdByResourceScope", query="select pe from PolicyEntity pe inner join pe.resources r inner join pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds) and p.id = pe.id)) and exists (select p.id from ResourceEntity r inner join r.policies p where r.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.id = pe.id and p.type = 'scope' and r.id in (:resourceId)))"),
@NamedQuery(name="findPolicyIdByNullResourceScope", query="select pe from PolicyEntity pe inner join pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.id = pe.id and p.type = 'scope' and s.id in (:scopeIds))) and pe.resources is empty"), @NamedQuery(name="findPolicyIdByNullResourceScope", query="select pe from PolicyEntity pe left join fetch pe.config c inner join pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.id = pe.id and p.type = 'scope' and s.id in (:scopeIds))) and pe.resources is empty and not exists (select pec from pe.config pec where KEY(pec) = 'defaultResourceType')"),
@NamedQuery(name="findPolicyIdByType", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type"), @NamedQuery(name="findPolicyIdByType", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type"),
@NamedQuery(name="findPolicyIdByResourceType", query="select p from PolicyEntity p inner join p.config c inner join fetch p.associatedPolicies a where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type"), @NamedQuery(name="findPolicyIdByResourceType", query="select p from PolicyEntity p inner join p.config c inner join fetch p.associatedPolicies a where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type"),
@NamedQuery(name="findPolicyIdByDependentPolices", query="select p.id from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)"), @NamedQuery(name="findPolicyIdByDependentPolices", query="select p.id from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)"),

View file

@ -780,7 +780,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
} }
@Test @Test
public void testObtainAllEntitlementsForResource() throws Exception { public void testObtainAllEntitlementsForResourceWithResourcePermission() throws Exception {
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST); ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
AuthorizationResource authorization = client.authorization(); AuthorizationResource authorization = client.authorization();
@ -849,6 +849,11 @@ public class EntitlementAPITest extends AbstractAuthzTest {
request.addPermission(resource.getId(), "scope:view", "scope:update", "scope:delete"); request.addPermission(resource.getId(), "scope:view", "scope:update", "scope:delete");
response = authzClient.authorization(accessToken).authorize(request);
assertNotNull(response.getToken());
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
assertEquals(1, permissions.size());
for (Permission grantedPermission : permissions) { for (Permission grantedPermission : permissions) {
assertEquals(resource.getId(), grantedPermission.getResourceId()); assertEquals(resource.getId(), grantedPermission.getResourceId());
assertEquals(2, grantedPermission.getScopes().size()); assertEquals(2, grantedPermission.getScopes().size());
@ -856,6 +861,87 @@ public class EntitlementAPITest extends AbstractAuthzTest {
} }
} }
@Test
public void testObtainAllEntitlementsForResourceWithScopePermission() throws Exception {
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
AuthorizationResource authorization = client.authorization();
JSPolicyRepresentation policy = new JSPolicyRepresentation();
policy.setName(KeycloakModelUtils.generateId());
policy.setCode("$evaluation.grant();");
authorization.policies().js().create(policy).close();
ResourceRepresentation resourceWithoutType = new ResourceRepresentation();
resourceWithoutType.setName(KeycloakModelUtils.generateId());
resourceWithoutType.addScope("scope:view", "scope:update", "scope:delete");
try (Response response = authorization.resources().create(resourceWithoutType)) {
resourceWithoutType = response.readEntity(ResourceRepresentation.class);
}
ResourceRepresentation resourceWithType = new ResourceRepresentation();
resourceWithType.setName(KeycloakModelUtils.generateId());
resourceWithType.setType("type-one");
resourceWithType.addScope("scope:view", "scope:update", "scope:delete");
try (Response response = authorization.resources().create(resourceWithType)) {
resourceWithType = response.readEntity(ResourceRepresentation.class);
}
ScopePermissionRepresentation permission = new ScopePermissionRepresentation();
permission.setName(KeycloakModelUtils.generateId());
permission.addResource(resourceWithoutType.getId());
permission.addScope("scope:view");
permission.addPolicy(policy.getName());
authorization.permissions().scope().create(permission).close();
permission = new ScopePermissionRepresentation();
permission.setName(KeycloakModelUtils.generateId());
permission.setResourceType("type-one");
permission.addScope("scope:update");
permission.addPolicy(policy.getName());
authorization.permissions().scope().create(permission).close();
String accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "kolo", "password").getAccessToken();
AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG);
AuthorizationRequest request = new AuthorizationRequest();
request.addPermission(resourceWithoutType.getId(), "scope:view", "scope:update", "scope:delete");
AuthorizationResponse response = authzClient.authorization(accessToken).authorize(request);
assertNotNull(response.getToken());
Collection<Permission> permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
assertEquals(1, permissions.size());
for (Permission grantedPermission : permissions) {
assertEquals(resourceWithoutType.getId(), grantedPermission.getResourceId());
assertEquals(1, grantedPermission.getScopes().size());
assertTrue(grantedPermission.getScopes().containsAll(Arrays.asList("scope:view")));
}
request = new AuthorizationRequest();
request.addPermission(resourceWithType.getId(), "scope:view", "scope:update", "scope:delete");
response = authzClient.authorization(accessToken).authorize(request);
assertNotNull(response.getToken());
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
assertEquals(1, permissions.size());
for (Permission grantedPermission : permissions) {
assertEquals(resourceWithType.getId(), grantedPermission.getResourceId());
assertEquals(1, grantedPermission.getScopes().size());
assertTrue(grantedPermission.getScopes().containsAll(Arrays.asList("scope:update")));
}
}
@Test @Test
public void testServerDecisionStrategy() throws Exception { public void testServerDecisionStrategy() throws Exception {
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST); ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
@ -1040,29 +1126,59 @@ public class EntitlementAPITest extends AbstractAuthzTest {
authorization.resources().create(resource).close(); authorization.resources().create(resource).close();
} }
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation(); for (int i = 0; i < 10; i++) {
ResourceRepresentation resource = new ResourceRepresentation();
permission.setName(KeycloakModelUtils.generateId()); resource.setType("type-four");
permission.setResourceType("type-one"); resource.setName(KeycloakModelUtils.generateId());
permission.addPolicy(policy.getName()); resource.addScope("scope:view", "scope:update");
authorization.permissions().resource().create(permission).close(); authorization.resources().create(resource).close();
}
permission = new ResourcePermissionRepresentation(); for (int i = 0; i < 10; i++) {
ResourceRepresentation resource = new ResourceRepresentation();
permission.setName(KeycloakModelUtils.generateId()); resource.setType("type-five");
permission.setResourceType("type-two"); resource.setName(KeycloakModelUtils.generateId());
permission.addPolicy(policy.getName()); resource.addScope("scope:view");
authorization.permissions().resource().create(permission).close(); authorization.resources().create(resource).close();
}
permission = new ResourcePermissionRepresentation();
permission.setName(KeycloakModelUtils.generateId()); ResourcePermissionRepresentation resourcePermission = new ResourcePermissionRepresentation();
permission.setResourceType("type-three");
permission.addPolicy(policy.getName());
authorization.permissions().resource().create(permission).close(); resourcePermission.setName(KeycloakModelUtils.generateId());
resourcePermission.setResourceType("type-one");
resourcePermission.addPolicy(policy.getName());
authorization.permissions().resource().create(resourcePermission).close();
resourcePermission = new ResourcePermissionRepresentation();
resourcePermission.setName(KeycloakModelUtils.generateId());
resourcePermission.setResourceType("type-two");
resourcePermission.addPolicy(policy.getName());
authorization.permissions().resource().create(resourcePermission).close();
resourcePermission = new ResourcePermissionRepresentation();
resourcePermission.setName(KeycloakModelUtils.generateId());
resourcePermission.setResourceType("type-three");
resourcePermission.addPolicy(policy.getName());
authorization.permissions().resource().create(resourcePermission).close();
ScopePermissionRepresentation scopePersmission = new ScopePermissionRepresentation();
scopePersmission.setName(KeycloakModelUtils.generateId());
scopePersmission.setResourceType("type-four");
scopePersmission.addScope("scope:view");
scopePersmission.addPolicy(policy.getName());
authorization.permissions().scope().create(scopePersmission).close();
String accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "kolo", "password").getAccessToken(); String accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "kolo", "password").getAccessToken();
AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG); AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG);
@ -1081,6 +1197,26 @@ public class EntitlementAPITest extends AbstractAuthzTest {
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions(); permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
assertEquals(10, permissions.size()); assertEquals(10, permissions.size());
request = new AuthorizationRequest();
request.addPermission("resource-type:type-four", "scope:view");
response = authzClient.authorization(accessToken).authorize(request);
assertNotNull(response.getToken());
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
assertEquals(10, permissions.size());
for (Permission grantedPermission : permissions) {
assertEquals(1, grantedPermission.getScopes().size());
assertTrue(grantedPermission.getScopes().containsAll(Arrays.asList("scope:view")));
}
request = new AuthorizationRequest();
request.addPermission("resource-type:type-five", "scope:view");
try {
authzClient.authorization(accessToken).authorize(request);
fail("no type-five resources can be granted since scope permission for scope:view only applies to type-four");
} catch (RuntimeException expected) {
assertEquals(403, HttpResponseException.class.cast(expected.getCause()).getStatusCode());
assertTrue(HttpResponseException.class.cast(expected.getCause()).toString().contains("access_denied"));
}
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
ResourceRepresentation resource = new ResourceRepresentation(); ResourceRepresentation resource = new ResourceRepresentation();