Merge pull request #5240 from pedroigor/KEYCLOAK-7353
[KEYCLOAK-7353] Support Policy Management in Protection API
This commit is contained in:
commit
aa128d6c07
64 changed files with 2245 additions and 375 deletions
|
@ -28,6 +28,7 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.AuthorizationContext;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.HttpFacade.Request;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.ClientAuthorizationContext;
|
||||
|
@ -165,7 +166,7 @@ public abstract class AbstractPolicyEnforcer {
|
|||
policyEnforcer.getPathMatcher().removeFromCache(getPath(request));
|
||||
}
|
||||
|
||||
return hasValidClaims(actualPathConfig, httpFacade, authorization);
|
||||
return hasValidClaims(actualPathConfig, permission, httpFacade, authorization);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -187,35 +188,22 @@ public abstract class AbstractPolicyEnforcer {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean hasValidClaims(PathConfig actualPathConfig, OIDCHttpFacade httpFacade, Authorization authorization) {
|
||||
Map<String, Map<String, Object>> claimInformationPointConfig = actualPathConfig.getClaimInformationPointConfig();
|
||||
private boolean hasValidClaims(PathConfig actualPathConfig, Permission permission, OIDCHttpFacade httpFacade, Authorization authorization) {
|
||||
Map<String, Set<String>> grantedClaims = permission.getClaims();
|
||||
|
||||
if (claimInformationPointConfig != null) {
|
||||
Map<String, List<String>> claims = new HashMap<>();
|
||||
if (grantedClaims != null) {
|
||||
Map<String, List<String>> claims = resolveClaims(actualPathConfig, httpFacade);
|
||||
|
||||
for (Entry<String, Map<String, Object>> entry : claimInformationPointConfig.entrySet()) {
|
||||
ClaimInformationPointProviderFactory factory = policyEnforcer.getClaimInformationPointProviderFactories().get(entry.getKey());
|
||||
|
||||
if (factory == null) {
|
||||
throw new RuntimeException("Could not find claim information provider with name [" + entry.getKey() + "]");
|
||||
}
|
||||
|
||||
claims.putAll(factory.create(entry.getValue()).resolve(httpFacade));
|
||||
if (claims.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, List<String>> grantedClaims = authorization.getClaims();
|
||||
for (Entry<String, Set<String>> entry : grantedClaims.entrySet()) {
|
||||
List<String> requestClaims = claims.get(entry.getKey());
|
||||
|
||||
if (grantedClaims != null) {
|
||||
if (claims.isEmpty()) {
|
||||
if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
|
||||
return false;
|
||||
}
|
||||
for (Entry<String, List<String>> entry : grantedClaims.entrySet()) {
|
||||
List<String> requestClaims = claims.get(entry.getKey());
|
||||
|
||||
if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,4 +330,28 @@ public abstract class AbstractPolicyEnforcer {
|
|||
private PathConfig getPathConfig(Request request) {
|
||||
return isDefaultAccessDeniedUri(request) ? null : policyEnforcer.getPathMatcher().matches(getPath(request));
|
||||
}
|
||||
|
||||
protected Map<String, List<String>> resolveClaims(PathConfig pathConfig, OIDCHttpFacade httpFacade) {
|
||||
Map<String, List<String>> claims = getClaims(getEnforcerConfig().getClaimInformationPointConfig(), httpFacade);
|
||||
|
||||
claims.putAll(getClaims(pathConfig.getClaimInformationPointConfig(), httpFacade));
|
||||
|
||||
return claims;
|
||||
}
|
||||
|
||||
private Map<String, List<String>> getClaims(Map<String, Map<String, Object>>claimInformationPointConfig, HttpFacade httpFacade) {
|
||||
Map<String, List<String>> claims = new HashMap<>();
|
||||
|
||||
if (claimInformationPointConfig != null) {
|
||||
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
|
||||
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
|
||||
|
||||
if (factory != null) {
|
||||
claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return claims;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
|
@ -87,19 +86,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
|||
grantedPermissions.add(newPermission);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<String>> newClaims = newAuthorization.getClaims();
|
||||
|
||||
if (newClaims != null) {
|
||||
Map<String, List<String>> claims = authorization.getClaims();
|
||||
|
||||
if (claims == null) {
|
||||
claims = new HashMap<>();
|
||||
authorization.setClaims(claims);
|
||||
}
|
||||
|
||||
claims.putAll(newClaims);
|
||||
}
|
||||
}
|
||||
|
||||
original.setAuthorization(authorization);
|
||||
|
@ -169,11 +155,11 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
|||
String ticket = getPermissionTicket(pathConfig, methodConfig, getAuthzClient(), httpFacade);
|
||||
authzRequest.setTicket(ticket);
|
||||
} else {
|
||||
if (accessToken.getAuthorization() != null) {
|
||||
if (isBearerAuthorization(httpFacade) || accessToken.getAuthorization() != null) {
|
||||
authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
|
||||
}
|
||||
|
||||
Map<String, List<String>> claims = getClaims(pathConfig, httpFacade);
|
||||
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
|
||||
|
||||
if (!claims.isEmpty()) {
|
||||
authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
|
||||
|
@ -186,7 +172,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
|||
}
|
||||
|
||||
LOGGER.debug("Obtaining authorization for authenticated user.");
|
||||
AuthorizationResponse authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
|
||||
AuthorizationResponse authzResponse;
|
||||
|
||||
if (isBearerAuthorization(httpFacade)) {
|
||||
authzRequest.setSubjectToken(accessTokenString);
|
||||
authzResponse = getAuthzClient().authorization().authorize(authzRequest);
|
||||
} else {
|
||||
authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
|
||||
}
|
||||
|
||||
if (authzResponse != null) {
|
||||
return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
|
||||
|
@ -200,7 +193,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
|||
return null;
|
||||
}
|
||||
|
||||
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, HttpFacade httpFacade) {
|
||||
private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, OIDCHttpFacade httpFacade) {
|
||||
if (getEnforcerConfig().getUserManagedAccess() != null) {
|
||||
ProtectionResource protection = authzClient.protection();
|
||||
PermissionResource permission = protection.permission();
|
||||
|
@ -209,7 +202,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
|||
permissionRequest.setResourceId(pathConfig.getId());
|
||||
permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
|
||||
|
||||
Map<String, List<String>> claims = getClaims(pathConfig, httpFacade);
|
||||
Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
|
||||
|
||||
if (!claims.isEmpty()) {
|
||||
permissionRequest.setClaims(claims);
|
||||
|
@ -221,22 +214,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
|||
return null;
|
||||
}
|
||||
|
||||
private Map<String, List<String>> getClaims(PathConfig pathConfig, HttpFacade httpFacade) {
|
||||
Map<String, List<String>> claims = new HashMap<>();
|
||||
Map<String, Map<String, Object>> claimInformationPointConfig = pathConfig.getClaimInformationPointConfig();
|
||||
|
||||
if (claimInformationPointConfig != null) {
|
||||
for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
|
||||
ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
|
||||
|
||||
if (factory != null) {
|
||||
claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
|
||||
}
|
||||
}
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
private boolean isBearerAuthorization(OIDCHttpFacade httpFacade) {
|
||||
List<String> authHeaders = httpFacade.getRequest().getHeaders("Authorization");
|
||||
if (authHeaders == null || authHeaders.size() == 0) {
|
||||
|
|
|
@ -319,6 +319,7 @@ public class PolicyEnforcer {
|
|||
config.setMethods(originalConfig.getMethods());
|
||||
config.setParentConfig(originalConfig);
|
||||
config.setEnforcementMode(originalConfig.getEnforcementMode());
|
||||
config.setClaimInformationPointConfig(originalConfig.getClaimInformationPointConfig());
|
||||
|
||||
return config;
|
||||
}
|
||||
|
|
|
@ -103,6 +103,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"));
|
||||
|
|
|
@ -88,9 +88,6 @@ public class AccessToken extends IDToken {
|
|||
@JsonProperty("permissions")
|
||||
private List<Permission> permissions;
|
||||
|
||||
@JsonProperty("claims")
|
||||
private Map<String, List<String>> claims;
|
||||
|
||||
public List<Permission> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
@ -98,14 +95,6 @@ public class AccessToken extends IDToken {
|
|||
public void setPermissions(List<Permission> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public void setClaims(Map<String, List<String>> claims) {
|
||||
this.claims = claims;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getClaims() {
|
||||
return claims;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonProperty("trusted-certs")
|
||||
|
|
|
@ -54,6 +54,10 @@ public class PolicyEnforcerConfig {
|
|||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private UserManagedAccessConfig userManagedAccess;
|
||||
|
||||
@JsonProperty("claim-information-point")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private Map<String, Map<String, Object>> claimInformationPointConfig;
|
||||
|
||||
public List<PathConfig> getPaths() {
|
||||
return this.paths;
|
||||
}
|
||||
|
@ -102,6 +106,14 @@ public class PolicyEnforcerConfig {
|
|||
this.onDenyRedirectTo = onDenyRedirectTo;
|
||||
}
|
||||
|
||||
public Map<String, Map<String, Object>> getClaimInformationPointConfig() {
|
||||
return claimInformationPointConfig;
|
||||
}
|
||||
|
||||
public void setClaimInformationPointConfig(Map<String, Map<String, Object>> config) {
|
||||
this.claimInformationPointConfig = config;
|
||||
}
|
||||
|
||||
public static class PathConfig {
|
||||
|
||||
public static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
|
||||
|
|
|
@ -35,6 +35,7 @@ public class AbstractPolicyRepresentation {
|
|||
private Set<String> scopes;
|
||||
private Logic logic = Logic.POSITIVE;
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
private String owner;
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
|
@ -135,6 +136,20 @@ public class AbstractPolicyRepresentation {
|
|||
this.scopes.addAll(Arrays.asList(id));
|
||||
}
|
||||
|
||||
public void removeScope(String scope) {
|
||||
if (scopes != null) {
|
||||
scopes.remove(scope);
|
||||
}
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -39,7 +39,7 @@ public class AuthorizationRequest {
|
|||
private PermissionTicketToken permissions = new PermissionTicketToken();
|
||||
private Metadata metadata;
|
||||
private String audience;
|
||||
private String accessToken;
|
||||
private String subjectToken;
|
||||
private boolean submitRequest;
|
||||
private Map<String, List<String>> claims;
|
||||
|
||||
|
@ -123,12 +123,12 @@ public class AuthorizationRequest {
|
|||
return audience;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
public void setSubjectToken(String subjectToken) {
|
||||
this.subjectToken = subjectToken;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
public String getSubjectToken() {
|
||||
return subjectToken;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getClaims() {
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2016 Red Hat, Inc., and individual 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.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:federico@martel-innovate.com">Federico M. Facca</a>
|
||||
*/
|
||||
public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
|
||||
|
||||
private String id;
|
||||
private String description;
|
||||
private Set<String> roles;
|
||||
private Set<String> groups;
|
||||
private Set<String> clients;
|
||||
private String condition;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "uma";
|
||||
}
|
||||
|
||||
public void setId(String id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setRoles(Set<String> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public void addRole(String... role) {
|
||||
if (roles == null) {
|
||||
roles = new HashSet<>();
|
||||
}
|
||||
|
||||
roles.addAll(Arrays.asList(role));
|
||||
}
|
||||
|
||||
public void addClientRole(String clientId, String roleName) {
|
||||
addRole(clientId + "/" + roleName);
|
||||
}
|
||||
|
||||
public void removeRole(String role) {
|
||||
if (roles != null) {
|
||||
roles.remove(role);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setGroups(Set<String> groups) {
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
public void addGroup(String... group) {
|
||||
if (groups == null) {
|
||||
groups = new HashSet<>();
|
||||
}
|
||||
|
||||
groups.addAll(Arrays.asList(group));
|
||||
}
|
||||
|
||||
public void removeGroup(String group) {
|
||||
if (groups != null) {
|
||||
groups.remove(group);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
public void setClients(Set<String> clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
public void addClient(String... client) {
|
||||
if (clients == null) {
|
||||
clients = new HashSet<>();
|
||||
}
|
||||
|
||||
clients.addAll(Arrays.asList(client));
|
||||
}
|
||||
|
||||
public void removeClient(String client) {
|
||||
if (clients != null) {
|
||||
clients.remove(client);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public void setCondition(String condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
}
|
|
@ -454,7 +454,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
protected class ScopeCache implements ScopeStore {
|
||||
@Override
|
||||
public Scope create(String name, ResourceServer resourceServer) {
|
||||
Scope scope = getScopeStoreDelegate().create(name, resourceServer);
|
||||
return create(null, name, resourceServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope create(String id, String name, ResourceServer resourceServer) {
|
||||
Scope scope = getScopeStoreDelegate().create(id, name, resourceServer);
|
||||
registerScopeInvalidation(scope.getId(), scope.getName(), resourceServer.getId());
|
||||
return scope;
|
||||
}
|
||||
|
@ -538,7 +543,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
protected class ResourceCache implements ResourceStore {
|
||||
@Override
|
||||
public Resource create(String name, ResourceServer resourceServer, String owner) {
|
||||
Resource resource = getResourceStoreDelegate().create(name, resourceServer, owner);
|
||||
return create(null, name, resourceServer, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
|
||||
Resource resource = getResourceStoreDelegate().create(id, name, resourceServer, owner);
|
||||
Resource cached = findById(resource.getId(), resourceServer.getId());
|
||||
registerResourceInvalidation(resource.getId(), resource.getName(), resource.getType(), resource.getUri(), resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toSet()), resourceServer.getId(), resource.getOwner());
|
||||
return cached;
|
||||
|
|
|
@ -206,6 +206,8 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
|
|||
predicates.add(builder.isNull(root.get("requester")));
|
||||
} else if (PermissionTicket.POLICY_IS_NOT_NULL.equals(name)) {
|
||||
predicates.add(builder.isNotNull(root.get("policy")));
|
||||
} else if (PermissionTicket.POLICY.equals(name)) {
|
||||
predicates.add(root.join("policy").get("id").in(value));
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported filter [" + name + "]");
|
||||
}
|
||||
|
|
|
@ -58,7 +58,12 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
|
||||
PolicyEntity entity = new PolicyEntity();
|
||||
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
if (representation.getId() == null) {
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
} else {
|
||||
entity.setId(representation.getId());
|
||||
}
|
||||
|
||||
entity.setType(representation.getType());
|
||||
entity.setName(representation.getName());
|
||||
entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer));
|
||||
|
@ -136,9 +141,9 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
attributes.forEach((name, value) -> {
|
||||
if ("permission".equals(name)) {
|
||||
if (Boolean.valueOf(value[0])) {
|
||||
predicates.add(root.get("type").in("resource", "scope"));
|
||||
predicates.add(root.get("type").in("resource", "scope", "uma"));
|
||||
} else {
|
||||
predicates.add(builder.not(root.get("type").in("resource", "scope")));
|
||||
predicates.add(builder.not(root.get("type").in("resource", "scope", "uma")));
|
||||
}
|
||||
} else if ("id".equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
|
@ -148,6 +153,8 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
predicates.add(builder.isNotNull(root.get("owner")));
|
||||
} else if ("resource".equals(name)) {
|
||||
predicates.add(root.join("resources").get("id").in(value));
|
||||
} else if ("scope".equals(name)) {
|
||||
predicates.add(root.join("scopes").get("id").in(value));
|
||||
} else {
|
||||
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
|
||||
}
|
||||
|
|
|
@ -53,9 +53,19 @@ public class JPAResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public Resource create(String name, ResourceServer resourceServer, String owner) {
|
||||
return create(null, name, resourceServer, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
|
||||
ResourceEntity entity = new ResourceEntity();
|
||||
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
if (id == null) {
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
} else {
|
||||
entity.setId(id);
|
||||
}
|
||||
|
||||
entity.setName(name);
|
||||
entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer));
|
||||
entity.setOwner(owner);
|
||||
|
@ -185,6 +195,8 @@ public class JPAResourceStore implements ResourceStore {
|
|||
predicates.add(builder.equal(builder.lower(root.get(name)), value[0].toLowerCase()));
|
||||
} else if ("uri_not_null".equals(name)) {
|
||||
predicates.add(builder.isNotNull(root.get("uri")));
|
||||
} else if ("owner".equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
} else {
|
||||
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
|
||||
}
|
||||
|
|
|
@ -54,9 +54,19 @@ public class JPAScopeStore implements ScopeStore {
|
|||
|
||||
@Override
|
||||
public Scope create(final String name, final ResourceServer resourceServer) {
|
||||
return create(null, name, resourceServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope create(String id, final String name, final ResourceServer resourceServer) {
|
||||
ScopeEntity entity = new ScopeEntity();
|
||||
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
if (id == null) {
|
||||
entity.setId(KeycloakModelUtils.generateId());
|
||||
} else {
|
||||
entity.setId(id);
|
||||
}
|
||||
|
||||
entity.setName(name);
|
||||
entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer));
|
||||
|
||||
|
|
|
@ -232,6 +232,11 @@ public final class AuthorizationProvider implements Provider {
|
|||
return delegate.create(name, resourceServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope create(String id, String name, ResourceServer resourceServer) {
|
||||
return delegate.create(id, name, resourceServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
Scope scope = findById(id, null);
|
||||
|
@ -411,6 +416,11 @@ public final class AuthorizationProvider implements Provider {
|
|||
return delegate.create(name, resourceServer, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
|
||||
return delegate.create(id, name, resourceServer, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
Resource resource = findById(id, null);
|
||||
|
|
|
@ -30,6 +30,7 @@ public interface PermissionTicket {
|
|||
String REQUESTER = "requester";
|
||||
String REQUESTER_IS_NULL = "requester_is_null";
|
||||
String POLICY_IS_NOT_NULL = "policy_is_not_null";
|
||||
String POLICY = "policy";
|
||||
|
||||
/**
|
||||
* Returns the unique identifier for this instance.
|
||||
|
|
|
@ -22,11 +22,14 @@ import org.keycloak.authorization.model.Resource;
|
|||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -42,9 +45,19 @@ public class ResourcePermission {
|
|||
private Map<String, Set<String>> claims;
|
||||
|
||||
public ResourcePermission(Resource resource, List<Scope> scopes, ResourceServer resourceServer) {
|
||||
this(resource, scopes, resourceServer, null);
|
||||
}
|
||||
|
||||
public ResourcePermission(Resource resource, List<Scope> scopes, ResourceServer resourceServer, Map<String, ? extends Collection<String>> claims) {
|
||||
this.resource = resource;
|
||||
this.scopes = scopes;
|
||||
this.resourceServer = resourceServer;
|
||||
if (claims != null) {
|
||||
this.claims = new HashMap<>();
|
||||
for (Entry<String, ? extends Collection<String>> entry : claims.entrySet()) {
|
||||
this.claims.computeIfAbsent(entry.getKey(), key -> new LinkedHashSet<>()).addAll(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -192,6 +192,16 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
|
|||
}
|
||||
}
|
||||
|
||||
if (policyResources.isEmpty() && scopes.isEmpty()) {
|
||||
String defaultResourceType = policy.getConfig().get("defaultResourceType");
|
||||
|
||||
if (defaultResourceType == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return defaultResourceType.equals(permission.getResource().getType());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.evaluation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -26,6 +26,7 @@ import java.util.stream.Collectors;
|
|||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.identity.Identity;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
|
@ -57,6 +58,50 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
|
|||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDecision(DefaultEvaluation evaluation) {
|
||||
super.onDecision(evaluation);
|
||||
removePermissionsIfGranted(evaluation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes permissions (represented by {@code ticket}) granted by any user-managed policy so we don't create unnecessary permission tickets.
|
||||
*
|
||||
* @param evaluation the evaluation
|
||||
*/
|
||||
private void removePermissionsIfGranted(DefaultEvaluation evaluation) {
|
||||
if (Effect.PERMIT.equals(evaluation.getEffect())) {
|
||||
Policy policy = evaluation.getParentPolicy();
|
||||
|
||||
if ("uma".equals(policy.getType())) {
|
||||
ResourcePermission grantedPermission = evaluation.getPermission();
|
||||
List<PermissionTicketToken.ResourcePermission> permissions = ticket.getResources();
|
||||
|
||||
Iterator<PermissionTicketToken.ResourcePermission> itPermissions = permissions.iterator();
|
||||
|
||||
while (itPermissions.hasNext()) {
|
||||
PermissionTicketToken.ResourcePermission permission = itPermissions.next();
|
||||
|
||||
if (permission.getResourceId().equals(grantedPermission.getResource().getId())) {
|
||||
Set<String> scopes = permission.getScopes();
|
||||
Iterator<String> itScopes = scopes.iterator();
|
||||
|
||||
while (itScopes.hasNext()) {
|
||||
Scope scope = authorization.getStoreFactory().getScopeStore().findByName(itScopes.next(), resourceServer.getId());
|
||||
if (policy.getScopes().contains(scope)) {
|
||||
itScopes.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (scopes.isEmpty()) {
|
||||
itPermissions.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
super.onComplete();
|
||||
|
@ -64,17 +109,17 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
|
|||
if (request.isSubmitRequest()) {
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
List<PermissionTicketToken.ResourcePermission> resources = ticket.getResources();
|
||||
List<PermissionTicketToken.ResourcePermission> permissions = ticket.getResources();
|
||||
|
||||
if (resources != null) {
|
||||
for (PermissionTicketToken.ResourcePermission permission : resources) {
|
||||
if (permissions != null) {
|
||||
for (PermissionTicketToken.ResourcePermission permission : permissions) {
|
||||
Resource resource = resourceStore.findById(permission.getResourceId(), resourceServer.getId());
|
||||
|
||||
if (resource == null) {
|
||||
resource = resourceStore.findByName(permission.getResourceId(), identity.getId(), resourceServer.getId());
|
||||
}
|
||||
|
||||
if (!resource.isOwnerManagedAccess() || resource.getOwner().equals(identity.getId()) || resource.getOwner().equals(resourceServer.getId())) {
|
||||
if (resource == null || !resource.isOwnerManagedAccess() || resource.getOwner().equals(identity.getId()) || resource.getOwner().equals(resourceServer.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -91,9 +136,9 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
|
|||
filters.put(PermissionTicket.REQUESTER, identity.getId());
|
||||
filters.put(PermissionTicket.SCOPE_IS_NULL, Boolean.TRUE.toString());
|
||||
|
||||
List<PermissionTicket> permissions = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
|
||||
List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
|
||||
|
||||
if (permissions.isEmpty()) {
|
||||
if (tickets.isEmpty()) {
|
||||
authorization.getStoreFactory().getPermissionTicketStore().create(resource.getId(), null, identity.getId(), resource.getResourceServer());
|
||||
}
|
||||
} else {
|
||||
|
@ -112,9 +157,9 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
|
|||
filters.put(PermissionTicket.REQUESTER, identity.getId());
|
||||
filters.put(PermissionTicket.SCOPE, scope.getId());
|
||||
|
||||
List<PermissionTicket> permissions = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
|
||||
List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
|
||||
|
||||
if (permissions.isEmpty()) {
|
||||
if (tickets.isEmpty()) {
|
||||
authorization.getStoreFactory().getPermissionTicketStore().create(resource.getId(), scope.getId(), identity.getId(), resource.getResourceServer());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public interface PolicyProviderFactory<R extends AbstractPolicyRepresentation> e
|
|||
|
||||
PolicyProvider create(AuthorizationProvider authorization);
|
||||
|
||||
R toRepresentation(Policy policy);
|
||||
R toRepresentation(Policy policy, AuthorizationProvider authorization);
|
||||
|
||||
Class<R> getRepresentationType();
|
||||
|
||||
|
|
|
@ -40,6 +40,17 @@ public interface ResourceStore {
|
|||
*/
|
||||
Resource create(String name, ResourceServer resourceServer, String owner);
|
||||
|
||||
/**
|
||||
* <p>Creates a {@link Resource} instance backed by this persistent storage implementation.
|
||||
*
|
||||
* @param id the id of this resource. It must be unique.
|
||||
* @param name the name of this resource. It must be unique.
|
||||
* @param resourceServer the resource server to where the given resource belongs to
|
||||
* @param owner the owner of this resource or null if the resource server is the owner
|
||||
* @return an instance backed by the underlying storage implementation
|
||||
*/
|
||||
Resource create(String id, String name, ResourceServer resourceServer, String owner);
|
||||
|
||||
/**
|
||||
* Removes a {@link Resource} instance, with the given {@code id} from the persistent storage.
|
||||
*
|
||||
|
|
|
@ -42,6 +42,18 @@ public interface ScopeStore {
|
|||
*/
|
||||
Scope create(String name, ResourceServer resourceServer);
|
||||
|
||||
/**
|
||||
* Creates a new {@link Scope} instance. The new instance is not necessarily persisted though, which may require
|
||||
* a call to the {#save} method to actually make it persistent.
|
||||
*
|
||||
* @param id the id of the scope
|
||||
* @param name the name of the scope
|
||||
* @param resourceServer the resource server to which this scope belongs
|
||||
*
|
||||
* @return a new instance of {@link Scope}
|
||||
*/
|
||||
Scope create(String id, String name, ResourceServer resourceServer);
|
||||
|
||||
/**
|
||||
* Deletes a scope from the underlying persistence mechanism.
|
||||
*
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.keycloak.models.utils;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import java.io.IOException;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -24,7 +26,6 @@ import org.keycloak.authorization.model.Resource;
|
|||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
|
@ -37,9 +38,11 @@ import org.keycloak.provider.ProviderConfigProperty;
|
|||
import org.keycloak.representations.idm.*;
|
||||
import org.keycloak.representations.idm.authorization.*;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -777,7 +780,7 @@ public class ModelToRepresentation {
|
|||
}
|
||||
} else {
|
||||
try {
|
||||
representation = (R) providerFactory.toRepresentation(policy);
|
||||
representation = (R) providerFactory.toRepresentation(policy, authorization);
|
||||
} catch (Exception cause) {
|
||||
throw new RuntimeException("Could not create policy [" + policy.getType() + "] representation", cause);
|
||||
}
|
||||
|
|
|
@ -2381,7 +2381,7 @@ public class RepresentationToModel {
|
|||
return existing;
|
||||
}
|
||||
|
||||
Resource model = resourceStore.create(resource.getName(), resourceServer, ownerId);
|
||||
Resource model = resourceStore.create(resource.getId(), resource.getName(), resourceServer, ownerId);
|
||||
|
||||
model.setDisplayName(resource.getDisplayName());
|
||||
model.setType(resource.getType());
|
||||
|
@ -2426,7 +2426,7 @@ public class RepresentationToModel {
|
|||
return existing;
|
||||
}
|
||||
|
||||
Scope model = scopeStore.create(scope.getName(), resourceServer);
|
||||
Scope model = scopeStore.create(scope.getId(), scope.getName(), resourceServer);
|
||||
|
||||
model.setDisplayName(scope.getDisplayName());
|
||||
model.setIconUri(scope.getIconUri());
|
||||
|
|
|
@ -19,11 +19,11 @@ package org.keycloak.authorization.admin;
|
|||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -34,11 +34,9 @@ import java.util.stream.Stream;
|
|||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
|
||||
|
@ -49,7 +47,6 @@ import org.keycloak.authorization.model.Resource;
|
|||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.permission.ResourcePermission;
|
||||
import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
|
||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||
import org.keycloak.authorization.policy.evaluation.Result;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
|
@ -62,10 +59,10 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
@ -90,36 +87,34 @@ public class PolicyEvaluationService {
|
|||
this.auth = auth;
|
||||
}
|
||||
|
||||
static class Decision extends DecisionResultCollector {
|
||||
Throwable error;
|
||||
List<Result> results;
|
||||
|
||||
@Override
|
||||
protected void onComplete(List<Result> results) {
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable cause) {
|
||||
this.error = cause;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> List<T> asList(T... a) {
|
||||
List<T> list = new LinkedList<T>();
|
||||
for (T t : a) list.add(t);
|
||||
return list;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
public Response evaluate(PolicyEvaluationRequest evaluationRequest) throws Throwable {
|
||||
public Response evaluate(PolicyEvaluationRequest evaluationRequest) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
CloseableKeycloakIdentity identity = createIdentity(evaluationRequest);
|
||||
try {
|
||||
return Response.ok(PolicyEvaluationResponseBuilder.build(evaluate(evaluationRequest, createEvaluationContext(evaluationRequest, identity)), resourceServer, authorization, identity)).build();
|
||||
AuthorizationRequest request = new AuthorizationRequest();
|
||||
Map<String, List<String>> claims = new HashMap<>();
|
||||
Map<String, String> givenAttributes = evaluationRequest.getContext().get("attributes");
|
||||
|
||||
if (givenAttributes != null) {
|
||||
givenAttributes.forEach((key, entryValue) -> {
|
||||
if (entryValue != null) {
|
||||
List<String> values = new ArrayList();
|
||||
|
||||
for (String value : entryValue.split(",")) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
claims.put(key, values);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
request.setClaims(claims);
|
||||
|
||||
return Response.ok(PolicyEvaluationResponseBuilder.build(evaluate(evaluationRequest, createEvaluationContext(evaluationRequest, identity), request), resourceServer, authorization, identity)).build();
|
||||
} catch (Exception e) {
|
||||
throw new ErrorResponseException(OAuthErrorException.SERVER_ERROR, "Error while evaluating permissions.", Status.INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
|
@ -127,8 +122,8 @@ public class PolicyEvaluationService {
|
|||
}
|
||||
}
|
||||
|
||||
private List<Result> evaluate(PolicyEvaluationRequest evaluationRequest, EvaluationContext evaluationContext) {
|
||||
return authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate();
|
||||
private List<Result> evaluate(PolicyEvaluationRequest evaluationRequest, EvaluationContext evaluationContext, AuthorizationRequest request) {
|
||||
return authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization, request), evaluationContext).evaluate();
|
||||
}
|
||||
|
||||
private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation, KeycloakIdentity identity) {
|
||||
|
@ -157,7 +152,7 @@ public class PolicyEvaluationService {
|
|||
};
|
||||
}
|
||||
|
||||
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
|
||||
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization, AuthorizationRequest request) {
|
||||
List<ResourceRepresentation> resources = representation.getResources();
|
||||
return resources.stream().flatMap((Function<ResourceRepresentation, Stream<ResourcePermission>>) resource -> {
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
|
@ -175,18 +170,18 @@ public class PolicyEvaluationService {
|
|||
|
||||
if (resource.getId() != null) {
|
||||
Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId(), resourceServer.getId());
|
||||
return Permissions.createResourcePermissions(resourceModel, scopeNames, authorization).stream();
|
||||
return Arrays.asList(Permissions.createResourcePermissions(resourceModel, scopeNames, authorization, request)).stream();
|
||||
} else if (resource.getType() != null) {
|
||||
return storeFactory.getResourceStore().findByType(resource.getType(), resourceServer.getId()).stream().flatMap(resource1 -> Permissions.createResourcePermissions(resource1, scopeNames, authorization).stream());
|
||||
return storeFactory.getResourceStore().findByType(resource.getType(), resourceServer.getId()).stream().map(resource1 -> Permissions.createResourcePermissions(resource1, scopeNames, authorization, request));
|
||||
} else {
|
||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||
List<Scope> scopes = scopeNames.stream().map(scopeName -> scopeStore.findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
|
||||
List<ResourcePermission> collect = new ArrayList<ResourcePermission>();
|
||||
List<ResourcePermission> collect = new ArrayList<>();
|
||||
|
||||
if (!scopes.isEmpty()) {
|
||||
collect.addAll(scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList()));
|
||||
collect.addAll(scopes.stream().map(scope -> new ResourcePermission(null, Arrays.asList(scope), resourceServer)).collect(Collectors.toList()));
|
||||
} else {
|
||||
collect.addAll(Permissions.all(resourceServer, evaluationContext.getIdentity(), authorization));
|
||||
collect.addAll(Permissions.all(resourceServer, evaluationContext.getIdentity(), authorization, request));
|
||||
}
|
||||
|
||||
return collect.stream();
|
||||
|
@ -266,13 +261,6 @@ public class PolicyEvaluationService {
|
|||
}
|
||||
|
||||
AccessToken.Access realmAccess = accessToken.getRealmAccess();
|
||||
Map<String, Object> claims = accessToken.getOtherClaims();
|
||||
Map<String, String> givenAttributes = representation.getContext().get("attributes");
|
||||
|
||||
if (givenAttributes != null) {
|
||||
givenAttributes.forEach((key, value) -> claims.put(key, asList(value)));
|
||||
}
|
||||
|
||||
|
||||
if (representation.getRoleIds() != null) {
|
||||
representation.getRoleIds().forEach(roleName -> realmAccess.addRole(roleName));
|
||||
|
|
|
@ -73,8 +73,10 @@ public class PolicyResourceService {
|
|||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response update(@Context UriInfo uriInfo,String payload) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
public Response update(@Context UriInfo uriInfo, String payload) {
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
}
|
||||
|
||||
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
|
||||
|
||||
|
@ -94,7 +96,9 @@ public class PolicyResourceService {
|
|||
|
||||
@DELETE
|
||||
public Response delete(@Context UriInfo uriInfo) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
}
|
||||
|
||||
if (policy == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -119,7 +123,9 @@ public class PolicyResourceService {
|
|||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response findById() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
if (policy == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -137,7 +143,9 @@ public class PolicyResourceService {
|
|||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response getDependentPolicies() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
if (policy == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -161,7 +169,9 @@ public class PolicyResourceService {
|
|||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response getScopes() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
if (policy == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -182,7 +192,9 @@ public class PolicyResourceService {
|
|||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response getResources() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
if (policy == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -203,7 +215,9 @@ public class PolicyResourceService {
|
|||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response getAssociatedPolicies() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
if (policy == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
|
@ -43,10 +45,14 @@ import org.jboss.resteasy.annotations.cache.NoCache;
|
|||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
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.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
|
@ -103,7 +109,9 @@ public class PolicyService {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Response create(@Context UriInfo uriInfo, String payload) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
}
|
||||
|
||||
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
|
||||
Policy policy = create(representation);
|
||||
|
@ -143,7 +151,10 @@ public class PolicyService {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Response findByName(@QueryParam("name") String name) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
|
||||
if (name == null) {
|
||||
|
@ -168,9 +179,12 @@ public class PolicyService {
|
|||
@QueryParam("resource") String resource,
|
||||
@QueryParam("scope") String scope,
|
||||
@QueryParam("permission") Boolean permission,
|
||||
@QueryParam("owner") String owner,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResult) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
Map<String, String[]> search = new HashMap<>();
|
||||
|
||||
|
@ -186,42 +200,56 @@ public class PolicyService {
|
|||
search.put("type", new String[] {type});
|
||||
}
|
||||
|
||||
if (owner != null && !"".equals(owner.trim())) {
|
||||
search.put("owner", new String[] {owner});
|
||||
}
|
||||
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
|
||||
if (resource != null || scope != null) {
|
||||
List<Policy> policies = new ArrayList<>();
|
||||
if (resource != null && !"".equals(resource.trim())) {
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
Resource resourceModel = resourceStore.findById(resource, resourceServer.getId());
|
||||
|
||||
if (resource != null && !"".equals(resource.trim())) {
|
||||
HashMap<String, String[]> resourceSearch = new HashMap<>();
|
||||
if (resourceModel == null) {
|
||||
Map<String, String[]> resourceFilters = new HashMap<>();
|
||||
|
||||
resourceSearch.put("name", new String[]{resource});
|
||||
resourceFilters.put("name", new String[]{resource});
|
||||
|
||||
storeFactory.getResourceStore().findByResourceServer(resourceSearch, resourceServer.getId(), -1, 1).forEach(resource1 -> {
|
||||
policies.addAll(policyStore.findByResource(resource1.getId(), resourceServer.getId()));
|
||||
if (resource1.getType() != null) {
|
||||
policies.addAll(policyStore.findByResourceType(resource1.getType(), resourceServer.getId()));
|
||||
}
|
||||
});
|
||||
if (owner != null) {
|
||||
resourceFilters.put("owner", new String[]{owner});
|
||||
}
|
||||
|
||||
Set<String> resources = resourceStore.findByResourceServer(resourceFilters, resourceServer.getId(), -1, 1).stream().map(Resource::getId).collect(Collectors.toSet());
|
||||
|
||||
if (resources.isEmpty()) {
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
search.put("resource", resources.toArray(new String[resources.size()]));
|
||||
} else {
|
||||
search.put("resource", new String[] {resourceModel.getId()});
|
||||
}
|
||||
}
|
||||
|
||||
if (scope != null && !"".equals(scope.trim())) {
|
||||
HashMap<String, String[]> scopeSearch = new HashMap<>();
|
||||
if (scope != null && !"".equals(scope.trim())) {
|
||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||
Scope scopeModel = scopeStore.findById(scope, resourceServer.getId());
|
||||
|
||||
scopeSearch.put("name", new String[]{scope});
|
||||
if (scopeModel == null) {
|
||||
Map<String, String[]> scopeFilters = new HashMap<>();
|
||||
|
||||
storeFactory.getScopeStore().findByResourceServer(scopeSearch, resourceServer.getId(), -1, 1).forEach(scope1 -> {
|
||||
policies.addAll(policyStore.findByScopeIds(Arrays.asList(scope1.getId()), resourceServer.getId()));
|
||||
});
|
||||
scopeFilters.put("name", new String[]{scope});
|
||||
|
||||
Set<String> scopes = scopeStore.findByResourceServer(scopeFilters, resourceServer.getId(), -1, 1).stream().map(Scope::getId).collect(Collectors.toSet());
|
||||
|
||||
if (scopes.isEmpty()) {
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
search.put("scope", scopes.toArray(new String[scopes.size()]));
|
||||
} else {
|
||||
search.put("scope", new String[] {scopeModel.getId()});
|
||||
}
|
||||
|
||||
if (policies.isEmpty()) {
|
||||
return Response.ok(Collections.emptyList()).build();
|
||||
}
|
||||
|
||||
new ArrayList<>(policies).forEach(policy -> findAssociatedPolicies(policy, policies));
|
||||
|
||||
search.put("id", policies.stream().map(Policy::getId).toArray(String[]::new));
|
||||
}
|
||||
|
||||
if (permission != null) {
|
||||
|
@ -249,7 +277,10 @@ public class PolicyService {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Response findPolicyProviders() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
return Response.ok(
|
||||
authorization.getProviderFactories().stream()
|
||||
.filter(factory -> !factory.isInternal())
|
||||
|
@ -268,7 +299,10 @@ public class PolicyService {
|
|||
|
||||
@Path("evaluate")
|
||||
public PolicyEvaluationService getPolicyEvaluateResource() {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
if (auth != null) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
PolicyEvaluationService resource = new PolicyEvaluationService(this.resourceServer, this.authorization, this.auth);
|
||||
|
||||
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||
|
|
|
@ -41,7 +41,7 @@ public class PolicyTypeService extends PolicyService {
|
|||
|
||||
private final String type;
|
||||
|
||||
PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
|
||||
public PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
|
||||
super(resourceServer, authorization, auth, adminEvent);
|
||||
this.type = type;
|
||||
}
|
||||
|
|
|
@ -19,12 +19,15 @@ package org.keycloak.authorization.admin.representation;
|
|||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.Decision;
|
||||
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.policy.evaluation.Result;
|
||||
import org.keycloak.authorization.util.Permissions;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.DecisionEffect;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
|
||||
|
@ -169,33 +172,64 @@ public class PolicyEvaluationResponseBuilder {
|
|||
return response;
|
||||
}
|
||||
|
||||
private static PolicyEvaluationResponse.PolicyResultRepresentation toRepresentation(Result.PolicyResult policy, AuthorizationProvider authorization) {
|
||||
private static PolicyEvaluationResponse.PolicyResultRepresentation toRepresentation(Result.PolicyResult result, AuthorizationProvider authorization) {
|
||||
PolicyEvaluationResponse.PolicyResultRepresentation policyResultRep = new PolicyEvaluationResponse.PolicyResultRepresentation();
|
||||
|
||||
PolicyRepresentation representation = new PolicyRepresentation();
|
||||
Policy policy = result.getPolicy();
|
||||
|
||||
representation.setId(policy.getPolicy().getId());
|
||||
representation.setName(policy.getPolicy().getName());
|
||||
representation.setType(policy.getPolicy().getType());
|
||||
representation.setDecisionStrategy(policy.getPolicy().getDecisionStrategy());
|
||||
representation.setId(policy.getId());
|
||||
representation.setName(policy.getName());
|
||||
representation.setType(policy.getType());
|
||||
representation.setDecisionStrategy(policy.getDecisionStrategy());
|
||||
representation.setDescription(policy.getDescription());
|
||||
|
||||
representation.setResources(policy.getPolicy().getResources().stream().map(resource -> resource.getName()).collect(Collectors.toSet()));
|
||||
if ("uma".equals(representation.getType())) {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
|
||||
Set<String> scopeNames = policy.getPolicy().getScopes().stream().map(scope -> scope.getName()).collect(Collectors.toSet());
|
||||
filters.put(PermissionTicket.POLICY, policy.getId());
|
||||
|
||||
List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, policy.getResourceServer().getId(), -1, 1);
|
||||
|
||||
if (!tickets.isEmpty()) {
|
||||
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
||||
PermissionTicket ticket = tickets.get(0);
|
||||
UserModel owner = keycloakSession.users().getUserById(ticket.getOwner(), authorization.getRealm());
|
||||
UserModel requester = keycloakSession.users().getUserById(ticket.getRequester(), authorization.getRealm());
|
||||
|
||||
representation.setDescription("Resource owner (" + getUserEmailOrUserName(owner) + ") grants access to " + getUserEmailOrUserName(requester));
|
||||
} else {
|
||||
String description = representation.getDescription();
|
||||
|
||||
if (description != null) {
|
||||
representation.setDescription(description + " (User-Managed Policy)");
|
||||
} else {
|
||||
representation.setDescription("User-Managed Policy");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
representation.setResources(policy.getResources().stream().map(resource -> resource.getName()).collect(Collectors.toSet()));
|
||||
|
||||
Set<String> scopeNames = policy.getScopes().stream().map(scope -> scope.getName()).collect(Collectors.toSet());
|
||||
|
||||
representation.setScopes(scopeNames);
|
||||
|
||||
policyResultRep.setPolicy(representation);
|
||||
|
||||
if (policy.getStatus() == Decision.Effect.DENY) {
|
||||
if (result.getStatus() == Decision.Effect.DENY) {
|
||||
policyResultRep.setStatus(DecisionEffect.DENY);
|
||||
policyResultRep.setScopes(representation.getScopes());
|
||||
} else {
|
||||
policyResultRep.setStatus(DecisionEffect.PERMIT);
|
||||
}
|
||||
|
||||
policyResultRep.setAssociatedPolicies(policy.getAssociatedPolicies().stream().map(result -> toRepresentation(result, authorization)).collect(Collectors.toList()));
|
||||
policyResultRep.setAssociatedPolicies(result.getAssociatedPolicies().stream().map(policy1 -> toRepresentation(policy1, authorization)).collect(Collectors.toList()));
|
||||
|
||||
return policyResultRep;
|
||||
}
|
||||
|
||||
private static String getUserEmailOrUserName(UserModel user) {
|
||||
return (user.getEmail() != null ? user.getEmail() : user.getUsername());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ public class AuthorizationTokenService {
|
|||
try {
|
||||
Map claims = JsonSerialization.readValue(Base64Url.decode(authorizationRequest.getClaimToken()), Map.class);
|
||||
authorizationRequest.setClaims(claims);
|
||||
return new KeycloakEvaluationContext(new KeycloakIdentity(authorization.getKeycloakSession(), Tokens.getAccessToken(authorizationRequest.getAccessToken(), authorization.getKeycloakSession())), claims, authorization.getKeycloakSession());
|
||||
return new KeycloakEvaluationContext(new KeycloakIdentity(authorization.getKeycloakSession(), Tokens.getAccessToken(authorizationRequest.getSubjectToken(), authorization.getKeycloakSession())), claims, authorization.getKeycloakSession());
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to map claims from claim token [" + claimToken + "]", cause);
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ public class AuthorizationTokenService {
|
|||
try {
|
||||
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
||||
RealmModel realm = authorization.getRealm();
|
||||
String accessToken = authorizationRequest.getAccessToken();
|
||||
String accessToken = authorizationRequest.getSubjectToken();
|
||||
|
||||
if (accessToken == null) {
|
||||
throw new RuntimeException("Claim token can not be null and must be a valid IDToken");
|
||||
|
@ -161,7 +161,7 @@ public class AuthorizationTokenService {
|
|||
List<Result> evaluation;
|
||||
|
||||
if (ticket.getResources().isEmpty() && request.getRpt() == null) {
|
||||
evaluation = evaluateAllPermissions(resourceServer, evaluationContext, identity);
|
||||
evaluation = evaluateAllPermissions(request, resourceServer, evaluationContext, identity);
|
||||
} else if(!request.getPermissions().getResources().isEmpty()) {
|
||||
evaluation = evaluatePermissions(request, ticket, resourceServer, evaluationContext, identity);
|
||||
} else {
|
||||
|
@ -212,9 +212,9 @@ public class AuthorizationTokenService {
|
|||
.evaluate(new PermissionTicketAwareDecisionResultCollector(request, ticket, identity, resourceServer, authorization)).results();
|
||||
}
|
||||
|
||||
private List<Result> evaluateAllPermissions(ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
|
||||
private List<Result> evaluateAllPermissions(AuthorizationRequest request, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
|
||||
return authorization.evaluators()
|
||||
.from(Permissions.all(resourceServer, identity, authorization), evaluationContext)
|
||||
.from(Permissions.all(resourceServer, identity, authorization, request), evaluationContext)
|
||||
.evaluate();
|
||||
}
|
||||
|
||||
|
@ -235,7 +235,6 @@ public class AuthorizationTokenService {
|
|||
Authorization authorization = new Authorization();
|
||||
|
||||
authorization.setPermissions(entitlements);
|
||||
authorization.setClaims(request.getClaims());
|
||||
|
||||
rpt.setAuthorization(authorization);
|
||||
|
||||
|
@ -309,8 +308,9 @@ public class AuthorizationTokenService {
|
|||
|
||||
private List<ResourcePermission> createPermissions(PermissionTicketToken ticket, AuthorizationRequest request, ResourceServer resourceServer, KeycloakIdentity identity, AuthorizationProvider authorization) {
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Map<String, Set<String>> permissionsToEvaluate = new LinkedHashMap<>();
|
||||
Map<String, ResourcePermission> permissionsToEvaluate = new LinkedHashMap<>();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||
Metadata metadata = request.getMetadata();
|
||||
Integer limit = metadata != null ? metadata.getLimit() : null;
|
||||
|
||||
|
@ -359,31 +359,37 @@ public class AuthorizationTokenService {
|
|||
requestedScopes.addAll(Arrays.asList(clientAdditionalScopes.split(" ")));
|
||||
}
|
||||
|
||||
List<Scope> requestedScopesModel = requestedScopes.stream().map(s -> scopeStore.findByName(s, resourceServer.getId())).collect(Collectors.toList());
|
||||
|
||||
if (!existingResources.isEmpty()) {
|
||||
for (Resource resource : existingResources) {
|
||||
Set<String> scopes = permissionsToEvaluate.get(resource.getId());
|
||||
ResourcePermission permission = permissionsToEvaluate.get(resource.getId());
|
||||
|
||||
if (scopes == null) {
|
||||
scopes = new HashSet<>();
|
||||
permissionsToEvaluate.put(resource.getId(), scopes);
|
||||
if (permission == null) {
|
||||
permission = Permissions.createResourcePermissions(resource, requestedScopes, authorization, request);
|
||||
permissionsToEvaluate.put(resource.getId(), permission);
|
||||
if (limit != null) {
|
||||
limit--;
|
||||
}
|
||||
} else {
|
||||
for (Scope scope : requestedScopesModel) {
|
||||
if (!permission.getScopes().contains(scope)) {
|
||||
permission.getScopes().add(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scopes.addAll(requestedScopes);
|
||||
}
|
||||
} else {
|
||||
List<Resource> resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), ticket.getAudience()[0]);
|
||||
List<Resource> resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), resourceServer.getId());
|
||||
|
||||
for (Resource resource : resources) {
|
||||
permissionsToEvaluate.put(resource.getId(), requestedScopes);
|
||||
permissionsToEvaluate.put(resource.getId(), Permissions.createResourcePermissions(resource, requestedScopes, authorization, request));
|
||||
if (limit != null) {
|
||||
limit--;
|
||||
}
|
||||
}
|
||||
|
||||
permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", requestedScopes);
|
||||
permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", new ResourcePermission(null, requestedScopesModel, resourceServer, request.getClaims()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,28 +415,42 @@ public class AuthorizationTokenService {
|
|||
List<Permission> permissions = authorizationData.getPermissions();
|
||||
|
||||
if (permissions != null) {
|
||||
for (Permission permission : permissions) {
|
||||
for (Permission grantedPermission : permissions) {
|
||||
if (limit != null && limit <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
Resource resourcePermission = resourceStore.findById(permission.getResourceId(), ticket.getAudience()[0]);
|
||||
Resource resourcePermission = resourceStore.findById(grantedPermission.getResourceId(), ticket.getAudience()[0]);
|
||||
|
||||
if (resourcePermission != null) {
|
||||
Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
|
||||
ResourcePermission permission = permissionsToEvaluate.get(resourcePermission.getId());
|
||||
|
||||
if (scopes == null) {
|
||||
scopes = new HashSet<>();
|
||||
permissionsToEvaluate.put(resourcePermission.getId(), scopes);
|
||||
if (permission == null) {
|
||||
permission = new ResourcePermission(resourcePermission, new ArrayList<>(), resourceServer, grantedPermission.getClaims());
|
||||
permissionsToEvaluate.put(resourcePermission.getId(), permission);
|
||||
if (limit != null) {
|
||||
limit--;
|
||||
}
|
||||
} else {
|
||||
if (grantedPermission.getClaims() != null) {
|
||||
for (Entry<String, Set<String>> entry : grantedPermission.getClaims().entrySet()) {
|
||||
Set<String> claims = permission.getClaims().get(entry.getKey());
|
||||
|
||||
if (claims != null) {
|
||||
claims.addAll(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> scopePermission = permission.getScopes();
|
||||
for (String scopeName : grantedPermission.getScopes()) {
|
||||
Scope scope = scopeStore.findByName(scopeName, resourceServer.getId());
|
||||
|
||||
if (scopePermission != null) {
|
||||
scopes.addAll(scopePermission);
|
||||
if (scope != null) {
|
||||
if (!permission.getScopes().contains(scope)) {
|
||||
permission.getScopes().add(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -439,19 +459,7 @@ public class AuthorizationTokenService {
|
|||
}
|
||||
}
|
||||
|
||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||
|
||||
return permissionsToEvaluate.entrySet().stream()
|
||||
.flatMap((Function<Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
|
||||
String key = entry.getKey();
|
||||
if ("$KC_SCOPE_PERMISSION".equals(key)) {
|
||||
List<Scope> scopes = entry.getValue().stream().map(scopeName -> scopeStore.findByName(scopeName, resourceServer.getId())).filter(scope -> Objects.nonNull(scope)).collect(Collectors.toList());
|
||||
return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
|
||||
} else {
|
||||
Resource entryResource = resourceStore.findById(key, resourceServer.getId());
|
||||
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
return new ArrayList<>(permissionsToEvaluate.values());
|
||||
}
|
||||
|
||||
private PermissionTicketToken verifyPermissionTicket(AuthorizationRequest request) {
|
||||
|
|
|
@ -61,6 +61,7 @@ public class UmaConfiguration extends OIDCConfigurationRepresentation {
|
|||
|
||||
configuration.setPermissionEndpoint(uriBuilder.clone().path(RealmsResource.class).path(RealmsResource.class, "getAuthorizationService").path(AuthorizationService.class, "getProtectionService").path(ProtectionService.class, "permission").build(realm.getName()).toString());
|
||||
configuration.setResourceRegistrationEndpoint(uriBuilder.clone().path(RealmsResource.class).path(RealmsResource.class, "getAuthorizationService").path(AuthorizationService.class, "getProtectionService").path(ProtectionService.class, "resource").build(realm.getName()).toString());
|
||||
configuration.setPolicyEndpoint(uriBuilder.clone().path(RealmsResource.class).path(RealmsResource.class, "getAuthorizationService").path(AuthorizationService.class, "getProtectionService").path(ProtectionService.class, "policy").build(realm.getName()).toString());
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
@ -71,6 +72,9 @@ public class UmaConfiguration extends OIDCConfigurationRepresentation {
|
|||
@JsonProperty("permission_endpoint")
|
||||
private String permissionEndpoint;
|
||||
|
||||
@JsonProperty("policy_endpoint")
|
||||
private String policyEndpoint;
|
||||
|
||||
public String getResourceRegistrationEndpoint() {
|
||||
return this.resourceRegistrationEndpoint;
|
||||
}
|
||||
|
@ -86,4 +90,12 @@ public class UmaConfiguration extends OIDCConfigurationRepresentation {
|
|||
void setPermissionEndpoint(String permissionEndpoint) {
|
||||
this.permissionEndpoint = permissionEndpoint;
|
||||
}
|
||||
|
||||
public String getPolicyEndpoint() {
|
||||
return this.policyEndpoint;
|
||||
}
|
||||
|
||||
void setPolicyEndpoint(String policyEndpoint) {
|
||||
this.policyEndpoint = policyEndpoint;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import javax.ws.rs.Path;
|
|||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import org.keycloak.authorization.protection.permission.PermissionTicketService;
|
||||
import org.keycloak.authorization.protection.policy.UserManagedPermissionService;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -57,12 +58,7 @@ public class ProtectionService {
|
|||
public Object resource() {
|
||||
KeycloakIdentity identity = createIdentity(true);
|
||||
ResourceServer resourceServer = getResourceServer(identity);
|
||||
RealmModel realm = authorization.getRealm();
|
||||
ClientModel client = realm.getClientById(resourceServer.getId());
|
||||
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
||||
UserModel serviceAccount = keycloakSession.users().getServiceAccount(client);
|
||||
AdminEventBuilder adminEvent = new AdminEventBuilder(realm, new AdminAuth(realm, identity.getAccessToken(), serviceAccount, client), keycloakSession, clientConnection);
|
||||
ResourceSetService resourceManager = new ResourceSetService(resourceServer, this.authorization, null, adminEvent.realm(realm).authClient(client).authUser(serviceAccount));
|
||||
ResourceSetService resourceManager = new ResourceSetService(resourceServer, this.authorization, null, createAdminEventBuilder(identity, resourceServer));
|
||||
|
||||
ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
|
||||
|
||||
|
@ -73,6 +69,15 @@ public class ProtectionService {
|
|||
return resource;
|
||||
}
|
||||
|
||||
private AdminEventBuilder createAdminEventBuilder(KeycloakIdentity identity, ResourceServer resourceServer) {
|
||||
RealmModel realm = authorization.getRealm();
|
||||
ClientModel client = realm.getClientById(resourceServer.getId());
|
||||
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
||||
UserModel serviceAccount = keycloakSession.users().getServiceAccount(client);
|
||||
AdminEventBuilder adminEvent = new AdminEventBuilder(realm, new AdminAuth(realm, identity.getAccessToken(), serviceAccount, client), keycloakSession, clientConnection);
|
||||
return adminEvent.realm(realm).authClient(client).authUser(serviceAccount);
|
||||
}
|
||||
|
||||
@Path("/permission")
|
||||
public Object permission() {
|
||||
KeycloakIdentity identity = createIdentity(false);
|
||||
|
@ -95,6 +100,17 @@ public class ProtectionService {
|
|||
return resource;
|
||||
}
|
||||
|
||||
@Path("/uma-policy")
|
||||
public Object policy() {
|
||||
KeycloakIdentity identity = createIdentity(false);
|
||||
|
||||
UserManagedPermissionService resource = new UserManagedPermissionService(identity, getResourceServer(identity), this.authorization, createAdminEventBuilder(identity, getResourceServer(identity)));
|
||||
|
||||
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
private KeycloakIdentity createIdentity(boolean checkProtectionScope) {
|
||||
KeycloakIdentity identity = new KeycloakIdentity(this.authorization.getKeycloakSession());
|
||||
ResourceServer resourceServer = getResourceServer(identity);
|
||||
|
|
|
@ -125,6 +125,8 @@ public class PermissionTicketService {
|
|||
throw new ErrorResponseException("invalid_permission", "Permission already exists", Response.Status.BAD_REQUEST);
|
||||
|
||||
PermissionTicket ticket = ticketStore.create(resource.getId(), scope.getId(), user.getId(), resourceServer);
|
||||
if(representation.isGranted())
|
||||
ticket.setGrantedTimestamp(java.lang.System.currentTimeMillis());
|
||||
representation = ModelToRepresentation.toRepresentation(ticket, authorization);
|
||||
return Response.ok(representation).build();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2016 Red Hat, Inc., and individual 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.protection.policy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
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.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.admin.PermissionService;
|
||||
import org.keycloak.authorization.admin.PolicyTypeResourceService;
|
||||
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||
import org.keycloak.authorization.identity.Identity;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:federico@martel-innovate.com">Federico M. Facca</a>
|
||||
*/
|
||||
public class UserManagedPermissionService {
|
||||
|
||||
private final ResourceServer resourceServer;
|
||||
private final Identity identity;
|
||||
private final AuthorizationProvider authorization;
|
||||
private final PermissionService delegate;
|
||||
|
||||
public UserManagedPermissionService(KeycloakIdentity identity, ResourceServer resourceServer, AuthorizationProvider authorization, AdminEventBuilder eventBuilder) {
|
||||
this.identity = identity;
|
||||
this.resourceServer = resourceServer;
|
||||
this.authorization = authorization;
|
||||
delegate = new PermissionService(resourceServer, authorization, null, eventBuilder);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(delegate);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{resourceId}")
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
public Response create(@Context UriInfo uriInfo, @PathParam("resourceId") String resourceId, UmaPermissionRepresentation representation) {
|
||||
if (representation.getId() != null) {
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Newly created uma policies should not have an id", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
checkRequest(resourceId, representation);
|
||||
|
||||
representation.addResource(resourceId);
|
||||
representation.setOwner(identity.getId());
|
||||
|
||||
return findById(delegate.create(representation).getId());
|
||||
}
|
||||
|
||||
@Path("{policyId}")
|
||||
@PUT
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
public Response update(@Context UriInfo uriInfo, @PathParam("policyId") String policyId, String payload) {
|
||||
UmaPermissionRepresentation representation;
|
||||
|
||||
try {
|
||||
representation = JsonSerialization.readValue(payload, UmaPermissionRepresentation.class);
|
||||
} catch (IOException e) {
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Failed to parse representation", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
checkRequest(getAssociatedResourceId(policyId), representation);
|
||||
|
||||
return PolicyTypeResourceService.class.cast(delegate.getResource(policyId)).update(uriInfo, payload);
|
||||
}
|
||||
|
||||
@Path("{policyId}")
|
||||
@DELETE
|
||||
public Response delete(@Context UriInfo uriInfo, @PathParam("policyId") String policyId) {
|
||||
checkRequest(getAssociatedResourceId(policyId), null);
|
||||
PolicyTypeResourceService.class.cast(delegate.getResource(policyId)).delete(uriInfo);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Path("{policyId}")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
public Response findById(@PathParam("policyId") String policyId) {
|
||||
checkRequest(getAssociatedResourceId(policyId), null);
|
||||
return PolicyTypeResourceService.class.cast(delegate.getResource(policyId)).findById();
|
||||
}
|
||||
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response find(@QueryParam("name") String name,
|
||||
@QueryParam("resource") String resource,
|
||||
@QueryParam("scope") String scope,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResult) {
|
||||
return delegate.findAll(null, name, "uma", resource, scope, true, identity.getId(), firstResult, maxResult);
|
||||
}
|
||||
|
||||
private Policy getPolicy(@PathParam("policyId") String policyId) {
|
||||
Policy existing = authorization.getStoreFactory().getPolicyStore().findById(policyId, resourceServer.getId());
|
||||
|
||||
if (existing == null) {
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Policy with [" + policyId + "] does not exist", Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
private void checkRequest(String resourceId, UmaPermissionRepresentation representation) {
|
||||
ResourceStore resourceStore = this.authorization.getStoreFactory().getResourceStore();
|
||||
Resource resource = resourceStore.findById(resourceId, resourceServer.getId());
|
||||
|
||||
if (resource == null) {
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Resource [" + resourceId + "] cannot be found", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (!resource.getOwner().equals(identity.getId())) {
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Only resource onwer can access policies for resource [" + resourceId + "]", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (!resource.isOwnerManagedAccess()) {
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Only resources with owner managed accessed can have policies", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (!resourceServer.isAllowRemoteResourceManagement()) {
|
||||
throw new ErrorResponseException(OAuthErrorException.REQUEST_NOT_SUPPORTED, "Remote Resource Management not enabled on resource server [" + resourceServer.getId() + "]", Status.FORBIDDEN);
|
||||
}
|
||||
|
||||
if (representation != null) {
|
||||
Set<String> resourceScopes = resource.getScopes().stream().map(scope -> scope.getName()).collect(Collectors.toSet());
|
||||
Set<String> scopes = representation.getScopes();
|
||||
|
||||
if (scopes == null || scopes.isEmpty()) {
|
||||
scopes = resourceScopes;
|
||||
representation.setScopes(scopes);
|
||||
}
|
||||
|
||||
if (!resourceScopes.containsAll(scopes)) {
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Some of the scopes [" + scopes + "] are not valid for resource [" + resourceId + "]", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getAssociatedResourceId(String policyId) {
|
||||
return getPolicy(policyId).getResources().iterator().next().getId();
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ import org.keycloak.authorization.policy.evaluation.Result;
|
|||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
|
@ -68,23 +69,23 @@ public final class Permissions {
|
|||
* @param authorization
|
||||
* @return
|
||||
*/
|
||||
public static List<ResourcePermission> all(ResourceServer resourceServer, Identity identity, AuthorizationProvider authorization) {
|
||||
public static List<ResourcePermission> all(ResourceServer resourceServer, Identity identity, AuthorizationProvider authorization, AuthorizationRequest request) {
|
||||
List<ResourcePermission> permissions = new ArrayList<>();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
|
||||
// obtain all resources where owner is the resource server
|
||||
resourceStore.findByOwner(resourceServer.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization)));
|
||||
resourceStore.findByOwner(resourceServer.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization, request)));
|
||||
|
||||
// obtain all resources where owner is the current user
|
||||
resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization)));
|
||||
resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization, request)));
|
||||
|
||||
// obtain all resources granted to the user via permission tickets (uma)
|
||||
List<PermissionTicket> tickets = storeFactory.getPermissionTicketStore().findGranted(identity.getId(), resourceServer.getId());
|
||||
Map<String, ResourcePermission> userManagedPermissions = new HashMap<>();
|
||||
|
||||
for (PermissionTicket ticket : tickets) {
|
||||
userManagedPermissions.computeIfAbsent(ticket.getResource().getId(), id -> new ResourcePermission(ticket.getResource(), new ArrayList<>(), resourceServer));
|
||||
userManagedPermissions.computeIfAbsent(ticket.getResource().getId(), id -> new ResourcePermission(ticket.getResource(), new ArrayList<>(), resourceServer, request.getClaims()));
|
||||
}
|
||||
|
||||
permissions.addAll(userManagedPermissions.values());
|
||||
|
@ -92,8 +93,7 @@ public final class Permissions {
|
|||
return permissions;
|
||||
}
|
||||
|
||||
public static List<ResourcePermission> createResourcePermissions(Resource resource, Set<String> requestedScopes, AuthorizationProvider authorization) {
|
||||
List<ResourcePermission> permissions = new ArrayList<>();
|
||||
public static ResourcePermission createResourcePermissions(Resource resource, Set<String> requestedScopes, AuthorizationProvider authorization, AuthorizationRequest request) {
|
||||
String type = resource.getType();
|
||||
ResourceServer resourceServer = resource.getResourceServer();
|
||||
List<Scope> scopes;
|
||||
|
@ -127,12 +127,11 @@ public final class Permissions {
|
|||
return byName;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer()));
|
||||
|
||||
return permissions;
|
||||
return new ResourcePermission(resource, scopes, resource.getResourceServer(), request.getClaims());
|
||||
}
|
||||
|
||||
public static List<ResourcePermission> createResourcePermissionsWithScopes(Resource resource, List<Scope> scopes, AuthorizationProvider authorization) {
|
||||
public static List<ResourcePermission> createResourcePermissionsWithScopes(Resource resource, List<Scope> scopes, AuthorizationProvider authorization, AuthorizationRequest request) {
|
||||
List<ResourcePermission> permissions = new ArrayList<>();
|
||||
String type = resource.getType();
|
||||
ResourceServer resourceServer = resource.getResourceServer();
|
||||
|
@ -153,7 +152,7 @@ public final class Permissions {
|
|||
});
|
||||
}
|
||||
|
||||
permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer()));
|
||||
permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer(), request.getClaims()));
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
|
|
@ -320,7 +320,6 @@ public class ExportUtils {
|
|||
} else {
|
||||
rep.getOwner().setId(null);
|
||||
}
|
||||
rep.setId(null);
|
||||
rep.getScopes().forEach(scopeRepresentation -> {
|
||||
scopeRepresentation.setId(null);
|
||||
scopeRepresentation.setIconUri(null);
|
||||
|
@ -335,10 +334,10 @@ public class ExportUtils {
|
|||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
|
||||
policies.addAll(policyStore.findByResourceServer(settingsModel.getId())
|
||||
.stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope"))
|
||||
.stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope") && policy.getOwner() == null)
|
||||
.map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList()));
|
||||
policies.addAll(policyStore.findByResourceServer(settingsModel.getId())
|
||||
.stream().filter(policy -> policy.getType().equals("resource") || policy.getType().equals("scope"))
|
||||
.stream().filter(policy -> (policy.getType().equals("resource") || policy.getType().equals("scope") && policy.getOwner() == null))
|
||||
.map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList()));
|
||||
|
||||
representation.setPolicies(policies);
|
||||
|
@ -346,7 +345,6 @@ public class ExportUtils {
|
|||
List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel.getId()).stream().map(scope -> {
|
||||
ScopeRepresentation rep = toRepresentation(scope);
|
||||
|
||||
rep.setId(null);
|
||||
rep.setPolicies(null);
|
||||
rep.setResources(null);
|
||||
|
||||
|
@ -362,8 +360,6 @@ public class ExportUtils {
|
|||
try {
|
||||
PolicyRepresentation rep = toRepresentation(policy, authorizationProvider, true, true);
|
||||
|
||||
rep.setId(null);
|
||||
|
||||
Map<String, String> config = new HashMap<>(rep.getConfig());
|
||||
|
||||
rep.setConfig(config);
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.forms.account.freemarker.model;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -30,6 +31,7 @@ import javax.ws.rs.core.UriInfo;
|
|||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
|
@ -260,7 +262,7 @@ public class AuthorizationBean {
|
|||
if (shares == null) {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.RESOURCE, this.resource.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
|
||||
shares = toPermissionRepresentation(findPermissions(filters));
|
||||
|
@ -269,6 +271,31 @@ public class AuthorizationBean {
|
|||
return shares;
|
||||
}
|
||||
|
||||
public Collection<ManagedPermissionBean> getPolicies() {
|
||||
Map<String, String[]> filters = new HashMap<>();
|
||||
|
||||
filters.put("type", new String[] {"uma"});
|
||||
filters.put("resource", new String[] {this.resource.getId()});
|
||||
filters.put("owner", new String[] {getOwner().getId()});
|
||||
|
||||
List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findByResourceServer(filters, getResourceServer().getId(), -1, -1);
|
||||
|
||||
if (policies.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return policies.stream()
|
||||
.filter(policy -> {
|
||||
Map<String, String> filters1 = new HashMap<>();
|
||||
|
||||
filters1.put(PermissionTicket.POLICY, policy.getId());
|
||||
|
||||
return authorization.getStoreFactory().getPermissionTicketStore().find(filters1, resourceServer.getId(), -1, 1)
|
||||
.isEmpty();
|
||||
})
|
||||
.map(ManagedPermissionBean::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ResourceServerBean getResourceServer() {
|
||||
return resourceServer;
|
||||
}
|
||||
|
@ -326,6 +353,10 @@ public class AuthorizationBean {
|
|||
this.clientModel = clientModel;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return clientModel.getId();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
String name = clientModel.getName();
|
||||
|
||||
|
@ -336,6 +367,10 @@ public class AuthorizationBean {
|
|||
return clientModel.getClientId();
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientModel.getClientId();
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
Set<String> redirectUris = clientModel.getRedirectUris();
|
||||
|
||||
|
@ -346,4 +381,34 @@ public class AuthorizationBean {
|
|||
return redirectUris.iterator().next();
|
||||
}
|
||||
}
|
||||
|
||||
public class ManagedPermissionBean {
|
||||
|
||||
private final Policy policy;
|
||||
private List<ManagedPermissionBean> policies;
|
||||
|
||||
public ManagedPermissionBean(Policy policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return policy.getId();
|
||||
}
|
||||
|
||||
public Collection<ScopeRepresentation> getScopes() {
|
||||
return policy.getScopes().stream().map(ModelToRepresentation::toRepresentation).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.policy.getDescription();
|
||||
}
|
||||
|
||||
public Collection<ManagedPermissionBean> getPolicies() {
|
||||
if (this.policies == null) {
|
||||
this.policies = policy.getAssociatedPolicies().stream().map(ManagedPermissionBean::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return this.policies;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1069,7 +1069,7 @@ public class TokenEndpoint {
|
|||
authorizationRequest.setRpt(formParams.getFirst("rpt"));
|
||||
authorizationRequest.setScope(formParams.getFirst("scope"));
|
||||
authorizationRequest.setAudience(formParams.getFirst("audience"));
|
||||
authorizationRequest.setAccessToken(accessTokenString);
|
||||
authorizationRequest.setSubjectToken(formParams.getFirst("subject_token") != null ? formParams.getFirst("subject_token") : accessTokenString);
|
||||
|
||||
String submitRequest = formParams.getFirst("submit_request");
|
||||
|
||||
|
|
|
@ -20,9 +20,11 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.authentication.RequiredActionContext;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
import org.keycloak.common.util.Base64Url;
|
||||
|
@ -726,45 +728,90 @@ public class AccountFormService extends AbstractSecuredLocalService {
|
|||
boolean isGrant = "grant".equals(action);
|
||||
boolean isDeny = "deny".equals(action);
|
||||
boolean isRevoke = "revoke".equals(action);
|
||||
boolean isRevokePolicy = "revokePolicy".equals(action);
|
||||
boolean isRevokePolicyAll = "revokePolicyAll".equals(action);
|
||||
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
if (isRevokePolicy || isRevokePolicyAll) {
|
||||
List<String> ids = new ArrayList(Arrays.asList(permissionId));
|
||||
Iterator<String> iterator = ids.iterator();
|
||||
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
|
||||
Policy policy = null;
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.REQUESTER, session.users().getUserByUsername(requester, realm).getId());
|
||||
while (iterator.hasNext()) {
|
||||
String id = iterator.next();
|
||||
|
||||
if (isRevoke) {
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
} else {
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer().getId(), -1, -1);
|
||||
Iterator<PermissionTicket> iterator = tickets.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
PermissionTicket ticket = iterator.next();
|
||||
|
||||
if (isGrant) {
|
||||
if (permissionId != null && permissionId.length > 0 && !Arrays.asList(permissionId).contains(ticket.getId())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isGrant && !ticket.isGranted()) {
|
||||
ticket.setGrantedTimestamp(System.currentTimeMillis());
|
||||
iterator.remove();
|
||||
} else if (isDeny || isRevoke) {
|
||||
if (permissionId != null && permissionId.length > 0 && Arrays.asList(permissionId).contains(ticket.getId())) {
|
||||
if (!id.contains(":")) {
|
||||
policy = policyStore.findById(id, client.getId());
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Set<Scope> scopesToKeep = new HashSet<>();
|
||||
|
||||
if (isRevokePolicyAll) {
|
||||
for (Scope scope : policy.getScopes()) {
|
||||
policy.removeScope(scope);
|
||||
}
|
||||
} else {
|
||||
for (String id : ids) {
|
||||
scopesToKeep.add(authorization.getStoreFactory().getScopeStore().findById(id.split(":")[1], client.getId()));
|
||||
}
|
||||
|
||||
for (Scope scope : policy.getScopes()) {
|
||||
if (!scopesToKeep.contains(scope)) {
|
||||
policy.removeScope(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (policy.getScopes().isEmpty()) {
|
||||
for (Policy associated : policy.getAssociatedPolicies()) {
|
||||
policyStore.delete(associated.getId());
|
||||
}
|
||||
|
||||
policyStore.delete(policy.getId());
|
||||
}
|
||||
} else {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.REQUESTER, session.users().getUserByUsername(requester, realm).getId());
|
||||
|
||||
if (isRevoke) {
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
} else {
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer().getId(), -1, -1);
|
||||
Iterator<PermissionTicket> iterator = tickets.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
PermissionTicket ticket = iterator.next();
|
||||
|
||||
if (isGrant) {
|
||||
if (permissionId != null && permissionId.length > 0 && !Arrays.asList(permissionId).contains(ticket.getId())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isGrant && !ticket.isGranted()) {
|
||||
ticket.setGrantedTimestamp(System.currentTimeMillis());
|
||||
iterator.remove();
|
||||
} else if (isDeny || isRevoke) {
|
||||
if (permissionId != null && permissionId.length > 0 && Arrays.asList(permissionId).contains(ticket.getId())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (PermissionTicket ticket : tickets) {
|
||||
ticketStore.delete(ticket.getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (PermissionTicket ticket : tickets) {
|
||||
ticketStore.delete(ticket.getId());
|
||||
}
|
||||
|
||||
if (isRevoke) {
|
||||
if (isRevoke || isRevokePolicy || isRevokePolicyAll) {
|
||||
return forwardToPage("resource-detail", AccountPages.RESOURCE_DETAIL);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public class TestPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public AbstractPolicyRepresentation toRepresentation(Policy policy) {
|
||||
public AbstractPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
|
||||
return new PolicyRepresentation();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.admin.client.authorization;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -38,6 +39,7 @@ import org.junit.BeforeClass;
|
|||
import org.junit.Test;
|
||||
import org.keycloak.AuthorizationContext;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
|
@ -59,12 +61,14 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationResponse;
|
||||
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.RoleBuilder;
|
||||
import org.keycloak.testsuite.util.RolesBuilder;
|
||||
|
@ -106,6 +110,10 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
.redirectUris("http://localhost/resource-server-test")
|
||||
.defaultRoles("uma_protection")
|
||||
.directAccessGrants())
|
||||
.client(ClientBuilder.create().clientId("public-client-test")
|
||||
.publicClient()
|
||||
.redirectUris("http://localhost:8180/auth/realms/master/app/auth/*")
|
||||
.directAccessGrants())
|
||||
.build());
|
||||
}
|
||||
|
||||
|
@ -125,7 +133,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
|
||||
headers.put("Authorization", Arrays.asList("Bearer " + token));
|
||||
|
||||
AuthorizationContext context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
AuthorizationContext context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
|
||||
assertFalse(context.isGranted());
|
||||
|
||||
AuthorizationRequest request = new AuthorizationRequest();
|
||||
|
@ -137,22 +145,22 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
|
||||
assertNotNull(token);
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
|
||||
assertTrue(context.isGranted());
|
||||
|
||||
parameters.put("withdrawal.amount", Arrays.asList("200"));
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
|
||||
assertFalse(context.isGranted());
|
||||
|
||||
parameters.put("withdrawal.amount", Arrays.asList("50"));
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
|
||||
assertTrue(context.isGranted());
|
||||
|
||||
parameters.put("withdrawal.amount", Arrays.asList("10"));
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
|
||||
|
||||
request = new AuthorizationRequest();
|
||||
|
||||
|
@ -161,8 +169,23 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
response = authzClient.authorization("marta", "password").authorize(request);
|
||||
token = response.getToken();
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
|
||||
assertTrue(context.isGranted());
|
||||
|
||||
request = new AuthorizationRequest();
|
||||
|
||||
request.setTicket(extractTicket(headers));
|
||||
|
||||
response = authzClient.authorization("marta", "password").authorize(request);
|
||||
token = response.getToken();
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "GET", token, headers, parameters));
|
||||
assertTrue(context.isGranted());
|
||||
|
||||
assertEquals(1, context.getPermissions().size());
|
||||
Permission permission = context.getPermissions().get(0);
|
||||
|
||||
assertEquals(parameters.get("withdrawal.amount").get(0), permission.getClaims().get("withdrawal.amount").iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -182,6 +205,52 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
|
||||
parameters.put("withdrawal.amount", Arrays.asList("50"));
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
assertTrue(context.isGranted());
|
||||
assertEquals(1, context.getPermissions().size());
|
||||
Permission permission = context.getPermissions().get(0);
|
||||
assertEquals(parameters.get("withdrawal.amount").get(0), permission.getClaims().get("withdrawal.amount").iterator().next());
|
||||
|
||||
parameters.put("withdrawal.amount", Arrays.asList("200"));
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
assertFalse(context.isGranted());
|
||||
|
||||
parameters.put("withdrawal.amount", Arrays.asList("50"));
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
assertTrue(context.isGranted());
|
||||
|
||||
parameters.put("withdrawal.amount", Arrays.asList("10"));
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
|
||||
assertTrue(context.isGranted());
|
||||
|
||||
assertEquals(1, context.getPermissions().size());
|
||||
permission = context.getPermissions().get(0);
|
||||
assertEquals(parameters.get("withdrawal.amount").get(0), permission.getClaims().get("withdrawal.amount").iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnforceEntitlementAccessWithClaimsWithBearerToken() {
|
||||
initAuthorizationSettings(getClientResource("resource-server-test"));
|
||||
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getAdapterConfiguration("enforcer-entitlement-claims-test.json"));
|
||||
PolicyEnforcer policyEnforcer = deployment.getPolicyEnforcer();
|
||||
HashMap<String, List<String>> headers = new HashMap<>();
|
||||
HashMap<String, List<String>> parameters = new HashMap<>();
|
||||
|
||||
AuthzClient authzClient = getAuthzClient("enforcer-entitlement-claims-test.json");
|
||||
String token = authzClient.obtainAccessToken("marta", "password").getToken();
|
||||
|
||||
headers.put("Authorization", Arrays.asList("Bearer " + token));
|
||||
|
||||
AuthorizationContext context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
assertFalse(context.isGranted());
|
||||
|
||||
parameters.put("withdrawal.amount", Arrays.asList("50"));
|
||||
|
||||
context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
|
||||
assertTrue(context.isGranted());
|
||||
|
||||
|
@ -203,7 +272,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEnforceEntitlementAccessWithClaimsWithBearerToken() {
|
||||
public void testEnforceEntitlementAccessWithClaimsWithBearerTokenFromPublicClient() {
|
||||
initAuthorizationSettings(getClientResource("resource-server-test"));
|
||||
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getAdapterConfiguration("enforcer-entitlement-claims-test.json"));
|
||||
|
@ -211,8 +280,13 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
HashMap<String, List<String>> headers = new HashMap<>();
|
||||
HashMap<String, List<String>> parameters = new HashMap<>();
|
||||
|
||||
AuthzClient authzClient = getAuthzClient("enforcer-entitlement-claims-test.json");
|
||||
String token = authzClient.obtainAccessToken("marta", "password").getToken();
|
||||
oauth.realm(REALM_NAME);
|
||||
oauth.clientId("public-client-test");
|
||||
oauth.doLogin("marta", "password");
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, null);
|
||||
String token = response.getAccessToken();
|
||||
|
||||
headers.put("Authorization", Arrays.asList("Bearer " + token));
|
||||
|
||||
|
@ -306,7 +380,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
return clients.get(representation.getId());
|
||||
}
|
||||
|
||||
private OIDCHttpFacade createHttpFacade(String path, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
|
||||
private OIDCHttpFacade createHttpFacade(String path, String method, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
|
||||
return new OIDCHttpFacade() {
|
||||
Request request;
|
||||
Response response;
|
||||
|
@ -325,7 +399,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
@Override
|
||||
public Request getRequest() {
|
||||
if (request == null) {
|
||||
request = createHttpRequest(path, headers, parameters, requestBody);
|
||||
request = createHttpRequest(path, method, headers, parameters, requestBody);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
@ -346,7 +420,11 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
}
|
||||
|
||||
private OIDCHttpFacade createHttpFacade(String path, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters) {
|
||||
return createHttpFacade(path, token, headers, parameters, null);
|
||||
return createHttpFacade(path, null, token, headers, parameters, null);
|
||||
}
|
||||
|
||||
private OIDCHttpFacade createHttpFacade(String path, String method, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters) {
|
||||
return createHttpFacade(path, method, token, headers, parameters, null);
|
||||
}
|
||||
|
||||
private Response createHttpResponse(Map<String, List<String>> headers) {
|
||||
|
@ -401,14 +479,14 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
};
|
||||
}
|
||||
|
||||
private Request createHttpRequest(String path, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
|
||||
private Request createHttpRequest(String path, String method, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
|
||||
return new Request() {
|
||||
|
||||
private InputStream inputStream;
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return "GET";
|
||||
return method == null ? "GET" : method;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -67,13 +67,17 @@ public abstract class AbstractResourceServerTest extends AbstractKeycloakTest {
|
|||
.user(UserBuilder.create().username("marta").password("password")
|
||||
.addRoles("uma_authorization", "uma_protection")
|
||||
.role("resource-server-test", "uma_protection"))
|
||||
.user(UserBuilder.create().username("alice").password("password")
|
||||
.addRoles("uma_authorization", "uma_protection")
|
||||
.role("resource-server-test", "uma_protection"))
|
||||
.user(UserBuilder.create().username("kolo").password("password"))
|
||||
.client(ClientBuilder.create().clientId("resource-server-test")
|
||||
.secret("secret")
|
||||
.authorizationServicesEnabled(true)
|
||||
.redirectUris("http://localhost/resource-server-test")
|
||||
.defaultRoles("uma_protection")
|
||||
.directAccessGrants())
|
||||
.directAccessGrants()
|
||||
.serviceAccountsEnabled(true))
|
||||
.client(ClientBuilder.create().clientId("test-app")
|
||||
.redirectUris("http://localhost:8180/auth/realms/master/app/auth")
|
||||
.publicClient())
|
||||
|
@ -155,7 +159,7 @@ public abstract class AbstractResourceServerTest extends AbstractKeycloakTest {
|
|||
return authorization.authorize(authorizationRequest);
|
||||
}
|
||||
|
||||
protected RealmResource getRealm() throws Exception {
|
||||
protected RealmResource getRealm() {
|
||||
return adminClient.realm("authz-test");
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,561 @@
|
|||
/*
|
||||
* 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.testsuite.authz;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.authorization.client.AuthorizationDeniedException;
|
||||
import org.keycloak.authorization.client.resource.AuthorizationResource;
|
||||
import org.keycloak.authorization.client.resource.PolicyResource;
|
||||
import org.keycloak.authorization.client.resource.ProtectionResource;
|
||||
import org.keycloak.authorization.client.util.HttpResponseException;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationResponse;
|
||||
import org.keycloak.representations.idm.authorization.PermissionRequest;
|
||||
import org.keycloak.representations.idm.authorization.PermissionResponse;
|
||||
import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.GroupBuilder;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.RoleBuilder;
|
||||
import org.keycloak.testsuite.util.RolesBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class UserManagedPermissionServiceTest extends AbstractResourceServerTest {
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(RealmBuilder.create().name(REALM_NAME)
|
||||
.roles(RolesBuilder.create()
|
||||
.realmRole(RoleBuilder.create().name("uma_authorization").build())
|
||||
.realmRole(RoleBuilder.create().name("uma_protection").build())
|
||||
.realmRole(RoleBuilder.create().name("role_a").build())
|
||||
.realmRole(RoleBuilder.create().name("role_b").build())
|
||||
.realmRole(RoleBuilder.create().name("role_c").build())
|
||||
.realmRole(RoleBuilder.create().name("role_d").build())
|
||||
)
|
||||
.group(GroupBuilder.create().name("group_a")
|
||||
.subGroups(Arrays.asList(GroupBuilder.create().name("group_b").build()))
|
||||
.build())
|
||||
.group(GroupBuilder.create().name("group_c").build())
|
||||
.user(UserBuilder.create().username("marta").password("password")
|
||||
.addRoles("uma_authorization", "uma_protection")
|
||||
.role("resource-server-test", "uma_protection"))
|
||||
.user(UserBuilder.create().username("alice").password("password")
|
||||
.addRoles("uma_authorization", "uma_protection")
|
||||
.role("resource-server-test", "uma_protection"))
|
||||
.user(UserBuilder.create().username("kolo").password("password")
|
||||
.addRoles("role_a")
|
||||
.addGroups("group_a"))
|
||||
.client(ClientBuilder.create().clientId("resource-server-test")
|
||||
.secret("secret")
|
||||
.authorizationServicesEnabled(true)
|
||||
.redirectUris("http://localhost/resource-server-test")
|
||||
.defaultRoles("uma_protection")
|
||||
.directAccessGrants()
|
||||
.serviceAccountsEnabled(true))
|
||||
.client(ClientBuilder.create().clientId("client-a")
|
||||
.redirectUris("http://localhost/resource-server-test")
|
||||
.publicClient())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName("Resource A");
|
||||
resource.setOwnerManagedAccess(true);
|
||||
resource.setOwner("marta");
|
||||
resource.addScope("Scope A", "Scope B", "Scope C");
|
||||
|
||||
resource = getAuthzClient().protection().resource().create(resource);
|
||||
|
||||
UmaPermissionRepresentation newPermission = new UmaPermissionRepresentation();
|
||||
|
||||
newPermission.setName("Custom User-Managed Permission");
|
||||
newPermission.setDescription("Users from specific roles are allowed to access");
|
||||
newPermission.addScope("Scope A", "Scope B", "Scope C");
|
||||
newPermission.addRole("role_a", "role_b", "role_c", "role_d");
|
||||
newPermission.addGroup("/group_a", "/group_a/group_b", "/group_c");
|
||||
newPermission.addClient("client-a", "resource-server-test");
|
||||
newPermission.setCondition("$evaluation.grant()");
|
||||
|
||||
ProtectionResource protection = getAuthzClient().protection("marta", "password");
|
||||
|
||||
UmaPermissionRepresentation permission = protection.policy(resource.getId()).create(newPermission);
|
||||
|
||||
assertEquals(newPermission.getName(), permission.getName());
|
||||
assertEquals(newPermission.getDescription(), permission.getDescription());
|
||||
assertTrue(permission.getScopes().containsAll(newPermission.getScopes()));
|
||||
assertTrue(permission.getRoles().containsAll(newPermission.getRoles()));
|
||||
assertTrue(permission.getGroups().containsAll(newPermission.getGroups()));
|
||||
assertTrue(permission.getClients().containsAll(newPermission.getClients()));
|
||||
assertEquals(newPermission.getCondition(), permission.getCondition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName("Resource A");
|
||||
resource.setOwnerManagedAccess(true);
|
||||
resource.setOwner("marta");
|
||||
resource.addScope("Scope A", "Scope B", "Scope C");
|
||||
|
||||
resource = getAuthzClient().protection().resource().create(resource);
|
||||
|
||||
UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
|
||||
|
||||
permission.setName("Custom User-Managed Permission");
|
||||
permission.setDescription("Users from specific roles are allowed to access");
|
||||
permission.addScope("Scope A");
|
||||
permission.addRole("role_a");
|
||||
|
||||
ProtectionResource protection = getAuthzClient().protection("marta", "password");
|
||||
|
||||
permission = protection.policy(resource.getId()).create(permission);
|
||||
|
||||
assertEquals(1, getAssociatedPolicies(permission).size());
|
||||
|
||||
permission.setName("Changed");
|
||||
permission.setDescription("Changed");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
|
||||
UmaPermissionRepresentation updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertEquals(permission.getName(), updated.getName());
|
||||
assertEquals(permission.getDescription(), updated.getDescription());
|
||||
|
||||
permission.removeRole("role_a");
|
||||
permission.addRole("role_b", "role_c");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(1, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertTrue(permission.getRoles().containsAll(updated.getRoles()));
|
||||
|
||||
permission.addRole("role_d");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(1, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertTrue(permission.getRoles().containsAll(updated.getRoles()));
|
||||
|
||||
permission.addGroup("/group_a/group_b");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(2, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertTrue(permission.getGroups().containsAll(updated.getGroups()));
|
||||
|
||||
permission.addGroup("/group_a");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(2, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertTrue(permission.getGroups().containsAll(updated.getGroups()));
|
||||
|
||||
permission.removeGroup("/group_a/group_b");
|
||||
permission.addGroup("/group_c");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(2, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertTrue(permission.getGroups().containsAll(updated.getGroups()));
|
||||
|
||||
permission.addClient("client-a");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(3, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertTrue(permission.getClients().containsAll(updated.getClients()));
|
||||
|
||||
permission.addClient("resource-server-test");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(3, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertTrue(permission.getClients().containsAll(updated.getClients()));
|
||||
|
||||
permission.removeClient("client-a");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(3, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertTrue(permission.getClients().containsAll(updated.getClients()));
|
||||
|
||||
permission.setCondition("$evaluation.grant()");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(4, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertEquals(permission.getCondition(), updated.getCondition());
|
||||
|
||||
permission.setCondition(null);
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(3, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertEquals(permission.getCondition(), updated.getCondition());
|
||||
|
||||
permission.setRoles(null);
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(2, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertEquals(permission.getRoles(), updated.getRoles());
|
||||
|
||||
permission.setClients(null);
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(1, getAssociatedPolicies(permission).size());
|
||||
updated = protection.policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertEquals(permission.getClients(), updated.getClients());
|
||||
|
||||
permission.setGroups(null);
|
||||
|
||||
try {
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
assertEquals(1, getAssociatedPolicies(permission).size());
|
||||
fail("Permission must be removed because the last associated policy was removed");
|
||||
} catch (NotFoundException ignore) {
|
||||
|
||||
} catch (Exception e) {
|
||||
fail("Expected not found");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserManagedPermission() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName("Resource A");
|
||||
resource.setOwnerManagedAccess(true);
|
||||
resource.setOwner("marta");
|
||||
resource.addScope("Scope A", "Scope B", "Scope C");
|
||||
|
||||
resource = getAuthzClient().protection().resource().create(resource);
|
||||
|
||||
UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
|
||||
|
||||
permission.setName("Custom User-Managed Permission");
|
||||
permission.setDescription("Users from specific roles are allowed to access");
|
||||
permission.addScope("Scope A");
|
||||
permission.addRole("role_a");
|
||||
|
||||
ProtectionResource protection = getAuthzClient().protection("marta", "password");
|
||||
|
||||
permission = protection.policy(resource.getId()).create(permission);
|
||||
|
||||
AuthorizationResource authorization = getAuthzClient().authorization("kolo", "password");
|
||||
|
||||
AuthorizationRequest request = new AuthorizationRequest();
|
||||
|
||||
request.addPermission(resource.getId(), "Scope A");
|
||||
|
||||
AuthorizationResponse authzResponse = authorization.authorize(request);
|
||||
|
||||
assertNotNull(authzResponse);
|
||||
|
||||
permission.removeRole("role_a");
|
||||
permission.addRole("role_b");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
|
||||
try {
|
||||
authzResponse = authorization.authorize(request);
|
||||
fail("User should not have permission");
|
||||
} catch (Exception e) {
|
||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||
}
|
||||
|
||||
try {
|
||||
authzResponse = getAuthzClient().authorization("alice", "password").authorize(request);
|
||||
fail("User should not have permission");
|
||||
} catch (Exception e) {
|
||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||
}
|
||||
|
||||
permission.addRole("role_a");
|
||||
|
||||
protection.policy(resource.getId()).update(permission);
|
||||
|
||||
authzResponse = authorization.authorize(request);
|
||||
|
||||
assertNotNull(authzResponse);
|
||||
|
||||
protection.policy(resource.getId()).delete(permission.getId());
|
||||
|
||||
try {
|
||||
authzResponse = authorization.authorize(request);
|
||||
fail("User should not have permission");
|
||||
} catch (Exception e) {
|
||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||
}
|
||||
|
||||
try {
|
||||
getAuthzClient().protection("marta", "password").policy(resource.getId()).findById(permission.getId());
|
||||
fail("Permission must not exist");
|
||||
} catch (Exception e) {
|
||||
assertEquals(404, HttpResponseException.class.cast(e.getCause()).getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPermissionInAdditionToUserGrantedPermission() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName("Resource A");
|
||||
resource.setOwnerManagedAccess(true);
|
||||
resource.setOwner("marta");
|
||||
resource.addScope("Scope A", "Scope B", "Scope C");
|
||||
|
||||
resource = getAuthzClient().protection().resource().create(resource);
|
||||
|
||||
PermissionResponse ticketResponse = getAuthzClient().protection().permission().create(new PermissionRequest(resource.getId(), "Scope A"));
|
||||
|
||||
AuthorizationRequest request = new AuthorizationRequest();
|
||||
|
||||
request.setTicket(ticketResponse.getTicket());
|
||||
|
||||
try {
|
||||
getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||
fail("User should not have permission");
|
||||
} catch (Exception e) {
|
||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||
assertTrue(e.getMessage().contains("request_submitted"));
|
||||
}
|
||||
|
||||
List<PermissionTicketRepresentation> tickets = getAuthzClient().protection().permission().findByResource(resource.getId());
|
||||
|
||||
assertEquals(1, tickets.size());
|
||||
|
||||
PermissionTicketRepresentation ticket = tickets.get(0);
|
||||
|
||||
ticket.setGranted(true);
|
||||
|
||||
getAuthzClient().protection().permission().update(ticket);
|
||||
|
||||
AuthorizationResponse authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||
|
||||
assertNotNull(authzResponse);
|
||||
|
||||
UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
|
||||
|
||||
permission.setName("Custom User-Managed Permission");
|
||||
permission.addScope("Scope A");
|
||||
permission.addRole("role_a");
|
||||
|
||||
ProtectionResource protection = getAuthzClient().protection("marta", "password");
|
||||
|
||||
permission = protection.policy(resource.getId()).create(permission);
|
||||
|
||||
authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||
|
||||
ticket.setGranted(false);
|
||||
|
||||
getAuthzClient().protection().permission().update(ticket);
|
||||
|
||||
authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||
|
||||
permission = getAuthzClient().protection("marta", "password").policy(resource.getId()).findById(permission.getId());
|
||||
|
||||
assertNotNull(permission);
|
||||
|
||||
permission.removeRole("role_a");
|
||||
permission.addRole("role_b");
|
||||
|
||||
getAuthzClient().protection("marta", "password").policy(resource.getId()).update(permission);
|
||||
|
||||
try {
|
||||
getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||
fail("User should not have permission");
|
||||
} catch (Exception e) {
|
||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||
}
|
||||
|
||||
request = new AuthorizationRequest();
|
||||
|
||||
request.addPermission(resource.getId());
|
||||
|
||||
try {
|
||||
getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||
fail("User should not have permission");
|
||||
} catch (Exception e) {
|
||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||
}
|
||||
|
||||
getAuthzClient().protection("marta", "password").policy(resource.getId()).delete(permission.getId());
|
||||
|
||||
try {
|
||||
getAuthzClient().authorization("kolo", "password").authorize(request);
|
||||
fail("User should not have permission");
|
||||
} catch (Exception e) {
|
||||
assertTrue(AuthorizationDeniedException.class.isInstance(e));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPermissionWithoutScopes() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName(UUID.randomUUID().toString());
|
||||
resource.setOwner("marta");
|
||||
resource.setOwnerManagedAccess(true);
|
||||
resource.addScope("Scope A", "Scope B", "Scope C");
|
||||
|
||||
ProtectionResource protection = getAuthzClient().protection();
|
||||
|
||||
resource = protection.resource().create(resource);
|
||||
|
||||
UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
|
||||
|
||||
permission.setName("Custom User-Managed Policy");
|
||||
permission.addRole("role_a");
|
||||
|
||||
PolicyResource policy = getAuthzClient().protection("marta", "password").policy(resource.getId());
|
||||
|
||||
permission = policy.create(permission);
|
||||
|
||||
assertEquals(3, permission.getScopes().size());
|
||||
assertTrue(Arrays.asList("Scope A", "Scope B", "Scope C").containsAll(permission.getScopes()));
|
||||
|
||||
permission = policy.findById(permission.getId());
|
||||
|
||||
assertTrue(Arrays.asList("Scope A", "Scope B", "Scope C").containsAll(permission.getScopes()));
|
||||
assertEquals(3, permission.getScopes().size());
|
||||
|
||||
permission.removeScope("Scope B");
|
||||
|
||||
policy.update(permission);
|
||||
permission = policy.findById(permission.getId());
|
||||
|
||||
assertEquals(2, permission.getScopes().size());
|
||||
assertTrue(Arrays.asList("Scope A", "Scope C").containsAll(permission.getScopes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyResourceOwnerCanManagePolicies() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName(UUID.randomUUID().toString());
|
||||
resource.setOwner("marta");
|
||||
resource.addScope("Scope A", "Scope B", "Scope C");
|
||||
|
||||
ProtectionResource protection = getAuthzClient().protection();
|
||||
|
||||
resource = protection.resource().create(resource);
|
||||
|
||||
try {
|
||||
getAuthzClient().protection("alice", "password").policy(resource.getId()).create(new UmaPermissionRepresentation());
|
||||
fail("Error expected");
|
||||
} catch (Exception e) {
|
||||
assertTrue(HttpResponseException.class.cast(e.getCause()).toString().contains("Only resource onwer can access policies for resource"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyResourcesWithOwnerManagedAccess() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName(UUID.randomUUID().toString());
|
||||
resource.setOwner("marta");
|
||||
resource.addScope("Scope A", "Scope B", "Scope C");
|
||||
|
||||
ProtectionResource protection = getAuthzClient().protection();
|
||||
|
||||
resource = protection.resource().create(resource);
|
||||
|
||||
try {
|
||||
getAuthzClient().protection("marta", "password").policy(resource.getId()).create(new UmaPermissionRepresentation());
|
||||
fail("Error expected");
|
||||
} catch (Exception e) {
|
||||
assertTrue(HttpResponseException.class.cast(e.getCause()).toString().contains("Only resources with owner managed accessed can have policies"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindPermission() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName(UUID.randomUUID().toString());
|
||||
resource.setOwner("marta");
|
||||
resource.setOwnerManagedAccess(true);
|
||||
resource.addScope("Scope A", "Scope B", "Scope C");
|
||||
|
||||
ProtectionResource protection = getAuthzClient().protection();
|
||||
|
||||
resource = protection.resource().create(resource);
|
||||
|
||||
PolicyResource policy = getAuthzClient().protection("marta", "password").policy(resource.getId());
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
|
||||
|
||||
permission.setName("Custom User-Managed Policy " + i);
|
||||
permission.addRole("role_a");
|
||||
|
||||
policy.create(permission);
|
||||
}
|
||||
|
||||
assertEquals(10, policy.find(null, null, null, null).size());
|
||||
|
||||
List<UmaPermissionRepresentation> byId = policy.find("Custom User-Managed Policy 8", null, null, null);
|
||||
|
||||
assertEquals(1, byId.size());
|
||||
assertEquals(byId.get(0).getId(), policy.findById(byId.get(0).getId()).getId());
|
||||
assertEquals(10, policy.find(null, "Scope A", null, null).size());
|
||||
assertEquals(5, policy.find(null, null, -1, 5).size());
|
||||
assertEquals(2, policy.find(null, null, -1, 2).size());
|
||||
}
|
||||
|
||||
private List<PolicyRepresentation> getAssociatedPolicies(UmaPermissionRepresentation permission) {
|
||||
return getClient(getRealm()).authorization().policies().policy(permission.getId()).associatedPolicies();
|
||||
}
|
||||
|
||||
}
|
|
@ -200,6 +200,10 @@ doApprove=Approve
|
|||
doRemoveSharing=Remove Sharing
|
||||
doRemoveRequest=Remove Request
|
||||
peopleAccessResource=People with access to this resource
|
||||
resourceManagedPolicies=Permissions granting access to this resource
|
||||
resourceNoPermissionsGrantingAccess=No permissions granting access to this resource
|
||||
anyAction=Any action
|
||||
description=Description
|
||||
name=Name
|
||||
scopes=Scopes
|
||||
resource=Resource
|
||||
|
|
|
@ -180,6 +180,70 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<h3>
|
||||
${msg("resourceManagedPolicies")}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${msg("description")}</th>
|
||||
<th>${msg("permission")}</th>
|
||||
<th>${msg("action")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#if authorization.resource.policies?size != 0>
|
||||
<#list authorization.resource.policies as permission>
|
||||
<form action="${url.getResourceGrant(authorization.resource.id)}" name="revokePolicyForm-${authorization.resource.id}-${permission.id}" method="post">
|
||||
<input type="hidden" name="action" value="revokePolicy">
|
||||
<input type="hidden" name="permission_id" value="${permission.id}"/>
|
||||
<tr>
|
||||
<td>
|
||||
<#if permission.description??>
|
||||
${permission.description}
|
||||
</#if>
|
||||
</td>
|
||||
<td>
|
||||
<#if permission.scopes?size != 0>
|
||||
<#list permission.scopes as scope>
|
||||
<div class="search-box">
|
||||
<#if scope.displayName??>
|
||||
${scope.displayName}
|
||||
<#else>
|
||||
${scope.name}
|
||||
</#if>
|
||||
<button class="close-icon" type="button" name="removePolicyScope-${authorization.resource.id}-${permission.id}-${scope.id}" onclick="removeScopeElm(this.parentNode);document.forms['revokePolicyForm-${authorization.resource.id}-${permission.id}'].submit();"><i class="fa fa-times" aria-hidden="true"></i></button>
|
||||
<input type="hidden" name="permission_id" value="${permission.id}:${scope.id}"/>
|
||||
</div>
|
||||
</#list>
|
||||
<#else>
|
||||
${msg("anyAction")}
|
||||
</#if>
|
||||
</td>
|
||||
<td width="20%" align="middle" style="vertical-align: middle">
|
||||
<a href="#" id="revokePolicy-${authorization.resource.name}-${permission.id}" onclick="document.forms['revokePolicyForm-${authorization.resource.id}-${permission.id}']['action'].value = 'revokePolicyAll';document.forms['revokePolicyForm-${authorization.resource.id}-${permission.id}'].submit();" type="submit" class="btn btn-primary">${msg("doRevoke")}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
</#list>
|
||||
<#else>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
${msg("resourceNoPermissionsGrantingAccess")}
|
||||
</td>
|
||||
</tr>
|
||||
</#if>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<h3>
|
||||
|
|
|
@ -41,12 +41,18 @@
|
|||
<div>
|
||||
<div>
|
||||
<li data-ng-repeat="policyResult in result.policies">
|
||||
<strong><a
|
||||
href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a></strong>
|
||||
<strong>
|
||||
<a data-ng-show="policyResult.policy.type != 'uma'"
|
||||
href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a>
|
||||
<a data-ng-show="policyResult.policy.type == 'uma'"
|
||||
href="">
|
||||
{{policyResult.policy.description}}
|
||||
</a>
|
||||
</strong>
|
||||
decision was <span style="color: green" data-ng-show="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
|
||||
<span style="color: red" data-ng-hide="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
|
||||
by <strong>{{policyResult.policy.decisionStrategy}}</strong> decision. {{policyResult.policy.scopes.length > 0 ? (policyResult.status == 'DENY' ? 'Denied Scopes:' : 'Granted Scopes:') : ''}} <span data-ng-repeat="scope in policyResult.policy.scopes"><strong style="color: {{(policyResult.status == 'DENY' ? 'red' : 'green')}}">{{scope}}{{$last ? '' : ', '}}</strong></span>{{policyResult.policy.scopes.length > 0 ? '.' : ''}}
|
||||
<ul>
|
||||
<ul data-ng-show="policyResult.policy.type != 'uma'">
|
||||
<li data-ng-repeat="subPolicy in policyResult.associatedPolicies">
|
||||
<strong><a
|
||||
href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{subPolicy.policy.type}}/{{subPolicy.policy.id}}">{{subPolicy.policy.name}}</a></strong>
|
||||
|
|
Loading…
Reference in a new issue