keycloak-aplcache
Changes
core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java 70(+1 -69)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java 4(+4 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PermissionsResource.java 28(+28 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java 7(+7 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionResource.java 69(+69 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java 42(+42 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java 7(+7 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java 5(+5 -0)
Details
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
index dc04991..e36d7af 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyRepresentation.java
@@ -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());
- }
}
\ No newline at end of file
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java
index 07276ec..de8d958 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/AuthorizationResource.java
@@ -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();
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PermissionsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PermissionsResource.java
new file mode 100644
index 0000000..6c16080
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PermissionsResource.java
@@ -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();
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
index e5120c6..2d31ab1 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
@@ -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
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionResource.java
new file mode 100644
index 0000000..ec49ec8
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionResource.java
@@ -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();
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java
new file mode 100644
index 0000000..d4dd0ed
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcePermissionsResource.java
@@ -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);
+
+}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java
index 3a48114..ddcaf53 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourceScopesResource.java
@@ -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);
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
index 07438d0..e7daaa1 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ResourcesResource.java
@@ -57,5 +57,10 @@ public interface ResourcesResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
+ List<ResourceRepresentation> findByName(@QueryParam("name") String name);
+
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> resources();
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 1d27f3d..766e275 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -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.");
}
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java
index 32a607f..4ada87d 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java
@@ -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
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
index 66a3d1a..6a9a32f 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
@@ -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);
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
index 7d3f8b4..865e8db 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
@@ -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);
+ }
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
index 7724830..1ec9a13 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
@@ -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();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java
new file mode 100644
index 0000000..b1572da
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourcePermissionManagementTest.java
@@ -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);
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
index da835d5..1b0b50b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
@@ -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() {