diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyAdminResource.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyAdminResource.java index 0cfb0f9f32..0a5acce7c0 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyAdminResource.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyAdminResource.java @@ -19,8 +19,8 @@ package org.keycloak.authorization.policy.provider.aggregated; 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 java.util.ArrayList; import java.util.List; @@ -30,19 +30,13 @@ import java.util.List; */ public class AggregatePolicyAdminResource implements PolicyProviderAdminService { - private final ResourceServer resourceServer; - - public AggregatePolicyAdminResource(ResourceServer resourceServer) { - this.resourceServer = resourceServer; - } - @Override - public void onCreate(Policy policy) { + public void onCreate(Policy policy, AbstractPolicyRepresentation representation) { verifyCircularReference(policy, new ArrayList<>()); } @Override - public void onUpdate(Policy policy) { + public void onUpdate(Policy policy, AbstractPolicyRepresentation representation) { verifyCircularReference(policy, new ArrayList<>()); } diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java index 3e8697335a..24bb09dd2e 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java @@ -49,8 +49,8 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { - return new AggregatePolicyAdminResource(resourceServer); + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { + return new AggregatePolicyAdminResource(); } @Override diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java index 8cb00293a4..8f7e631c7f 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java @@ -40,7 +40,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { return null; } diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java index b4a5099ebd..617727f7c3 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java @@ -42,7 +42,7 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { return null; } diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java index d7a6b2bc0e..b6ffd74078 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/resource/ResourcePolicyProviderFactory.java @@ -1,13 +1,17 @@ package org.keycloak.authorization.policy.provider.resource; +import java.util.Map; + import org.keycloak.Config; import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.policy.provider.PolicyProvider; import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; import org.keycloak.authorization.policy.provider.PolicyProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation; /** * @author Pedro Igor @@ -32,8 +36,51 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { - return null; + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { + return new PolicyProviderAdminService() { + @Override + public void onCreate(Policy policy, ResourcePermissionRepresentation representation) { + updateResourceType(policy, representation); + } + + @Override + public void onUpdate(Policy policy, ResourcePermissionRepresentation representation) { + updateResourceType(policy, representation); + } + + private void updateResourceType(Policy policy, ResourcePermissionRepresentation representation) { + //TODO: remove this check once we migrate to new API + if (representation != null) { + Map config = policy.getConfig(); + + config.compute("defaultResourceType", (key, value) -> { + String resourceType = representation.getResourceType(); + return resourceType != null ? representation.getResourceType() : null; + }); + + policy.setConfig(config); + } + } + + @Override + public void onRemove(Policy policy) { + + } + + @Override + public Class getRepresentationType() { + return ResourcePermissionRepresentation.class; + } + + @Override + public ResourcePermissionRepresentation toRepresentation(Policy policy) { + ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation(); + + representation.setResourceType(policy.getConfig().get("defaultResourceType")); + + return representation; + } + }; } @Override diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java index 33db2d5473..ab262d1289 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java @@ -42,7 +42,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Consumer; /** * @author Pedro Igor @@ -67,7 +66,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { return null; } diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java index 0678eb3152..1c28a5fa83 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/scope/ScopePolicyProviderFactory.java @@ -2,12 +2,14 @@ package org.keycloak.authorization.policy.provider.scope; import org.keycloak.Config; import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.policy.provider.PolicyProvider; import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; import org.keycloak.authorization.policy.provider.PolicyProviderFactory; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation; /** * @author Pedro Igor @@ -32,8 +34,33 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { - return null; + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { + return new PolicyProviderAdminService() { + @Override + public void onCreate(Policy policy, ScopePermissionRepresentation representation) { + + } + + @Override + public void onUpdate(Policy policy, ScopePermissionRepresentation representation) { + + } + + @Override + public void onRemove(Policy policy) { + + } + + @Override + public Class getRepresentationType() { + return ScopePermissionRepresentation.class; + } + + @Override + public ScopePermissionRepresentation toRepresentation(Policy policy) { + return new ScopePermissionRepresentation(); + } + }; } @Override diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java index 96c1d24d25..e62602f05b 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyAdminResource.java @@ -20,6 +20,7 @@ package org.keycloak.authorization.policy.provider.time; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; +import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; import java.text.SimpleDateFormat; @@ -29,7 +30,7 @@ import java.text.SimpleDateFormat; public class TimePolicyAdminResource implements PolicyProviderAdminService { @Override - public void onCreate(Policy policy) { + public void onCreate(Policy policy, AbstractPolicyRepresentation representation) { validateConfig(policy); } @@ -44,7 +45,7 @@ public class TimePolicyAdminResource implements PolicyProviderAdminService { } @Override - public void onUpdate(Policy policy) { + public void onUpdate(Policy policy, AbstractPolicyRepresentation representation) { validateConfig(policy); } diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java index 94c5aad754..4e9fd9e8fa 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java @@ -32,7 +32,7 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { return new TimePolicyAdminResource(); } diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java index 09345ec8e3..a89830f44f 100644 --- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java +++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java @@ -62,7 +62,7 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { return null; } diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java index c6e570157e..f334789ada 100644 --- a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java +++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyAdminResource.java @@ -17,8 +17,8 @@ package org.keycloak.authorization.policy.provider.drools; 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.representations.idm.authorization.PolicyRepresentation; import org.kie.api.runtime.KieContainer; @@ -33,21 +33,19 @@ import javax.ws.rs.core.Response; */ public class DroolsPolicyAdminResource implements PolicyProviderAdminService { - private final ResourceServer resourceServer; private final DroolsPolicyProviderFactory factory; - public DroolsPolicyAdminResource(ResourceServer resourceServer, DroolsPolicyProviderFactory factory) { - this.resourceServer = resourceServer; + public DroolsPolicyAdminResource(DroolsPolicyProviderFactory factory) { this.factory = factory; } @Override - public void onCreate(Policy policy) { + public void onCreate(Policy policy, AbstractPolicyRepresentation representation) { this.factory.update(policy); } @Override - public void onUpdate(Policy policy) { + public void onUpdate(Policy policy, AbstractPolicyRepresentation representation) { this.factory.update(policy); } diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java index ce0f834ad7..d3dda336e3 100644 --- a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java +++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java @@ -49,8 +49,8 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { - return new DroolsPolicyAdminResource(resourceServer, this); + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { + return new DroolsPolicyAdminResource(this); } @Override diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java new file mode 100644 index 0000000000..ccd5ee1762 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java @@ -0,0 +1,132 @@ +/* + * 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.representations.idm.authorization; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * @author Pedro Igor + */ +public class AbstractPolicyRepresentation { + + private String id; + private String name; + private String description; + private String type; + private Set policies; + private Set resources; + private Set scopes; + private Logic logic = Logic.POSITIVE; + private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS; + + 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 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; + } + + public Set getPolicies() { + return policies; + } + + public void addPolicies(String... id) { + if (this.policies == null) { + this.policies = new HashSet<>(); + } + this.policies.addAll(Arrays.asList(id)); + } + + public Set getResources() { + return resources; + } + + public void addResource(String id) { + if (this.resources == null) { + this.resources = new HashSet<>(); + } + this.resources.add(id); + } + + public Set getScopes() { + return scopes; + } + + public void addScopes(String... id) { + if (this.scopes == null) { + this.scopes = new HashSet<>(); + } + this.scopes.addAll(Arrays.asList(id)); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final AbstractPolicyRepresentation policy = (AbstractPolicyRepresentation) o; + return Objects.equals(getId(), policy.getId()); + } + + @Override + public int hashCode() { + return Objects.hash(getId()); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourcePermissionRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourcePermissionRepresentation.java new file mode 100644 index 0000000000..ebc4cec324 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourcePermissionRepresentation.java @@ -0,0 +1,33 @@ +/* + * 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.representations.idm.authorization; + +/** + * @author Pedro Igor + */ +public class ResourcePermissionRepresentation extends AbstractPolicyRepresentation { + + private String resourceType; + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public String getResourceType() { + return resourceType; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ScopePermissionRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ScopePermissionRepresentation.java new file mode 100644 index 0000000000..f9d9767c42 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ScopePermissionRepresentation.java @@ -0,0 +1,23 @@ +/* + * 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.representations.idm.authorization; + +/** + * @author Pedro Igor + */ +public class ScopePermissionRepresentation extends AbstractPolicyRepresentation { +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java index 9cd4e74ae6..fba7eb58dd 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java +++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/CachedPolicyStore.java @@ -285,6 +285,7 @@ public class CachedPolicyStore implements PolicyStore { public void removeScope(Scope scope) { getDelegateForUpdate().removeScope(getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())); cached.removeScope(scope); + scopes.remove(scope); } @Override @@ -297,6 +298,7 @@ public class CachedPolicyStore implements PolicyStore { public void removeAssociatedPolicy(Policy associatedPolicy) { getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId())); cached.removeAssociatedPolicy(associatedPolicy); + associatedPolicies.remove(associatedPolicy); } @Override @@ -309,6 +311,7 @@ public class CachedPolicyStore implements PolicyStore { public void removeResource(Resource resource) { getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId())); cached.removeResource(resource); + resources.remove(resource); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java index 775cf64421..c7bef79092 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java +++ b/model/infinispan/src/main/java/org/keycloak/models/authorization/infinispan/entities/CachedPolicy.java @@ -160,7 +160,7 @@ public class CachedPolicy implements Policy, Serializable { @Override public void removeResource(Resource resource) { - this.resourcesIds.add(resource.getId()); + this.resourcesIds.remove(resource.getId()); } @Override diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java index d26208efff..ec7b6c9d44 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java @@ -19,15 +19,24 @@ package org.keycloak.authorization.policy.provider; import org.keycloak.authorization.model.Policy; +import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; /** * @author Pedro Igor */ -public interface PolicyProviderAdminService { +public interface PolicyProviderAdminService { - void onCreate(Policy policy); + void onCreate(Policy policy, R representation); - void onUpdate(Policy policy); + void onUpdate(Policy policy, R representation); void onRemove(Policy policy); + + default AbstractPolicyRepresentation toRepresentation(Policy policy) { + return null; + } + + default Class getRepresentationType() { + return null; + } } diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java index f7041b58ae..8385964ca8 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java @@ -19,7 +19,6 @@ package org.keycloak.authorization.policy.provider; import org.keycloak.authorization.AuthorizationProvider; -import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.provider.ProviderFactory; @@ -34,5 +33,5 @@ public interface PolicyProviderFactory extends ProviderFactory { PolicyProvider create(AuthorizationProvider authorization); - PolicyProviderAdminService getAdminResource(ResourceServer resourceServer); + PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization); } 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 2557bfd085..3693b9ecc0 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 @@ -25,6 +25,7 @@ import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.policy.provider.PolicyProvider; +import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.authorization.store.ResourceStore; @@ -92,6 +93,7 @@ import org.keycloak.representations.idm.UserConsentRepresentation; import org.keycloak.representations.idm.UserFederationMapperRepresentation; import org.keycloak.representations.idm.UserFederationProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation; @@ -2101,6 +2103,168 @@ public class RepresentationToModel { return null; } + public static Policy toModel(AbstractPolicyRepresentation representation, ResourceServer resourceServer, AuthorizationProvider authorization) { + String type = representation.getType(); + PolicyProvider provider = authorization.getProvider(type); + + if (provider == null) { + //TODO: temporary, remove this check on future versions as drools type is now deprecated + if ("drools".equalsIgnoreCase(type)) { + type = "rules"; + } + if (authorization.getProvider(type) == null) { + throw new RuntimeException("Unknown polucy type [" + type + "]. Could not find a provider for this type."); + } + } + + PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore(); + Policy existing; + + if (representation.getId() != null) { + existing = policyStore.findById(representation.getId(), resourceServer.getId()); + } else { + existing = policyStore.findByName(representation.getName(), resourceServer.getId()); + } + + if (existing != null) { + existing.setName(representation.getName()); + existing.setDescription(representation.getDescription()); + existing.setDecisionStrategy(representation.getDecisionStrategy()); + existing.setLogic(representation.getLogic()); + + updatePolicy(existing, representation, authorization); + + return existing; + } + + Policy model = policyStore.create(representation.getName(), type, resourceServer); + + model.setDescription(representation.getDescription()); + model.setDecisionStrategy(representation.getDecisionStrategy()); + model.setLogic(representation.getLogic()); + + updatePolicy(model, representation, authorization); + + representation.setId(model.getId()); + + return model; + } + + private static void updatePolicy(Policy policy, AbstractPolicyRepresentation representation, AuthorizationProvider authorization) { + ResourceServer resourceServer = policy.getResourceServer(); + StoreFactory storeFactory = authorization.getStoreFactory(); + Set newResources = representation.getResources(); + + if (newResources != null && !newResources.isEmpty()) { + Set associatedResources = policy.getResources(); + String newResourceId = newResources.iterator().next(); + + if (newResourceId != null) { + Resource newResource = storeFactory.getResourceStore().findById(newResourceId, resourceServer.getId()); + + if (newResource == null) { + throw new RuntimeException("Resource with id [" + newResourceId + "] does not exist"); + } + + if (!associatedResources.isEmpty()) { + Resource associatedResource = associatedResources.iterator().next(); + + if (!associatedResource.getId().equals(newResource.getId())) { + policy.removeResource(associatedResource); + } + } + + policy.addResource(newResource); + } else { + for (Resource resource : new ArrayList<>(associatedResources)) { + policy.removeResource(resource); + } + } + } else { + for (Resource associatedResource : new HashSet(policy.getResources())) { + policy.removeResource(associatedResource); + } + } + + PolicyStore policyStore = storeFactory.getPolicyStore(); + Set policies = representation.getPolicies(); + + for (String policyId : policies) { + boolean hasPolicy = false; + + for (Policy policyModel : new HashSet(policy.getAssociatedPolicies())) { + if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) { + hasPolicy = true; + } + } + + + if (!hasPolicy) { + Policy associatedPolicy = policyStore.findById(policyId, resourceServer.getId()); + + if (associatedPolicy == null) { + associatedPolicy = policyStore.findByName(policyId, resourceServer.getId()); + } + + policy.addAssociatedPolicy(associatedPolicy); + } + } + + for (Policy policyModel : new HashSet(policy.getAssociatedPolicies())) { + boolean hasPolicy = false; + + for (String policyId : policies) { + if (policyModel.getId().equals(policyId) || policyModel.getName().equals(policyId)) { + hasPolicy = true; + } + } + if (!hasPolicy) { + policy.removeAssociatedPolicy(policyModel); + ; + } + } + + Set newScopes = representation.getScopes(); + + if (newScopes != null && !newScopes.isEmpty()) { + for (String scopeId : newScopes) { + boolean hasScope = false; + + for (Scope scopeModel : new HashSet(policy.getScopes())) { + if (scopeModel.getId().equals(scopeId)) { + hasScope = true; + } + } + if (!hasScope) { + Scope scope = storeFactory.getScopeStore().findById(scopeId, resourceServer.getId()); + + if (scope == null) { + storeFactory.getScopeStore().findByName(scopeId, resourceServer.getId()); + } + + policy.addScope(scope); + } + } + + for (Scope scopeModel : new HashSet(policy.getScopes())) { + boolean hasScope = false; + + for (String scopeId : newScopes) { + if (scopeModel.getId().equals(scopeId)) { + hasScope = true; + } + } + if (!hasScope) { + policy.removeScope(scopeModel); + } + } + } else { + for (Scope associatedScope : new HashSet(policy.getScopes())) { + policy.removeScope(associatedScope); + } + } + } + public static Policy toModel(PolicyRepresentation policy, ResourceServer resourceServer, AuthorizationProvider authorization) { String type = policy.getType(); PolicyProvider provider = authorization.getProvider(type); diff --git a/services/src/main/java/org/keycloak/authorization/admin/PermissionResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PermissionResourceService.java new file mode 100644 index 0000000000..6d4583d131 --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/PermissionResourceService.java @@ -0,0 +1,91 @@ +/* + * 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 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 Pedro Igor + */ +public class PermissionResourceService extends PolicyResourceService { + + public PermissionResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + super(policy, resourceServer, authorization, auth); + } + + @Override + protected void doUpdate(Policy policy, String payload) { + String type = policy.getType(); + PolicyProviderAdminService provider = getPolicyProviderAdminResource(type); + AbstractPolicyRepresentation representation = toRepresentation(type, payload, provider); + + policy = toModel(representation, policy.getResourceServer(), authorization); + + try { + provider.onUpdate(policy, representation); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + 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; + } + + @Override + protected Object toRepresentation(Policy policy) { + PolicyProviderAdminService provider = getPolicyProviderAdminResource(policy.getType()); + return toRepresentation(policy, provider.toRepresentation(policy)); + } + + private AbstractPolicyRepresentation toRepresentation(Policy policy, AbstractPolicyRepresentation representation) { + representation.setId(policy.getId()); + representation.setName(policy.getName()); + representation.setDescription(policy.getDescription()); + 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; + } +} diff --git a/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java new file mode 100644 index 0000000000..32a607f976 --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java @@ -0,0 +1,50 @@ +/* + * 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 java.util.List; +import java.util.Map; + +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.services.resources.admin.RealmAuth; + +/** + * @author Pedro Igor + */ +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); + } + + @Override + protected List doSearch(Integer firstResult, Integer maxResult, Map filters) { + filters.put("permission", new String[] {Boolean.TRUE.toString()}); + return super.doSearch(firstResult, maxResult, filters); + } +} diff --git a/services/src/main/java/org/keycloak/authorization/admin/PermissionTypeService.java b/services/src/main/java/org/keycloak/authorization/admin/PermissionTypeService.java new file mode 100644 index 0000000000..d2b598a335 --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/PermissionTypeService.java @@ -0,0 +1,94 @@ +/* + * 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 Pedro Igor + */ +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; + } +} diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java new file mode 100644 index 0000000000..003db4cb3e --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java @@ -0,0 +1,221 @@ +/* + * 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 java.util.List; +import java.util.stream.Collectors; + +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.Response; +import javax.ws.rs.core.Response.Status; + +import org.jboss.resteasy.annotations.cache.NoCache; +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.authorization.policy.provider.PolicyProviderFactory; +import org.keycloak.authorization.store.PolicyStore; +import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ResourceRepresentation; +import org.keycloak.representations.idm.authorization.ScopeRepresentation; +import org.keycloak.services.resources.admin.RealmAuth; +import org.keycloak.util.JsonSerialization; + +/** + * @author Pedro Igor + */ +public class PolicyResourceService { + + private final Policy policy; + protected final ResourceServer resourceServer; + protected final AuthorizationProvider authorization; + protected final RealmAuth auth; + + public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { + this.policy = policy; + this.resourceServer = resourceServer; + this.authorization = authorization; + this.auth = auth; + } + + @PUT + @Consumes("application/json") + @Produces("application/json") + @NoCache + public Response update(String payload) { + this.auth.requireManage(); + + doUpdate(policy, payload); + + return Response.status(Status.CREATED).build(); + } + + protected void doUpdate(Policy policy, String payload) { + PolicyRepresentation representation; + + try { + representation = JsonSerialization.readValue(payload, PolicyRepresentation.class); + } catch (IOException cause) { + throw new RuntimeException("Failed to deserialize representation", cause); + } + + representation.setId(policy.getId()); + + policy = toModel(representation, resourceServer, authorization); + + PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType()); + + if (resource != null) { + try { + resource.onUpdate(policy, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @DELETE + public Response delete() { + this.auth.requireManage(); + StoreFactory storeFactory = authorization.getStoreFactory(); + PolicyStore policyStore = storeFactory.getPolicyStore(); + PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType()); + + if (resource != null) { + try { + resource.onRemove(policy); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> { + if (dependentPolicy.getAssociatedPolicies().size() == 1) { + policyStore.delete(dependentPolicy.getId()); + } else { + dependentPolicy.removeAssociatedPolicy(policy); + } + }); + + policyStore.delete(policy.getId()); + + return Response.noContent().build(); + } + + @GET + @Produces("application/json") + @NoCache + public Response findById() { + this.auth.requireView(); + return Response.ok(toRepresentation(policy)).build(); + } + + protected Object toRepresentation(Policy model) { + return ModelToRepresentation.toRepresentation(model); + } + + @Path("/dependentPolicies") + @GET + @Produces("application/json") + @NoCache + public Response getDependentPolicies() { + this.auth.requireView(); + List policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(policy.getId(), resourceServer.getId()); + + return Response.ok(policies.stream().map(policy -> { + PolicyRepresentation representation1 = new PolicyRepresentation(); + + representation1.setId(policy.getId()); + representation1.setName(policy.getName()); + representation1.setType(policy.getType()); + + return representation1; + }).collect(Collectors.toList())).build(); + } + + @Path("/scopes") + @GET + @Produces("application/json") + @NoCache + public Response getScopes() { + this.auth.requireView(); + return Response.ok(policy.getScopes().stream().map(scope -> { + ScopeRepresentation representation = new ScopeRepresentation(); + + representation.setId(scope.getId()); + representation.setName(scope.getName()); + + return representation; + }).collect(Collectors.toList())).build(); + } + + @Path("/resources") + @GET + @Produces("application/json") + @NoCache + public Response getResources() { + this.auth.requireView(); + + return Response.ok(policy.getResources().stream().map(resource -> { + ResourceRepresentation representation = new ResourceRepresentation(); + + representation.setId(resource.getId()); + representation.setName(resource.getName()); + + return representation; + }).collect(Collectors.toList())).build(); + } + + @Path("/associatedPolicies") + @GET + @Produces("application/json") + @NoCache + public Response getAssociatedPolicies() { + this.auth.requireView(); + + return Response.ok(policy.getAssociatedPolicies().stream().map(policy -> { + PolicyRepresentation representation1 = new PolicyRepresentation(); + + representation1.setId(policy.getId()); + representation1.setName(policy.getName()); + representation1.setType(policy.getType()); + + return representation1; + }).collect(Collectors.toList())).build(); + } + + protected PolicyProviderAdminService getPolicyProviderAdminResource(String policyType) { + PolicyProviderFactory providerFactory = authorization.getProviderFactory(policyType); + + if (providerFactory != null) { + return providerFactory.getAdminResource(resourceServer, authorization); + } + + return null; + } +} 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 8982a298f1..3151b494c3 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java @@ -17,9 +17,9 @@ */ package org.keycloak.authorization.admin; -import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; import static org.keycloak.models.utils.RepresentationToModel.toModel; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -29,14 +29,13 @@ import java.util.Map; import java.util.stream.Collectors; import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; 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.QueryParam; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; @@ -48,24 +47,22 @@ import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; import org.keycloak.authorization.policy.provider.PolicyProviderFactory; import org.keycloak.authorization.store.PolicyStore; -import org.keycloak.authorization.store.ResourceStore; -import org.keycloak.authorization.store.ScopeStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.models.Constants; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation; import org.keycloak.representations.idm.authorization.PolicyRepresentation; -import org.keycloak.representations.idm.authorization.ResourceRepresentation; -import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.services.resources.admin.RealmAuth; +import org.keycloak.util.JsonSerialization; /** * @author Pedro Igor */ public class PolicyService { - private final ResourceServer resourceServer; - private final AuthorizationProvider authorization; - private final RealmAuth auth; + protected final ResourceServer resourceServer; + protected final AuthorizationProvider authorization; + protected final RealmAuth auth; public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) { this.resourceServer = resourceServer; @@ -73,210 +70,80 @@ public class PolicyService { this.auth = auth; } - @POST - @Consumes("application/json") - @Produces("application/json") - @NoCache - public Response create(PolicyRepresentation representation) { + @Path("{type}") + public Object getResource(@PathParam("type") String type) { this.auth.requireManage(); + PolicyProviderFactory providerFactory = authorization.getProviderFactory(type); + + if (providerFactory != null) { + return doCreatePolicyTypeResource(type); + } + + Policy policy = authorization.getStoreFactory().getPolicyStore().findById(type, resourceServer.getId()); + + if (policy == null) { + return Response.status(Status.NOT_FOUND).build(); + } + + 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); + } + + @POST + @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(); + } + + protected Object doCreate(String payload) { + PolicyRepresentation representation; + + try { + representation = JsonSerialization.readValue(payload, PolicyRepresentation.class); + } catch (IOException cause) { + throw new RuntimeException("Failed to deserialize representation", cause); + } + + create(representation); + + return representation; + } + + public void create(PolicyRepresentation representation) { Policy policy = toModel(representation, this.resourceServer, authorization); - PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization); + PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType()); if (resource != null) { try { - resource.onCreate(policy); + resource.onCreate(policy, null); } catch (Exception e) { throw new RuntimeException(e); } } representation.setId(policy.getId()); - - return Response.status(Status.CREATED).entity(representation).build(); } - @Path("{id}") - @PUT - @Consumes("application/json") - @Produces("application/json") - @NoCache - public Response update(@PathParam("id") String id, PolicyRepresentation representation) { - this.auth.requireManage(); - representation.setId(id); - StoreFactory storeFactory = authorization.getStoreFactory(); - Policy policy = storeFactory.getPolicyStore().findById(representation.getId(), resourceServer.getId()); - - if (policy == null) { - return Response.status(Status.NOT_FOUND).build(); - } - - policy = toModel(representation, resourceServer, authorization); - - PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization); - - if (resource != null) { - try { - resource.onUpdate(policy); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - return Response.status(Status.CREATED).build(); - } - - @Path("{id}") - @DELETE - public Response delete(@PathParam("id") String id) { - this.auth.requireManage(); - StoreFactory storeFactory = authorization.getStoreFactory(); - PolicyStore policyStore = storeFactory.getPolicyStore(); - Policy policy = policyStore.findById(id, resourceServer.getId()); - - if (policy == null) { - return Response.status(Status.NOT_FOUND).build(); - } - - PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization); - - if (resource != null) { - try { - resource.onRemove(policy); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - policyStore.findDependentPolicies(id, resourceServer.getId()).forEach(dependentPolicy -> { - if (dependentPolicy.getAssociatedPolicies().size() == 1) { - policyStore.delete(dependentPolicy.getId()); - } else { - dependentPolicy.removeAssociatedPolicy(policy); - } - }); - - policyStore.delete(policy.getId()); - - return Response.noContent().build(); - } - - @Path("{id}") - @GET - @Produces("application/json") - @NoCache - public Response findById(@PathParam("id") String id) { - this.auth.requireView(); - StoreFactory storeFactory = authorization.getStoreFactory(); - Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId()); - - if (model == null) { - return Response.status(Status.NOT_FOUND).build(); - } - - return Response.ok(toRepresentation(model)).build(); - } - - @Path("{id}/dependentPolicies") - @GET - @Produces("application/json") - @NoCache - public Response getDependentPolicies(@PathParam("id") String id) { - this.auth.requireView(); - StoreFactory storeFactory = authorization.getStoreFactory(); - Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId()); - - if (model == null) { - return Response.status(Status.NOT_FOUND).build(); - } - - List policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId(), resourceServer.getId()); - - return Response.ok(policies.stream().map(policy -> { - PolicyRepresentation representation1 = new PolicyRepresentation(); - - representation1.setId(policy.getId()); - representation1.setName(policy.getName()); - representation1.setType(policy.getType()); - - return representation1; - }).collect(Collectors.toList())).build(); - } - - @Path("{id}/scopes") - @GET - @Produces("application/json") - @NoCache - public Response getScopes(@PathParam("id") String id) { - this.auth.requireView(); - StoreFactory storeFactory = authorization.getStoreFactory(); - Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId()); - - if (model == null) { - return Response.status(Status.NOT_FOUND).build(); - } - - return Response.ok(model.getScopes().stream().map(scope -> { - ScopeRepresentation representation = new ScopeRepresentation(); - - representation.setId(scope.getId()); - representation.setName(scope.getName()); - - return representation; - }).collect(Collectors.toList())).build(); - } - - @Path("{id}/resources") - @GET - @Produces("application/json") - @NoCache - public Response getResources(@PathParam("id") String id) { - this.auth.requireView(); - StoreFactory storeFactory = authorization.getStoreFactory(); - Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId()); - - if (model == null) { - return Response.status(Status.NOT_FOUND).build(); - } - - return Response.ok(model.getResources().stream().map(resource -> { - ResourceRepresentation representation = new ResourceRepresentation(); - - representation.setId(resource.getId()); - representation.setName(resource.getName()); - - return representation; - }).collect(Collectors.toList())).build(); - } - - @Path("{id}/associatedPolicies") - @GET - @Produces("application/json") - @NoCache - public Response getAssociatedPolicies(@PathParam("id") String id) { - this.auth.requireView(); - StoreFactory storeFactory = authorization.getStoreFactory(); - Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId()); - - if (model == null) { - return Response.status(Status.NOT_FOUND).build(); - } - - return Response.ok(model.getAssociatedPolicies().stream().map(policy -> { - PolicyRepresentation representation1 = new PolicyRepresentation(); - - representation1.setId(policy.getId()); - representation1.setName(policy.getName()); - representation1.setType(policy.getType()); - - return representation1; - }).collect(Collectors.toList())).build(); + protected Object toRepresentation(Policy model) { + return ModelToRepresentation.toRepresentation(model); } @Path("/search") @GET - @Produces("application/json") + @Produces(MediaType.APPLICATION_JSON) @NoCache - public Response find(@QueryParam("name") String name) { + public Response findByName(@QueryParam("name") String name) { this.auth.requireView(); StoreFactory storeFactory = authorization.getStoreFactory(); @@ -294,7 +161,7 @@ public class PolicyService { } @GET - @Produces("application/json") + @Produces(MediaType.APPLICATION_JSON) @NoCache public Response findAll(@QueryParam("policyId") String id, @QueryParam("name") String name, @@ -363,15 +230,20 @@ public class PolicyService { } return Response.ok( - policyStore.findByResourceServer(search, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream() - .map(policy -> toRepresentation(policy)) - .collect(Collectors.toList())) + doSearch(firstResult, maxResult, search)) .build(); } + protected List doSearch(Integer firstResult, Integer maxResult, Map filters) { + PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore(); + return policyStore.findByResourceServer(filters, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream() + .map(policy -> toRepresentation(policy)) + .collect(Collectors.toList()); + } + @Path("providers") @GET - @Produces("application/json") + @Produces(MediaType.APPLICATION_JSON) @NoCache public Response findPolicyProviders() { this.auth.requireView(); @@ -400,17 +272,11 @@ public class PolicyService { return resource; } - @Path("{policyType}") - public Object getPolicyTypeResource(@PathParam("policyType") String policyType) { - this.auth.requireView(); - return getPolicyProviderAdminResource(policyType, this.authorization); - } - - private PolicyProviderAdminService getPolicyProviderAdminResource(String policyType, AuthorizationProvider authorization) { + protected PolicyProviderAdminService getPolicyProviderAdminResource(String policyType) { PolicyProviderFactory providerFactory = authorization.getProviderFactory(policyType); if (providerFactory != null) { - return providerFactory.getAdminResource(this.resourceServer); + return providerFactory.getAdminResource(resourceServer, authorization); } return null; diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java new file mode 100644 index 0000000000..7d3f8b46de --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java @@ -0,0 +1,47 @@ +/* + * 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 javax.ws.rs.Path; + +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.policy.provider.PolicyProviderAdminService; +import org.keycloak.services.resources.admin.RealmAuth; + +/** + * @author Pedro Igor + */ +public class PolicyTypeService extends PolicyService { + + private final String type; + + PolicyTypeService(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; + } +} diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java index 52d6a38e83..ffd67b6d2b 100644 --- a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java +++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java @@ -17,6 +17,22 @@ */ package org.keycloak.authorization.admin; +import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; + +import java.io.IOException; +import java.util.HashMap; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.model.ResourceServer; @@ -38,21 +54,6 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; import org.keycloak.services.resources.admin.RealmAuth; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.io.IOException; -import java.util.HashMap; - -import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; - /** * @author Pedro Igor */ @@ -171,6 +172,16 @@ public class ResourceServerService { return resource; } + @Path("/permission") + public Object getPermissionTypeResource() { + this.auth.requireView(); + PermissionService resource = new PermissionService(this.resourceServer, this.authorization, this.auth); + + ResteasyProviderFactory.getInstance().injectProperties(resource); + + return resource; + } + private void createDefaultPermission(ResourceRepresentation resource, PolicyRepresentation policy) { PolicyRepresentation defaultPermission = new PolicyRepresentation(); diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java index b3a04754db..8e07ee21a8 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java @@ -47,7 +47,7 @@ public class TestPolicyProviderFactory implements PolicyProviderFactory { } @Override - public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) { + public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) { return null; } diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js index e40fcec9b6..5cb9f1fb1c 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js @@ -646,7 +646,7 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l }; }); -module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) { +module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPermission, PolicyProvider, client) { $scope.realm = realm; $scope.client = client; $scope.policyProviders = []; @@ -654,7 +654,6 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route $scope.query = { realm: realm.realm, client : client.id, - permission: true, max : 20, first : 0 }; @@ -705,7 +704,7 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route $scope.searchQuery = function() { $scope.searchLoaded = false; - ResourceServerPolicy.query($scope.query, function(data) { + ResourceServerPermission.query($scope.query, function(data) { $scope.policies = data; $scope.searchLoaded = true; $scope.lastSearch = $scope.query.search; @@ -723,7 +722,7 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route policy.details = {loaded: false}; - ResourceServerPolicy.associatedPolicies({ + ResourceServerPermission.associatedPolicies({ realm : $route.current.params.realm, client : client.id, id : policy.id @@ -758,7 +757,7 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http policy = $scope.policy; } - $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/resolveModules' + $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/provider/resolveModules' , policy).success(function(data) { $scope.drools.moduleNames = data; $scope.resolveSessions(); @@ -766,7 +765,7 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http } $scope.resolveSessions = function() { - $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/resolveSessions' + $http.post(authUrl + '/admin/realms/'+ $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/policy/rules/provider/resolveSessions' , $scope.policy).success(function(data) { $scope.drools.moduleSessions = data; }); @@ -789,7 +788,7 @@ module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http }, realm, client, $scope); }); -module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $route, $location, realm, client, PolicyController, ResourceServerPolicy, ResourceServerResource) { +module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $route, $location, realm, client, PolicyController, ResourceServerPermission, ResourceServerResource) { PolicyController.onInit({ getPolicyType : function() { return "resource"; @@ -848,7 +847,7 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro max : 20, first : 0 }; - ResourceServerPolicy.query($scope.query, function(response) { + ResourceServerPermission.searchPolicies($scope.query, function(response) { data.results = response; query.callback(data); }); @@ -860,59 +859,89 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro }; $scope.applyToResourceType = function() { - if ($scope.policy.config.default) { - $scope.policy.config.resources = []; + if ($scope.applyToResourceTypeFlag) { + $scope.selectedResource = null; } else { - $scope.policy.config.defaultResourceType = null; + $scope.policy.resourceType = null; } } }, onInitUpdate : function(policy) { - policy.config.default = eval(policy.config.default); - policy.config.resources = {}; - ResourceServerPolicy.resources({ - realm : $route.current.params.realm, - client : client.id, - id : policy.id - }, function(resources) { - resources[0].text = resources[0].name; - $scope.policy.config.resources = resources[0]; - }); + if (!policy.resourceType) { + $scope.selectedResource = {}; + ResourceServerPermission.resources({ + realm: $route.current.params.realm, + client: client.id, + id: policy.id + }, function (resources) { + resources[0].text = resources[0].name; + $scope.selectedResource = resources[0]; + var copy = angular.copy($scope.selectedResource); + $scope.$watch('selectedResource', function() { + if (!angular.equals($scope.selectedResource, copy)) { + $scope.changed = true; + } + }, true); + }); + } else { + $scope.applyToResourceTypeFlag = true; + } - policy.config.applyPolicies = []; - ResourceServerPolicy.associatedPolicies({ + $scope.selectedPolicies = []; + ResourceServerPermission.associatedPolicies({ realm : $route.current.params.realm, client : client.id, id : policy.id }, function(policies) { for (i = 0; i < policies.length; i++) { policies[i].text = policies[i].name; - $scope.policy.config.applyPolicies.push(policies[i]); + $scope.selectedPolicies.push(policies[i]); + var copy = angular.copy($scope.selectedPolicies); + $scope.$watch('selectedPolicies', function() { + if (!angular.equals($scope.selectedPolicies, copy)) { + $scope.changed = true; + } + }, true); } }); }, onUpdate : function() { - if ($scope.policy.config.resources && $scope.policy.config.resources._id) { - $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]); + if ($scope.selectedResource && $scope.selectedResource._id) { + $scope.policy.resources = []; + $scope.policy.resources.push($scope.selectedResource._id); } else { - delete $scope.policy.config.resources + delete $scope.policy.resources } var policies = []; - for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) { - policies.push($scope.policy.config.applyPolicies[i].id); + for (i = 0; i < $scope.selectedPolicies.length; i++) { + policies.push($scope.selectedPolicies[i].id); } - $scope.policy.config.applyPolicies = JSON.stringify(policies); + $scope.policy.policies = policies; + delete $scope.policy.config; }, onInitCreate : function(newPolicy) { newPolicy.decisionStrategy = 'UNANIMOUS'; - newPolicy.config = {}; - newPolicy.config.resources = null; + $scope.selectedResource = null; + var copy = angular.copy($scope.selectedResource); + $scope.$watch('selectedResource', function() { + if (!angular.equals($scope.selectedResource, copy)) { + $scope.changed = true; + } + }, true); + + $scope.selectedPolicies = null; + var copy = angular.copy($scope.selectedPolicies); + $scope.$watch('selectedPolicies', function() { + if (!angular.equals($scope.selectedPolicies, copy)) { + $scope.changed = true; + } + }, true); var resourceId = $location.search()['rsrid']; @@ -923,25 +952,27 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro rsrid : resourceId }, function(data) { data.text = data.name; - $scope.policy.config.resources = data; + $scope.selectedResource = data; }); } }, onCreate : function() { - if ($scope.policy.config.resources && $scope.policy.config.resources._id) { - $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]); + if ($scope.selectedResource && $scope.selectedResource._id) { + $scope.policy.resources = []; + $scope.policy.resources.push($scope.selectedResource._id); } else { - delete $scope.policy.config.resources + delete $scope.policy.resources } var policies = []; - for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) { - policies.push($scope.policy.config.applyPolicies[i].id); + for (i = 0; i < $scope.selectedPolicies.length; i++) { + policies.push($scope.selectedPolicies[i].id); } - $scope.policy.config.applyPolicies = JSON.stringify(policies); + $scope.policy.policies = policies; + delete $scope.policy.config; } }, realm, client, $scope); }); @@ -1046,20 +1077,21 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route }; $scope.selectResource = function() { - $scope.policy.config.scopes = null; - if ($scope.policy.config.resources) { + $scope.selectedScopes = null; + if ($scope.policy.resources) { ResourceServerResource.scopes({ realm: $route.current.params.realm, client: client.id, - rsrid: $scope.policy.config.resources._id + rsrid: $scope.policy.resources._id }, function (data) { - $scope.policy.config.resources.scopes = data; + $scope.resourceScopes = data; }); } } }, onInitUpdate : function(policy) { + $scope.selectedScopes = []; ResourceServerPolicy.resources({ realm : $route.current.params.realm, client : client.id, @@ -1079,44 +1111,44 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route deep: false }, function (resource) { resource[0].text = resource[0].name; - $scope.policy.config.resources = resource[0]; + $scope.policy.resources = resource[0]; ResourceServerResource.scopes({ realm: $route.current.params.realm, client: client.id, rsrid: resource[0]._id }, function (scopes) { - $scope.policy.config.resources.scopes = scopes; + $scope.resourceScopes = scopes; }); ResourceServerPolicy.scopes({ realm: $route.current.params.realm, client: client.id, id: policy.id }, function (scopes) { - $scope.policy.config.scopes = []; + $scope.selectedScopes = []; for (i = 0; i < scopes.length; i++) { - $scope.policy.config.scopes.push(scopes[i].id); + $scope.selectedScopes.push(scopes[i].id); } }); }); }); } } else { - $scope.policy.config.resources = null; + $scope.policy.resources = null; ResourceServerPolicy.scopes({ realm : $route.current.params.realm, client : client.id, id : policy.id }, function(scopes) { - $scope.policy.config.scopes = []; + $scope.selectedScopes = []; for (i = 0; i < scopes.length; i++) { scopes[i].text = scopes[i].name; - $scope.policy.config.scopes.push(scopes[i]); + $scope.selectedScopes.push(scopes[i]); } }); } }); - policy.config.applyPolicies = []; + policy.policies = []; ResourceServerPolicy.associatedPolicies({ realm : $route.current.params.realm, client : client.id, @@ -1124,43 +1156,42 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route }, function(policies) { for (i = 0; i < policies.length; i++) { policies[i].text = policies[i].name; - $scope.policy.config.applyPolicies.push(policies[i]); + $scope.policy.policies.push(policies[i]); } }); }, onUpdate : function() { - if ($scope.policy.config.resources != null) { - $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]); + if ($scope.policy.resources != null) { + $scope.policy.resources = [$scope.policy.resources._id]; } else { - $scope.policy.config.resources = JSON.stringify([""]); + delete $scope.policy.resources; } var scopes = []; - for (i = 0; i < $scope.policy.config.scopes.length; i++) { - if ($scope.policy.config.scopes[i].id) { - scopes.push($scope.policy.config.scopes[i].id); + for (i = 0; i < $scope.selectedScopes.length; i++) { + if ($scope.selectedScopes[i].id) { + scopes.push($scope.selectedScopes[i].id); } else { - scopes.push($scope.policy.config.scopes[i]); + scopes.push($scope.selectedScopes[i]); } } - $scope.policy.config.scopes = JSON.stringify(scopes); + $scope.policy.scopes = scopes; var policies = []; - for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) { - policies.push($scope.policy.config.applyPolicies[i].id); + for (i = 0; i < $scope.policy.policies.length; i++) { + policies.push($scope.policy.policies[i].id); } - $scope.policy.config.applyPolicies = JSON.stringify(policies); + $scope.policy.policies = policies; }, onInitCreate : function(newPolicy) { newPolicy.decisionStrategy = 'UNANIMOUS'; - newPolicy.config = {}; - newPolicy.config.resources = null; + newPolicy.resources = null; var scopeId = $location.search()['scpid']; @@ -1171,38 +1202,39 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route id: scopeId, }, function (data) { data.text = data.name; - if (!$scope.policy.config.scopes) { - $scope.policy.config.scopes = []; + if (!$scope.policy.scopes) { + $scope.policy.scopes = []; } - $scope.policy.config.scopes.push(data); + $scope.policy.scopes.push(data); }); } }, onCreate : function() { - if ($scope.policy.config.resources != null) { - $scope.policy.config.resources = JSON.stringify([$scope.policy.config.resources._id]); + if ($scope.policy.resources != null) { + $scope.policy.resources = [$scope.policy.resources._id]; } var scopes = []; - for (i = 0; i < $scope.policy.config.scopes.length; i++) { - if ($scope.policy.config.scopes[i].id) { - scopes.push($scope.policy.config.scopes[i].id); + for (i = 0; i < $scope.selectedScopes.length; i++) { + if ($scope.selectedScopes[i].id) { + scopes.push($scope.selectedScopes[i].id); } else { - scopes.push($scope.policy.config.scopes[i]); + scopes.push($scope.selectedScopes[i]); } } - $scope.policy.config.scopes = JSON.stringify(scopes); + $scope.policy.scopes = scopes; var policies = []; - for (i = 0; i < $scope.policy.config.applyPolicies.length; i++) { - policies.push($scope.policy.config.applyPolicies[i].id); + for (i = 0; i < $scope.policy.policies.length; i++) { + policies.push($scope.policy.policies[i].id); } - $scope.policy.config.applyPolicies = JSON.stringify(policies); + $scope.policy.policies = policies; + delete $scope.policy.config; } }, realm, client, $scope); }); @@ -1743,7 +1775,7 @@ module.controller('ResourceServerPolicyAggregateDetailCtrl', function($scope, $r }, realm, client, $scope); }); -module.service("PolicyController", function($http, $route, $location, ResourceServer, ResourceServerPolicy, AuthzDialog, Notifications) { +module.service("PolicyController", function($http, $route, $location, ResourceServer, ResourceServerPolicy, ResourceServerPermission, AuthzDialog, Notifications) { var PolicyController = {}; @@ -1754,6 +1786,12 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe } } + var service = ResourceServerPolicy; + + if (delegate.isPermission()) { + service = ResourceServerPermission; + } + $scope.realm = realm; $scope.client = client; @@ -1799,7 +1837,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe if (delegate.onCreate) { delegate.onCreate(); } - ResourceServerPolicy.save({realm : realm.realm, client : client.id}, $scope.policy, function(data) { + service.save({realm : realm.realm, client : client.id, type: $scope.policy.type}, $scope.policy, function(data) { if (delegate.isPermission()) { $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission/" + $scope.policy.type + "/" + data.id); Notifications.success("The permission has been created."); @@ -1819,9 +1857,10 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe } } } else { - ResourceServerPolicy.get({ + service.get({ realm: realm.realm, client : client.id, + type: delegate.getPolicyType(), id: $route.current.params.id }, function(data) { $scope.originalPolicy = data; @@ -1845,7 +1884,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe if (delegate.onUpdate) { delegate.onUpdate(); } - ResourceServerPolicy.update({realm : realm.realm, client : client.id, id : $scope.policy.id}, $scope.policy, function() { + service.update({realm : realm.realm, client : client.id, type: $scope.policy.type, id : $scope.policy.id}, $scope.policy, function() { $route.reload(); if (delegate.isPermission()) { Notifications.success("The permission has been updated."); @@ -1871,7 +1910,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe $scope.remove = function() { var msg = ""; - ResourceServerPolicy.dependentPolicies({ + service.dependentPolicies({ realm : $route.current.params.realm, client : client.id, id : $scope.policy.id @@ -1887,7 +1926,7 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe } AuthzDialog.confirmDeleteWithMsg($scope.policy.name, "Policy", msg, function() { - ResourceServerPolicy.delete({realm : $scope.realm.realm, client : $scope.client.id, id : $scope.policy.id}, null, function() { + service.delete({realm : $scope.realm.realm, client : $scope.client.id, id : $scope.policy.id}, null, function() { if (delegate.isPermission()) { $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission"); Notifications.success("The permission has been deleted."); diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js index 1c4f584bd8..92219bb570 100644 --- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js @@ -36,10 +36,11 @@ module.factory('ResourceServerScope', function($resource) { }); module.factory('ResourceServerPolicy', function($resource) { - return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id', { + return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:type/:id', { realm : '@realm', client: '@client', - id : '@id' + id : '@id', + type: '@type' }, { 'update' : {method : 'PUT'}, 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/search', method : 'GET'}, @@ -50,6 +51,23 @@ module.factory('ResourceServerPolicy', function($resource) { }); }); +module.factory('ResourceServerPermission', function($resource) { + return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/permission/:type/:id', { + realm : '@realm', + client: '@client', + type: '@type', + id : '@id' + }, { + 'update' : {method : 'PUT'}, + 'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/permission/search', method : 'GET'}, + 'searchPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy', method : 'GET', isArray: true}, + 'associatedPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/associatedPolicies', method : 'GET', isArray: true}, + 'dependentPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/dependentPolicies', method : 'GET', isArray: true}, + 'scopes' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/permission/:id/scopes', method : 'GET', isArray: true}, + 'resources' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/permission/:id/resources', method : 'GET', isArray: true} + }); +}); + module.factory('PolicyProvider', function($resource) { return $resource(authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/providers', { realm : '@realm', diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html index 10e31707d0..7a5ba6d993 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-resource-detail.html @@ -29,25 +29,25 @@ {{:: 'authz-permission-description.tooltip' | translate}} - {{:: 'authz-permission-resource-apply-to-resource-type' | translate}} + {{:: 'authz-permission-resource-apply-to-resource-type' | translate}} - + {{:: 'authz-permission-resource-apply-to-resource-type.tooltip' | translate}} - + {{:: 'authz-resources' | translate}} * - + {{:: 'authz-permission-resource-resource.tooltip' | translate}} - - {{:: 'authz-resource-type' | translate}} * + + {{:: 'authz-resource-type' | translate}} * - + {{:: 'authz-permission-resource-type.tooltip' | translate}} @@ -56,7 +56,7 @@ {{:: 'authz-policy-apply-policy' | translate}} * - + {{:: 'authz-policy-apply-policy.tooltip' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html index 90a3dc698b..1754fe5b73 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/permission/provider/resource-server-policy-scope-detail.html @@ -32,27 +32,28 @@ {{:: 'authz-resource' | translate}} - + {{:: 'authz-permission-scope-resource.tooltip' | translate}} - + {{:: 'authz-scopes' | translate}} * + data-ng-required="policy.resources != null"> + {{scope.name}} + {{:: 'authz-permission-scope-scope.tooltip' | translate}} - + {{:: 'authz-scopes' | translate}} * - + {{:: 'authz-permission-scope-scope.tooltip' | translate}} @@ -60,7 +61,7 @@ {{:: 'authz-policy-apply-policy' | translate}} * - + {{:: 'authz-policy-apply-policy.tooltip' | translate}}