Tests for new permission management rest api
This commit is contained in:
parent
cf1e8d1dd8
commit
8e64bc3e4d
17 changed files with 537 additions and 205 deletions
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue