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) {
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) -> {
String resourceType = resourcePermission.getResourceType();
return resourceType != null ? resourcePermission.getResourceType() : null;
});
config.compute("defaultResourceType", (key, value) -> {
String resourceType = representation.getResourceType();
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.representations.idm.authorization.ScopePermissionRepresentation;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@ -59,7 +62,32 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory<ScopePe
@Override
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

View file

@ -21,8 +21,18 @@ package org.keycloak.representations.idm.authorization;
*/
public class ScopePermissionRepresentation extends AbstractPolicyRepresentation {
private String resourceType;
@Override
public String getType() {
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;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import javax.persistence.Access;
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="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="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="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)"),

View file

@ -780,7 +780,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
}
@Test
public void testObtainAllEntitlementsForResource() throws Exception {
public void testObtainAllEntitlementsForResourceWithResourcePermission() throws Exception {
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
AuthorizationResource authorization = client.authorization();
@ -849,6 +849,11 @@ public class EntitlementAPITest extends AbstractAuthzTest {
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) {
assertEquals(resource.getId(), grantedPermission.getResourceId());
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
public void testServerDecisionStrategy() throws Exception {
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
@ -1040,29 +1126,59 @@ public class EntitlementAPITest extends AbstractAuthzTest {
authorization.resources().create(resource).close();
}
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
for (int i = 0; i < 10; i++) {
ResourceRepresentation resource = new ResourceRepresentation();
permission.setName(KeycloakModelUtils.generateId());
permission.setResourceType("type-one");
permission.addPolicy(policy.getName());
resource.setType("type-four");
resource.setName(KeycloakModelUtils.generateId());
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());
permission.setResourceType("type-two");
permission.addPolicy(policy.getName());
resource.setType("type-five");
resource.setName(KeycloakModelUtils.generateId());
resource.addScope("scope:view");
authorization.permissions().resource().create(permission).close();
authorization.resources().create(resource).close();
}
permission = new ResourcePermissionRepresentation();
permission.setName(KeycloakModelUtils.generateId());
permission.setResourceType("type-three");
permission.addPolicy(policy.getName());
ResourcePermissionRepresentation resourcePermission = new ResourcePermissionRepresentation();
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();
AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG);
@ -1081,6 +1197,26 @@ public class EntitlementAPITest extends AbstractAuthzTest {
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
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++) {
ResourceRepresentation resource = new ResourceRepresentation();