[KEYCLOAK-7353] Support Policy Management in Protection API

See https://issues.jboss.org/browse/KEYCLOAK-7353
This commit is contained in:
Federico M. Facca authored and Pedro Igor committed 2018-06-06 19:36:42 -03:00
1 parent f8919f8baa
commit 5a9bfea419
64 files changed
+2245 -375

No files matched your search

@@ -102,6 +102,9 @@ public class ServerConfiguration {
@JsonProperty("permission_endpoint")
private String permissionEndpoint;
@JsonProperty("policy_endpoint")
private String policyEndpoint;
public String getIssuer() {
return issuer;
@@ -206,4 +209,8 @@ public class ServerConfiguration {
public String getPermissionEndpoint() {
return permissionEndpoint;
}
public String getPolicyEndpoint() {
return policyEndpoint;
}
}
@@ -0,0 +1,187 @@
/*
* Copyright 2018 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.client.resource;
import java.util.List;
import java.util.concurrent.Callable;
import com.fasterxml.jackson.core.type.TypeReference;
import org.keycloak.authorization.client.representation.ServerConfiguration;
import org.keycloak.authorization.client.util.Http;
import org.keycloak.authorization.client.util.Throwables;
import org.keycloak.authorization.client.util.TokenCallable;
import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
import org.keycloak.util.JsonSerialization;
/**
* An entry point for managing user-managed permissions for a particular resource
*
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyResource {
private String resourceId;
private final Http http;
private final ServerConfiguration serverConfiguration;
private final TokenCallable pat;
public PolicyResource(String resourceId, Http http, ServerConfiguration serverConfiguration, TokenCallable pat) {
this.resourceId = resourceId;
this.http = http;
this.serverConfiguration = serverConfiguration;
this.pat = pat;
}
/**
* Creates a new user-managed permission as represented by the given {@code permission}.
*
* @param permission the permission to create
* @return if successful, the permission created
*/
public UmaPermissionRepresentation create(final UmaPermissionRepresentation permission) {
if (permission == null) {
throw new IllegalArgumentException("Permission must not be null");
}
Callable<UmaPermissionRepresentation> callable = new Callable<UmaPermissionRepresentation>() {
@Override
public UmaPermissionRepresentation call() throws Exception {
return http.<UmaPermissionRepresentation>post(serverConfiguration.getPolicyEndpoint() + "/" + resourceId)
.authorizationBearer(pat.call())
.json(JsonSerialization.writeValueAsBytes(permission))
.response().json(UmaPermissionRepresentation.class).execute();
}
};
try {
return callable.call();
} catch (Exception cause) {
return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error creating policy for resurce [" + resourceId + "]", cause);
}
}
/**
* Updates an existing user-managed permission
*
* @param permission the permission to update
*/
public void update(final UmaPermissionRepresentation permission) {
if (permission == null) {
throw new IllegalArgumentException("Permission must not be null");
}
if (permission.getId() == null) {
throw new IllegalArgumentException("Permission id must not be null");
}
Callable<Void> callable = new Callable<Void>() {
@Override
public Void call() throws Exception {
http.<Void>put(serverConfiguration.getPolicyEndpoint() + "/"+ permission.getId())
.authorizationBearer(pat.call())
.json(JsonSerialization.writeValueAsBytes(permission)).execute();
return null;
}
};
try {
callable.call();
} catch (Exception cause) {
Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error updating policy for resurce [" + resourceId + "]", cause);
}
}
/**
* Deletes an existing user-managed permission
*
* @param id the permission id
*/
public void delete(final String id) {
Callable<Void> callable = new Callable<Void>() {
@Override
public Void call() {
http.<UmaPermissionRepresentation>delete(serverConfiguration.getPolicyEndpoint() + "/" + id)
.authorizationBearer(pat.call())
.response().execute();
return null;
}
};
try {
callable.call();
} catch (Exception cause) {
Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error updating policy for resurce [" + resourceId + "]", cause);
}
}
/**
* Queries the server for permission matching the given parameters.
*
* @param id the permission id
* @param name the name of the permission
* @param scope the scope associated with the permission
* @param firstResult the position of the first resource to retrieve
* @param maxResult the maximum number of resources to retrieve
* @return the permissions matching the given parameters
*/
public List<UmaPermissionRepresentation> find(final String name,
final String scope,
final Integer firstResult,
final Integer maxResult) {
Callable<List<UmaPermissionRepresentation>> callable = new Callable<List<UmaPermissionRepresentation>>() {
@Override
public List<UmaPermissionRepresentation> call() {
return http.<List<UmaPermissionRepresentation>>get(serverConfiguration.getPolicyEndpoint())
.authorizationBearer(pat.call())
.param("name", name)
.param("resource", resourceId)
.param("scope", scope)
.param("first", firstResult == null ? null : firstResult.toString())
.param("max", maxResult == null ? null : maxResult.toString())
.response().json(new TypeReference<List<UmaPermissionRepresentation>>(){}).execute();
}
};
try {
return callable.call();
} catch (Exception cause) {
return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error querying policies for resource [" + resourceId + "]", cause);
}
}
/**
* Queries the server for a permission with the given {@code id}.
*
* @param id the permission id
* @return the permission with the given id
*/
public UmaPermissionRepresentation findById(final String id) {
if (id == null) {
throw new IllegalArgumentException("Permission id must not be null");
}
Callable<UmaPermissionRepresentation> callable = new Callable<UmaPermissionRepresentation>() {
@Override
public UmaPermissionRepresentation call() {
return http.<UmaPermissionRepresentation>get(serverConfiguration.getPolicyEndpoint() + "/" + id)
.authorizationBearer(pat.call())
.response().json(UmaPermissionRepresentation.class).execute();
}
};
try {
return callable.call();
} catch (Exception cause) {
return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error creating policy for resurce [" + resourceId + "]", cause);
}
}
}
@@ -64,6 +64,10 @@ public class ProtectionResource {
return new PermissionResource(http, serverConfiguration, pat);
}
public PolicyResource policy(String resourceId) {
return new PolicyResource(resourceId, http, serverConfiguration, pat);
}
/**
* Introspects the given <code>rpt</code> using the token introspection endpoint.
*
@@ -83,6 +83,7 @@ public class HttpMethodAuthenticator<R> {
method.param("rpt", request.getRpt());
method.param("scope", request.getScope());
method.param("audience", request.getAudience());
method.param("subject_token", request.getSubjectToken());
if (permissions != null) {
for (ResourcePermission permission : permissions.getResources()) {
@@ -73,7 +73,7 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory<Agg
}
@Override
public AggregatePolicyRepresentation toRepresentation(Policy policy) {
public AggregatePolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
return new AggregatePolicyRepresentation();
}
@@ -1,6 +1,6 @@
package org.keycloak.authorization.policy.provider.client;
import java.util.function.Function;
import java.util.function.BiFunction;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
@@ -13,15 +13,15 @@ import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation
public class ClientPolicyProvider implements PolicyProvider {
private final Function<Policy, ClientPolicyRepresentation> representationFunction;
private final BiFunction<Policy, AuthorizationProvider, ClientPolicyRepresentation> representationFunction;
public ClientPolicyProvider(Function<Policy, ClientPolicyRepresentation> representationFunction) {
public ClientPolicyProvider(BiFunction<Policy, AuthorizationProvider, ClientPolicyRepresentation> representationFunction) {
this.representationFunction = representationFunction;
}
@Override
public void evaluate(Evaluation evaluation) {
ClientPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy());
ClientPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy(), evaluation.getAuthorizationProvider());
AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
EvaluationContext context = evaluation.getContext();
@@ -30,7 +30,7 @@ import org.keycloak.util.JsonSerialization;
public class ClientPolicyProviderFactory implements PolicyProviderFactory<ClientPolicyRepresentation> {
private ClientPolicyProvider provider = new ClientPolicyProvider(policy -> toRepresentation(policy));
private ClientPolicyProvider provider = new ClientPolicyProvider(this::toRepresentation);
@Override
public String getName() {
@@ -48,7 +48,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory<Client
}
@Override
public ClientPolicyRepresentation toRepresentation(Policy policy) {
public ClientPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
representation.setClients(new HashSet<>(Arrays.asList(getClients(policy))));
return representation;
@@ -75,12 +75,12 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory<Client
}
@Override
public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorizationProvider) {
ClientPolicyRepresentation userRep = toRepresentation(policy);
public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
ClientPolicyRepresentation userRep = toRepresentation(policy, authorization);
Map<String, String> config = new HashMap<>();
try {
RealmModel realm = authorizationProvider.getRealm();
RealmModel realm = authorization.getRealm();
config.put("clients", JsonSerialization.writeValueAsString(userRep.getClients().stream().map(id -> realm.getClientById(id).getClientId()).collect(Collectors.toList())));
} catch (IOException cause) {
throw new RuntimeException("Failed to export user policy [" + policy.getName() + "]", cause);
@@ -19,7 +19,7 @@ package org.keycloak.authorization.policy.provider.group;
import static org.keycloak.models.utils.ModelToRepresentation.buildGroupPath;
import java.util.List;
import java.util.function.Function;
import java.util.function.BiFunction;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.attribute.Attributes;
@@ -36,16 +36,16 @@ import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
*/
public class GroupPolicyProvider implements PolicyProvider {
private final Function<Policy, GroupPolicyRepresentation> representationFunction;
private final BiFunction<Policy, AuthorizationProvider, GroupPolicyRepresentation> representationFunction;
public GroupPolicyProvider(Function<Policy, GroupPolicyRepresentation> representationFunction) {
public GroupPolicyProvider(BiFunction<Policy, AuthorizationProvider, GroupPolicyRepresentation> representationFunction) {
this.representationFunction = representationFunction;
}
@Override
public void evaluate(Evaluation evaluation) {
GroupPolicyRepresentation policy = representationFunction.apply(evaluation.getPolicy());
AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
GroupPolicyRepresentation policy = representationFunction.apply(evaluation.getPolicy(), authorizationProvider);
RealmModel realm = authorizationProvider.getRealm();
Attributes.Entry groupsClaim = evaluation.getContext().getIdentity().getAttributes().getValue(policy.getGroupsClaim());
@@ -43,7 +43,7 @@ import org.keycloak.util.JsonSerialization;
*/
public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPolicyRepresentation> {
private GroupPolicyProvider provider = new GroupPolicyProvider(policy -> toRepresentation(policy));
private GroupPolicyProvider provider = new GroupPolicyProvider(this::toRepresentation);
@Override
public String getId() {
@@ -71,7 +71,7 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
}
@Override
public GroupPolicyRepresentation toRepresentation(Policy policy) {
public GroupPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
representation.setGroupsClaim(policy.getConfig().get("groupsClaim"));
@@ -109,19 +109,24 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
}
@Override
public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorizationProvider) {
public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
Map<String, String> config = new HashMap<>();
GroupPolicyRepresentation groupPolicy = toRepresentation(policy);
GroupPolicyRepresentation groupPolicy = toRepresentation(policy, authorization);
Set<GroupPolicyRepresentation.GroupDefinition> groups = groupPolicy.getGroups();
for (GroupPolicyRepresentation.GroupDefinition definition: groups) {
GroupModel group = authorizationProvider.getRealm().getGroupById(definition.getId());
GroupModel group = authorization.getRealm().getGroupById(definition.getId());
definition.setId(null);
definition.setPath(ModelToRepresentation.buildGroupPath(group));
}
try {
config.put("groupsClaim", groupPolicy.getGroupsClaim());
String groupsClaim = groupPolicy.getGroupsClaim();
if (groupsClaim != null) {
config.put("groupsClaim", groupsClaim);
}
config.put("groups", JsonSerialization.writeValueAsString(groups));
} catch (IOException cause) {
throw new RuntimeException("Failed to export group policy [" + policy.getName() + "]", cause);
@@ -147,17 +152,15 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
}
private void updatePolicy(Policy policy, String groupsClaim, Set<GroupPolicyRepresentation.GroupDefinition> groups, AuthorizationProvider authorization) {
if (groupsClaim == null) {
throw new RuntimeException("Group claims property not provided");
}
if (groups == null || groups.isEmpty()) {
throw new RuntimeException("You must provide at least one group");
}
Map<String, String> config = new HashMap<>(policy.getConfig());
config.put("groupsClaim", groupsClaim);
if (groupsClaim != null) {
config.put("groupsClaim", groupsClaim);
}
List<GroupModel> topLevelGroups = authorization.getRealm().getTopLevelGroups();
@@ -43,7 +43,7 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory<JSPolicyRe
}
@Override
public JSPolicyRepresentation toRepresentation(Policy policy) {
public JSPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
JSPolicyRepresentation representation = new JSPolicyRepresentation();
representation.setCode(policy.getConfig().get("code"));
return representation;
@@ -56,7 +56,7 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory<Reso
}
@Override
public ResourcePermissionRepresentation toRepresentation(Policy policy) {
public ResourcePermissionRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
representation.setResourceType(policy.getConfig().get("defaultResourceType"));
return representation;
@@ -58,7 +58,7 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory<ScopePe
}
@Override
public ScopePermissionRepresentation toRepresentation(Policy policy) {
public ScopePermissionRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
return new ScopePermissionRepresentation();
}
@@ -16,22 +16,40 @@
*/
package org.keycloak.authorization.policy.provider.permission;
import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.models.ClientModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation.GroupDefinition;
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation.RoleDefinition;
import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class UMAPolicyProviderFactory implements PolicyProviderFactory<PolicyRepresentation> {
public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermissionRepresentation> {
private UMAPolicyProvider provider = new UMAPolicyProvider();
@@ -57,53 +75,249 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<PolicyRep
@Override
public PolicyProvider create(KeycloakSession session) {
return null;
return provider;
}
@Override
public void onCreate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
verifyCircularReference(policy, new ArrayList<>());
public void onCreate(Policy policy, UmaPermissionRepresentation representation, AuthorizationProvider authorization) {
policy.setOwner(representation.getOwner());
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
Set<String> roles = representation.getRoles();
if (roles != null) {
for (String role : roles) {
createRolePolicy(policy, policyStore, role, representation.getOwner());
}
}
Set<String> groups = representation.getGroups();
if (groups != null) {
for (String group : groups) {
createGroupPolicy(policy, policyStore, group, representation.getOwner());
}
}
Set<String> clients = representation.getClients();
if (clients != null) {
for (String client : clients) {
createClientPolicy(policy, policyStore, client, representation.getOwner());
}
}
String condition = representation.getCondition();
if (condition != null) {
createJSPolicy(policy, policyStore, condition, representation.getOwner());
}
}
@Override
public void onUpdate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
verifyCircularReference(policy, new ArrayList<>());
public void onUpdate(Policy policy, UmaPermissionRepresentation representation, AuthorizationProvider authorization) {
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
for (Policy associatedPolicy : associatedPolicies) {
AbstractPolicyRepresentation associatedRep = ModelToRepresentation.toRepresentation(associatedPolicy, authorization, false, false);
if ("role".equals(associatedRep.getType())) {
RolePolicyRepresentation rep = RolePolicyRepresentation.class.cast(associatedRep);
rep.setRoles(new HashSet<>());
Set<String> updatedRoles = representation.getRoles();
if (updatedRoles != null) {
for (String role : updatedRoles) {
rep.addRole(role);
}
}
if (rep.getRoles().isEmpty()) {
policyStore.delete(associatedPolicy.getId());
} else {
RepresentationToModel.toModel(rep, authorization, associatedPolicy);
}
} else if ("js".equals(associatedRep.getType())) {
JSPolicyRepresentation rep = JSPolicyRepresentation.class.cast(associatedRep);
if (representation.getCondition() != null) {
rep.setCode(representation.getCondition());
RepresentationToModel.toModel(rep, authorization, associatedPolicy);
} else {
policyStore.delete(associatedPolicy.getId());
}
} else if ("group".equals(associatedRep.getType())) {
GroupPolicyRepresentation rep = GroupPolicyRepresentation.class.cast(associatedRep);
rep.setGroups(new HashSet<>());
Set<String> updatedGroups = representation.getGroups();
if (updatedGroups != null) {
for (String group : updatedGroups) {
rep.addGroupPath(group);
}
}
if (rep.getGroups().isEmpty()) {
policyStore.delete(associatedPolicy.getId());
} else {
RepresentationToModel.toModel(rep, authorization, associatedPolicy);
}
} else if ("client".equals(associatedRep.getType())) {
ClientPolicyRepresentation rep = ClientPolicyRepresentation.class.cast(associatedRep);
rep.setClients(new HashSet<>());
Set<String> updatedClients = representation.getClients();
if (updatedClients != null) {
for (String client : updatedClients) {
rep.addClient(client);
}
}
if (rep.getClients().isEmpty()) {
policyStore.delete(associatedPolicy.getId());
} else {
RepresentationToModel.toModel(rep, authorization, associatedPolicy);
}
}
}
Set<String> updatedRoles = representation.getRoles();
if (updatedRoles != null) {
boolean createPolicy = true;
for (Policy associatedPolicy : associatedPolicies) {
if ("role".equals(associatedPolicy.getType())) {
createPolicy = false;
}
}
if (createPolicy) {
for (String role : updatedRoles) {
createRolePolicy(policy, policyStore, role, policy.getOwner());
}
}
}
Set<String> updatedGroups = representation.getGroups();
if (updatedGroups != null) {
boolean createPolicy = true;
for (Policy associatedPolicy : associatedPolicies) {
if ("group".equals(associatedPolicy.getType())) {
createPolicy = false;
}
}
if (createPolicy) {
for (String group : updatedGroups) {
createGroupPolicy(policy, policyStore, group, policy.getOwner());
}
}
}
Set<String> updatedClients = representation.getClients();
if (updatedClients != null) {
boolean createPolicy = true;
for (Policy associatedPolicy : associatedPolicies) {
if ("client".equals(associatedPolicy.getType())) {
createPolicy = false;
}
}
if (createPolicy) {
for (String client : updatedClients) {
createClientPolicy(policy, policyStore, client, policy.getOwner());
}
}
}
String condition = representation.getCondition();
if (condition != null) {
boolean createPolicy = true;
for (Policy associatedPolicy : associatedPolicies) {
if ("js".equals(associatedPolicy.getType())) {
createPolicy = false;
}
}
if (createPolicy) {
createJSPolicy(policy, policyStore, condition, policy.getOwner());
}
}
}
@Override
public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
verifyCircularReference(policy, new ArrayList<>());
}
@Override
public PolicyRepresentation toRepresentation(Policy policy) {
return new PolicyRepresentation();
public UmaPermissionRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
UmaPermissionRepresentation representation = new UmaPermissionRepresentation();
representation.setScopes(policy.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()));
representation.setOwner(policy.getOwner());
for (Policy associatedPolicy : policy.getAssociatedPolicies()) {
AbstractPolicyRepresentation associatedRep = ModelToRepresentation.toRepresentation(associatedPolicy, authorization, false, false);
RealmModel realm = authorization.getRealm();
if ("role".equals(associatedRep.getType())) {
RolePolicyRepresentation rep = RolePolicyRepresentation.class.cast(associatedRep);
for (RoleDefinition definition : rep.getRoles()) {
RoleModel role = realm.getRoleById(definition.getId());
if (role.isClientRole()) {
representation.addClientRole(ClientModel.class.cast(role.getContainer()).getClientId(),role.getName());
} else {
representation.addRole(role.getName());
}
}
} else if ("js".equals(associatedRep.getType())) {
JSPolicyRepresentation rep = JSPolicyRepresentation.class.cast(associatedRep);
representation.setCondition(rep.getCode());
} else if ("group".equals(associatedRep.getType())) {
GroupPolicyRepresentation rep = GroupPolicyRepresentation.class.cast(associatedRep);
for (GroupDefinition definition : rep.getGroups()) {
representation.addGroup(ModelToRepresentation.buildGroupPath(realm.getGroupById(definition.getId())));
}
} else if ("client".equals(associatedRep.getType())) {
ClientPolicyRepresentation rep = ClientPolicyRepresentation.class.cast(associatedRep);
for (String client : rep.getClients()) {
representation.addClient(realm.getClientById(client).getClientId());
}
}
}
return representation;
}
@Override
public Class<PolicyRepresentation> getRepresentationType() {
return PolicyRepresentation.class;
}
private void verifyCircularReference(Policy policy, List<String> ids) {
if (!policy.getType().equals("uma")) {
return;
}
if (ids.contains(policy.getId())) {
throw new RuntimeException("Circular reference found [" + policy.getName() + "].");
}
ids.add(policy.getId());
for (Policy associated : policy.getAssociatedPolicies()) {
verifyCircularReference(associated, ids);
}
public Class<UmaPermissionRepresentation> getRepresentationType() {
return UmaPermissionRepresentation.class;
}
@Override
public void onRemove(Policy policy, AuthorizationProvider authorization) {
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
for (Policy associatedPolicy : policy.getAssociatedPolicies()) {
policyStore.delete(associatedPolicy.getId());
}
}
@Override
@@ -125,4 +339,56 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<PolicyRep
public String getId() {
return "uma";
}
private void createJSPolicy(Policy policy, PolicyStore policyStore, String condition, String owner) {
JSPolicyRepresentation rep = new JSPolicyRepresentation();
rep.setName(KeycloakModelUtils.generateId());
rep.setCode(condition);
Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
associatedPolicy.setOwner(owner);
policy.addAssociatedPolicy(associatedPolicy);
}
private void createClientPolicy(Policy policy, PolicyStore policyStore, String client, String owner) {
ClientPolicyRepresentation rep = new ClientPolicyRepresentation();
rep.setName(KeycloakModelUtils.generateId());
rep.addClient(client);
Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
associatedPolicy.setOwner(owner);
policy.addAssociatedPolicy(associatedPolicy);
}
private void createGroupPolicy(Policy policy, PolicyStore policyStore, String group, String owner) {
GroupPolicyRepresentation rep = new GroupPolicyRepresentation();
rep.setName(KeycloakModelUtils.generateId());
rep.addGroupPath(group);
Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
associatedPolicy.setOwner(owner);
policy.addAssociatedPolicy(associatedPolicy);
}
private void createRolePolicy(Policy policy, PolicyStore policyStore, String role, String owner) {
RolePolicyRepresentation rep = new RolePolicyRepresentation();
rep.setName(KeycloakModelUtils.generateId());
rep.addRole(role, false);
Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
associatedPolicy.setOwner(owner);
policy.addAssociatedPolicy(associatedPolicy);
}
}
@@ -18,7 +18,7 @@
package org.keycloak.authorization.policy.provider.role;
import java.util.Set;
import java.util.function.Function;
import java.util.function.BiFunction;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.identity.Identity;
@@ -35,16 +35,16 @@ import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
*/
public class RolePolicyProvider implements PolicyProvider {
private final Function<Policy, RolePolicyRepresentation> representationFunction;
private final BiFunction<Policy, AuthorizationProvider, RolePolicyRepresentation> representationFunction;
public RolePolicyProvider(Function<Policy, RolePolicyRepresentation> representationFunction) {
public RolePolicyProvider(BiFunction<Policy, AuthorizationProvider, RolePolicyRepresentation> representationFunction) {
this.representationFunction = representationFunction;
}
@Override
public void evaluate(Evaluation evaluation) {
Policy policy = evaluation.getPolicy();
Set<RolePolicyRepresentation.RoleDefinition> roleIds = representationFunction.apply(policy).getRoles();
Set<RolePolicyRepresentation.RoleDefinition> roleIds = representationFunction.apply(policy, evaluation.getAuthorizationProvider()).getRoles();
AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
Identity identity = evaluation.getContext().getIdentity();
@@ -52,7 +52,7 @@ import java.util.Set;
*/
public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePolicyRepresentation> {
private RolePolicyProvider provider = new RolePolicyProvider(policy -> toRepresentation(policy));
private RolePolicyProvider provider = new RolePolicyProvider(this::toRepresentation);
@Override
public String getName() {
@@ -75,7 +75,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
}
@Override
public RolePolicyRepresentation toRepresentation(Policy policy) {
public RolePolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
RolePolicyRepresentation representation = new RolePolicyRepresentation();
try {
@@ -114,7 +114,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
@Override
public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorizationProvider) {
Map<String, String> config = new HashMap<>();
Set<RolePolicyRepresentation.RoleDefinition> roles = toRepresentation(policy).getRoles();
Set<RolePolicyRepresentation.RoleDefinition> roles = toRepresentation(policy, authorizationProvider).getRoles();
for (RolePolicyRepresentation.RoleDefinition roleDefinition : roles) {
RoleModel role = authorizationProvider.getRealm().getRoleById(roleDefinition.getId());
@@ -66,7 +66,7 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory<TimePoli
}
@Override
public TimePolicyRepresentation toRepresentation(Policy policy) {
public TimePolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
TimePolicyRepresentation representation = new TimePolicyRepresentation();
Map<String, String> config = policy.getConfig();
@@ -17,8 +17,10 @@
*/
package org.keycloak.authorization.policy.provider.user;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.evaluation.Evaluation;
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
@@ -30,16 +32,16 @@ import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
*/
public class UserPolicyProvider implements PolicyProvider {
private final Function<Policy, UserPolicyRepresentation> representationFunction;
private final BiFunction<Policy, AuthorizationProvider, UserPolicyRepresentation> representationFunction;
public UserPolicyProvider(Function<Policy, UserPolicyRepresentation> representationFunction) {
public UserPolicyProvider(BiFunction<Policy, AuthorizationProvider, UserPolicyRepresentation> representationFunction) {
this.representationFunction = representationFunction;
}
@Override
public void evaluate(Evaluation evaluation) {
EvaluationContext context = evaluation.getContext();
UserPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy());
UserPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy(), evaluation.getAuthorizationProvider());
for (String userId : representation.getUsers()) {
if (context.getIdentity().getId().equals(userId)) {
@@ -25,7 +25,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.keycloak.Config;
@@ -52,7 +51,7 @@ import org.keycloak.util.JsonSerialization;
*/
public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPolicyRepresentation> {
private UserPolicyProvider provider = new UserPolicyProvider((Function<Policy, UserPolicyRepresentation>) policy -> toRepresentation(policy));
private UserPolicyProvider provider = new UserPolicyProvider(this::toRepresentation);
@Override
public String getName() {
@@ -75,7 +74,7 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPoli
}
@Override
public UserPolicyRepresentation toRepresentation(Policy policy) {
public UserPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
UserPolicyRepresentation representation = new UserPolicyRepresentation();
try {
@@ -113,7 +112,7 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPoli
@Override
public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorizationProvider) {
UserPolicyRepresentation userRep = toRepresentation(policy);
UserPolicyRepresentation userRep = toRepresentation(policy, authorizationProvider);
Map<String, String> config = new HashMap<>();
try {
@@ -51,7 +51,7 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory<RulePo
}
@Override
public RulePolicyRepresentation toRepresentation(Policy policy) {
public RulePolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
RulePolicyRepresentation representation = new RulePolicyRepresentation();
representation.setArtifactGroupId(policy.getConfig().get("mavenArtifactGroupId"));