Tests for new permission management rest api

This commit is contained in:
Pedro Igor 2017-04-08 11:56:01 -03:00
parent cf1e8d1dd8
commit 8e64bc3e4d
17 changed files with 537 additions and 205 deletions

View file

@ -18,53 +18,14 @@ package org.keycloak.representations.idm.authorization;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyRepresentation {
public class PolicyRepresentation extends AbstractPolicyRepresentation {
private String id;
private String name;
private String description;
private String type;
private Logic logic = Logic.POSITIVE;
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
private Map<String, String> config = new HashMap();
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public DecisionStrategy getDecisionStrategy() {
return this.decisionStrategy;
}
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
this.decisionStrategy = decisionStrategy;
}
public Logic getLogic() {
return logic;
}
public void setLogic(Logic logic) {
this.logic = logic;
}
public Map<String, String> getConfig() {
return this.config;
}
@ -72,33 +33,4 @@ public class PolicyRepresentation {
public void setConfig(Map<String, String> config) {
this.config = config;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final PolicyRepresentation policy = (PolicyRepresentation) o;
return Objects.equals(getId(), policy.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}

View file

@ -23,6 +23,7 @@ import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@ -58,4 +59,7 @@ public interface AuthorizationResource {
@Path("/policy")
PoliciesResource policies();
@Path("/permission")
PermissionsResource permissions();
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.admin.client.resource;
import javax.ws.rs.Path;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface PermissionsResource {
@Path("resource")
ResourcePermissionsResource resource();
}

View file

@ -28,6 +28,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@ -45,6 +46,12 @@ public interface PoliciesResource {
@Path("{id}")
PolicyResource policy(@PathParam("id") String id);
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
PolicyRepresentation findByName(@QueryParam("name") String name);
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache

View file

@ -0,0 +1,69 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.admin.client.resource;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface ResourcePermissionResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ResourcePermissionRepresentation toRepresentation();
@PUT
@Consumes(MediaType.APPLICATION_JSON)
void update(ResourcePermissionRepresentation representation);
@DELETE
void remove();
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View file

@ -0,0 +1,42 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.admin.client.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface ResourcePermissionsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
Response create(ResourcePermissionRepresentation representation);
@Path("{id}")
ResourcePermissionResource findById(@PathParam("id") String id);
}

View file

@ -25,6 +25,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@ -46,4 +47,10 @@ public interface ResourceScopesResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ScopeRepresentation> scopes();
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ScopeRepresentation findByName(@QueryParam("name") String name);
}

View file

@ -54,6 +54,11 @@ public interface ResourcesResource {
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> findByName(@QueryParam("name") String name);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)

View file

@ -2113,7 +2113,7 @@ public class RepresentationToModel {
type = "rules";
}
if (authorization.getProvider(type) == null) {
throw new RuntimeException("Unknown polucy type [" + type + "]. Could not find a provider for this type.");
throw new RuntimeException("Unknown policy type [" + type + "]. Could not find a provider for this type.");
}
}

View file

@ -28,18 +28,14 @@ import org.keycloak.services.resources.admin.RealmAuth;
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PermissionService extends PolicyService {
public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(resourceServer, authorization, auth);
}
@Override
protected Object doCreatePolicyTypeResource(String type) {
return new PermissionTypeService(type, resourceServer, authorization, auth);
}
@Override
protected PolicyResourceService doCreatePolicyResource(Policy policy) {
return new PermissionResourceService(policy, resourceServer, authorization, auth);
return new PolicyTypeResourceService(policy, resourceServer, authorization, auth);
}
@Override

View file

@ -1,94 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.authorization.admin;
import static org.keycloak.models.utils.RepresentationToModel.toModel;
import java.io.IOException;
import javax.ws.rs.Path;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PermissionTypeService extends PolicyService {
private final String type;
PermissionTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(resourceServer, authorization, auth);
this.type = type;
}
@Path("/provider")
public Object getPolicyAdminResourceProvider() {
PolicyProviderAdminService resource = getPolicyProviderAdminResource(type);
ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource;
}
@Override
protected Object doCreatePolicyResource(Policy policy) {
return new PermissionResourceService(policy, resourceServer,authorization, auth);
}
@Override
protected Object doCreate(String payload) {
PolicyProviderAdminService provider = getPolicyProviderAdminResource(type);
AbstractPolicyRepresentation representation = toRepresentation(type, payload, provider);
Policy policy = toModel(representation, this.resourceServer, authorization);
if (provider != null) {
try {
provider.onCreate(policy, representation);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return representation;
}
private AbstractPolicyRepresentation toRepresentation(String type, String payload, PolicyProviderAdminService provider) {
Class<? extends AbstractPolicyRepresentation> representationType = provider.getRepresentationType();
if (representationType == null) {
throw new RuntimeException("Policy provider for type [" + type + "] returned a null representation type.");
}
AbstractPolicyRepresentation representation;
try {
representation = JsonSerialization.readValue(payload, representationType);
} catch (IOException e) {
throw new RuntimeException("Failed to deserialize JSON using policy provider for type [" + type + "].", e);
}
return representation;
}
}

View file

@ -50,8 +50,10 @@ import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.Constants;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
@ -72,10 +74,10 @@ public class PolicyService {
@Path("{type}")
public Object getResource(@PathParam("type") String type) {
PolicyProviderFactory providerFactory = authorization.getProviderFactory(type);
PolicyProviderFactory providerFactory = getPolicyProviderFactory(type);
if (providerFactory != null) {
return doCreatePolicyTypeResource(type);
return new PolicyTypeService(type, resourceServer, authorization, auth);
}
Policy policy = authorization.getStoreFactory().getPolicyStore().findById(type, resourceServer.getId());
@ -83,10 +85,6 @@ public class PolicyService {
return doCreatePolicyResource(policy);
}
protected Object doCreatePolicyTypeResource(String type) {
return new PolicyTypeService(type, resourceServer, authorization, auth);
}
protected Object doCreatePolicyResource(Policy policy) {
return new PolicyResourceService(policy, resourceServer, authorization, auth);
}
@ -95,13 +93,39 @@ public class PolicyService {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@NoCache
@Deprecated
public Response create(String payload) {
this.auth.requireManage();
return Response.status(Status.CREATED).entity(doCreate(payload)).build();
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
Policy existing = authorization.getStoreFactory().getPolicyStore().findByName(representation.getName(), resourceServer.getId());
if (existing != null) {
return ErrorResponse.exists("Policy with name [" + representation.getName() + "] already exists");
}
Policy policy = doCreate(representation);
PolicyProviderAdminService provider = getPolicyProviderAdminResource(representation.getType());
if (provider != null) {
try {
provider.onCreate(policy, representation);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
representation.setId(policy.getId());
return Response.status(Status.CREATED).entity(representation).build();
}
protected Object doCreate(String payload) {
protected Policy doCreate(AbstractPolicyRepresentation representation) {
return create(PolicyRepresentation.class.cast(representation));
}
protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
PolicyRepresentation representation;
try {
@ -110,12 +134,10 @@ public class PolicyService {
throw new RuntimeException("Failed to deserialize representation", cause);
}
create(representation);
return representation;
}
public void create(PolicyRepresentation representation) {
public Policy create(PolicyRepresentation representation) {
Policy policy = toModel(representation, this.resourceServer, authorization);
PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType());
@ -127,7 +149,7 @@ public class PolicyService {
}
}
representation.setId(policy.getId());
return policy;
}
protected Object toRepresentation(Policy model) {
@ -268,7 +290,7 @@ public class PolicyService {
}
protected PolicyProviderAdminService getPolicyProviderAdminResource(String policyType) {
PolicyProviderFactory providerFactory = authorization.getProviderFactory(policyType);
PolicyProviderFactory providerFactory = getPolicyProviderFactory(policyType);
if (providerFactory != null) {
return providerFactory.getAdminResource(resourceServer, authorization);
@ -277,6 +299,10 @@ public class PolicyService {
return null;
}
private PolicyProviderFactory getPolicyProviderFactory(String policyType) {
return authorization.getProviderFactory(policyType);
}
private void findAssociatedPolicies(Policy policy, List<Policy> policies) {
policy.getAssociatedPolicies().forEach(associated -> {
policies.add(associated);

View file

@ -31,9 +31,9 @@ import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PermissionResourceService extends PolicyResourceService {
public class PolicyTypeResourceService extends PolicyResourceService {
public PermissionResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(policy, resourceServer, authorization, auth);
}
@ -82,10 +82,6 @@ public class PermissionResourceService extends PolicyResourceService {
representation.setType(policy.getType());
representation.setDecisionStrategy(policy.getDecisionStrategy());
representation.setLogic(policy.getLogic());
representation.addResource(policy.getResources().stream().map(resource -> resource.getId()).findFirst().orElse(null));
representation.addPolicies(policy.getAssociatedPolicies().stream().map(associated -> associated.getId()).toArray(value -> new String[value]));
representation.addScopes(policy.getScopes().stream().map(associated -> associated.getId()).toArray(value -> new String[value]));
return representation;
}
}

View file

@ -16,13 +16,20 @@
*/
package org.keycloak.authorization.admin;
import static org.keycloak.models.utils.RepresentationToModel.toModel;
import java.io.IOException;
import javax.ws.rs.Path;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@ -44,4 +51,36 @@ public class PolicyTypeService extends PolicyService {
return resource;
}
@Override
protected Object doCreatePolicyResource(Policy policy) {
return new PolicyTypeResourceService(policy, resourceServer,authorization, auth);
}
@Override
protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
PolicyProviderAdminService provider = getPolicyProviderAdminResource(type);
Class<? extends AbstractPolicyRepresentation> representationType = provider.getRepresentationType();
if (representationType == null) {
throw new RuntimeException("Policy provider for type [" + type + "] returned a null representation type.");
}
AbstractPolicyRepresentation representation;
try {
representation = JsonSerialization.readValue(payload, representationType);
} catch (IOException e) {
throw new RuntimeException("Failed to deserialize JSON using policy provider for type [" + type + "].", e);
}
representation.setType(type);
return representation;
}
@Override
protected Policy doCreate(AbstractPolicyRepresentation representation) {
return toModel(representation, resourceServer, authorization);
}
}

View file

@ -41,6 +41,7 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@ -69,8 +70,8 @@ public class ScopeService {
}
@POST
@Consumes("application/json")
@Produces("application/json")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response create(ScopeRepresentation scope) {
this.auth.requireManage();
Scope model = toModel(scope, this.resourceServer, authorization);
@ -82,8 +83,8 @@ public class ScopeService {
@Path("{id}")
@PUT
@Consumes("application/json")
@Produces("application/json")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response update(@PathParam("id") String id, ScopeRepresentation scope) {
this.auth.requireManage();
scope.setId(id);
@ -134,7 +135,7 @@ public class ScopeService {
@Path("{id}")
@GET
@Produces("application/json")
@Produces(MediaType.APPLICATION_JSON)
public Response findById(@PathParam("id") String id) {
this.auth.requireView();
Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id, resourceServer.getId());
@ -148,7 +149,7 @@ public class ScopeService {
@Path("{id}/resources")
@GET
@Produces("application/json")
@Produces(MediaType.APPLICATION_JSON)
public Response getResources(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = this.authorization.getStoreFactory();
@ -170,7 +171,7 @@ public class ScopeService {
@Path("{id}/permissions")
@GET
@Produces("application/json")
@Produces(MediaType.APPLICATION_JSON)
public Response getPermissions(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = this.authorization.getStoreFactory();
@ -195,7 +196,7 @@ public class ScopeService {
@Path("/search")
@GET
@Produces("application/json")
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response find(@QueryParam("name") String name) {
this.auth.requireView();

View file

@ -0,0 +1,263 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.admin.client.authorization;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Response;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.ResourcePermissionResource;
import org.keycloak.admin.client.resource.ResourcePermissionsResource;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.authorization.client.representation.ResourceRepresentation;
import org.keycloak.authorization.client.representation.ScopeRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class ResourcePermissionManagementTest extends AbstractKeycloakTest {
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
testRealms.add(RealmBuilder.create().name("authz-test")
.user(UserBuilder.create().username("marta").password("password"))
.user(UserBuilder.create().username("kolo").password("password"))
.client(ClientBuilder.create().clientId("resource-server-test")
.secret("secret")
.authorizationServicesEnabled(true)
.redirectUris("http://localhost/resource-server-test")
.defaultRoles("uma_protection")
.directAccessGrants())
.build());
}
@Before
public void configureAuthorization() throws Exception {
createResourcesAndScopes();
RealmResource realm = getRealm();
createPolicies(realm, getClient(realm));
}
@Test
public void testCreateResourcePermission() {
AuthorizationResource authorization = getClient(getRealm()).authorization();
ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
representation.setName("Resource A Permission");
representation.setDescription("description");
representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
representation.setLogic(Logic.NEGATIVE);
representation.addResource(getResourceId("Resource A", authorization));
representation.addPolicies(getPolicyIds(Arrays.asList("Only Marta Policy", "Only Kolo Policy"), authorization).stream().toArray((IntFunction<String[]>) value -> new String[value]));
assertCreated(authorization, representation);
}
@Test
public void testCreateResourceType() {
AuthorizationResource authorization = getClient(getRealm()).authorization();
ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
representation.setName("Resource A Type Permission");
representation.setDescription("description");
representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
representation.setLogic(Logic.NEGATIVE);
representation.setResourceType("test-resource");
representation.addPolicies(getPolicyIds(Arrays.asList("Only Marta Policy"), authorization).stream().toArray((IntFunction<String[]>) value -> new String[value]));
assertCreated(authorization, representation);
}
@Test
public void testDelete() {
AuthorizationResource authorization = getClient(getRealm()).authorization();
ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
representation.setName("Test Delete Permission");
representation.setResourceType("test-resource");
representation.addPolicies(getPolicyIds(Arrays.asList("Only Marta Policy"), authorization).stream().toArray((IntFunction<String[]>) value -> new String[value]));
ResourcePermissionsResource permissions = authorization.permissions().resource();
Response response = permissions.create(representation);
ResourcePermissionRepresentation created = response.readEntity(ResourcePermissionRepresentation.class);
permissions.findById(created.getId()).remove();
ResourcePermissionResource removed = permissions.findById(created.getId());
try {
removed.toRepresentation();
fail("Permission not removed");
} catch (NotFoundException ignore) {
}
}
@Test
public void failCreateWithSameName() {
AuthorizationResource authorization = getClient(getRealm()).authorization();
ResourcePermissionRepresentation permission1 = new ResourcePermissionRepresentation();
permission1.setName("Conflicting Name Permission");
permission1.setResourceType("test-resource");
permission1.addPolicies(getPolicyIds(Arrays.asList("Only Marta Policy"), authorization).stream().toArray((IntFunction<String[]>) value -> new String[value]));
ResourcePermissionsResource permissions = authorization.permissions().resource();
permissions.create(permission1);
ResourcePermissionRepresentation permission2 = new ResourcePermissionRepresentation();
permission2.setName(permission1.getName());
Response response = permissions.create(permission2);
assertEquals(Response.Status.CONFLICT.getStatusCode(), response.getStatus());
}
private void assertCreated(AuthorizationResource authorization, ResourcePermissionRepresentation representation) {
ResourcePermissionsResource permissions = authorization.permissions().resource();
Response response = permissions.create(representation);
ResourcePermissionRepresentation created = response.readEntity(ResourcePermissionRepresentation.class);
assertNotNull(created);
assertNotNull(created.getId());
ResourcePermissionResource permission = permissions.findById(created.getId());
ResourcePermissionRepresentation found = permission.toRepresentation();
assertNotNull(found);
assertEquals(created.getId(), found.getId());
assertEquals(created.getName(), found.getName());
assertEquals(created.getDescription(), found.getDescription());
assertEquals(created.getDecisionStrategy(), found.getDecisionStrategy());
assertEquals(created.getLogic(), found.getLogic());
assertEquals(created.getResourceType(), found.getResourceType());
assertNull(found.getResources());
assertNull(found.getPolicies());
assertEquals(representation.getPolicies().size(), permission.associatedPolicies().stream().map(representation1 -> representation1.getId()).filter(policyId -> representation.getPolicies().contains(policyId)).count());
if (representation.getResources() != null) {
assertEquals(representation.getResources().size(), permission.resources().stream().map(representation1 -> representation1.getId()).filter(resourceId -> representation.getResources().contains(resourceId)).count());
} else {
assertTrue(permission.resources().isEmpty());
}
}
private void createResourcesAndScopes() throws IOException {
AuthzClient authzClient = getAuthzClient();
Set<ScopeRepresentation> scopes = new HashSet<>();
scopes.add(new ScopeRepresentation("read"));
scopes.add(new ScopeRepresentation("write"));
scopes.add(new ScopeRepresentation("execute"));
List<ResourceRepresentation> resources = new ArrayList<>();
resources.add(new ResourceRepresentation("Resource A", scopes));
resources.add(new ResourceRepresentation("Resource B", scopes));
resources.add(new ResourceRepresentation("Resource C", scopes));
resources.forEach(resource -> authzClient.protection().resource().create(resource));
}
private void createPolicies(RealmResource realm, ClientResource client) throws IOException {
createUserPolicy("Only Marta Policy", realm, client, "marta");
createUserPolicy("Only Kolo Policy", realm, client, "kolo");
}
private void createUserPolicy(String name, RealmResource realm, ClientResource client, String username) throws IOException {
String userId = realm.users().search(username).stream().map(representation -> representation.getId()).findFirst().orElseThrow(() -> new RuntimeException("Expected user [userId]"));
PolicyRepresentation representation = new PolicyRepresentation();
representation.setName(name);
representation.setType("user");
Map<String, String> config = new HashMap<>();
config.put("users", JsonSerialization.writeValueAsString(new String[] {userId}));
representation.setConfig(config);
client.authorization().policies().create(representation);
}
private ClientResource getClient(RealmResource realm) {
ClientsResource clients = realm.clients();
return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
}
private RealmResource getRealm() {
try {
return AdminClientUtil.createAdminClient().realm("authz-test");
} catch (Exception cause) {
throw new RuntimeException("Failed to create admin client", cause);
}
}
private String getResourceId(String resourceName, AuthorizationResource authorization) {
return authorization.resources().findByName(resourceName).stream().map(representation -> representation.getId()).findFirst().orElseThrow(() -> new RuntimeException("Expected user [userId]"));
}
private List<String> getPolicyIds(List<String> policies, AuthorizationResource authorization) {
return policies.stream().map(policyName -> authorization.policies().findByName(policyName).getId()).collect(Collectors.toList());
}
private AuthzClient getAuthzClient() {
try {
return AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/default-keycloak.json"), Configuration.class));
} catch (IOException cause) {
throw new RuntimeException("Failed to create authz client", cause);
}
}
}

View file

@ -33,6 +33,7 @@ import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.RealmResource;
@ -192,8 +193,9 @@ public class ConflictingScopePermissionTest extends AbstractKeycloakTest {
}
private void createResourcePermission(String name, String resourceName, List<String> policies, ClientResource client) throws IOException {
String resourceId = client.authorization().resources().find(resourceName, null, null, null, null, -1, -1).stream().map(representation -> representation.getId()).findFirst().orElseThrow(() -> new RuntimeException("Expected user [userId]"));
List<String> policyIds = client.authorization().policies().policies().stream().filter(representation -> policies.contains(representation.getName())).map(representation -> representation.getId()).collect(Collectors.toList());
AuthorizationResource authorization = client.authorization();
String resourceId = getResourceId(resourceName, authorization);
List<String> policyIds = getPolicyIds(policies, authorization);
PolicyRepresentation representation = new PolicyRepresentation();
@ -207,18 +209,23 @@ public class ConflictingScopePermissionTest extends AbstractKeycloakTest {
representation.setConfig(config);
client.authorization().policies().create(representation);
authorization.policies().create(representation);
}
private String getResourceId(String resourceName, AuthorizationResource authorization) {
return authorization.resources().findByName(resourceName).stream().map(representation -> representation.getId()).findFirst().orElseThrow(() -> new RuntimeException("Expected user [userId]"));
}
private void createScopePermission(String name, String resourceName, List<String> scopes, List<String> policies, ClientResource client) throws IOException {
AuthorizationResource authorization = client.authorization();
String resourceId = null;
if (resourceName != null) {
resourceId = client.authorization().resources().find(resourceName, null, null, null, null, -1, -1).stream().map(representation -> representation.getId()).findFirst().orElseThrow(() -> new RuntimeException("Expected user [userId]"));
resourceId = getResourceId(resourceName, authorization);
}
List<String> scopeIds = client.authorization().scopes().scopes().stream().filter(representation -> policies.contains(representation.getName())).map(representation -> representation.getId()).collect(Collectors.toList());
List<String> policyIds = client.authorization().policies().policies().stream().filter(representation -> policies.contains(representation.getName())).map(representation -> representation.getId()).collect(Collectors.toList());
List<String> scopeIds = scopes.stream().map(scopeName -> authorization.scopes().findByName(scopeName).getId()).collect(Collectors.toList());
List<String> policyIds = getPolicyIds(policies, authorization);
PolicyRepresentation representation = new PolicyRepresentation();
@ -236,7 +243,11 @@ public class ConflictingScopePermissionTest extends AbstractKeycloakTest {
representation.setConfig(config);
client.authorization().policies().create(representation);
authorization.policies().create(representation);
}
private List<String> getPolicyIds(List<String> policies, AuthorizationResource authorization) {
return policies.stream().map(policyName -> authorization.policies().findByName(policyName).getId()).collect(Collectors.toList());
}
private AuthzClient getAuthzClient() {