KEYCLOAK-13066 Include resourceType in ScopePermissionRepresentation
This commit is contained in:
parent
82d3251ab4
commit
666832d1be
5 changed files with 199 additions and 33 deletions
|
@ -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);
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)"),
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue