[KEYCLOAK-7353] Support Policy Management in Protection API
See https://issues.jboss.org/browse/KEYCLOAK-7353
This commit is contained in:
1 parent
f8919f8baa
commit
5a9bfea419
64 files changed
+2245
-375
No files matched your search
+7
@@ -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;
|
||||
}
|
||||
}
|
||||
+187
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -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.
|
||||
*
|
||||
|
||||
+1
@@ -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()) {
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -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();
|
||||
|
||||
+5
-5
@@ -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);
|
||||
|
||||
+4
-4
@@ -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());
|
||||
|
||||
|
||||
+14
-11
@@ -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();
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+295
-29
@@ -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);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -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();
|
||||
|
||||
+3
-3
@@ -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());
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
|
||||
+5
-3
@@ -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)) {
|
||||
|
||||
+3
-4
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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"));
|
||||
|
||||
Reference in new issue
Block a user