Merge pull request #4015 from patriot1burke/master
KEYCLOAK-4727 KEYCLOAK-4652
This commit is contained in:
commit
c198f4ffa7
13 changed files with 903 additions and 290 deletions
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.representations.idm.authorization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public enum DecisionEffect {
|
||||
PERMIT,
|
||||
DENY
|
||||
}
|
|
@ -16,8 +16,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.authorization.admin.representation;
|
||||
package org.keycloak.representations.idm.authorization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -29,11 +31,11 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
|||
*/
|
||||
public class PolicyEvaluationRequest {
|
||||
|
||||
private Map<String, Map<String, String>> context;
|
||||
private List<Resource> resources;
|
||||
private Map<String, Map<String, String>> context = new HashMap<>();
|
||||
private List<ResourceRepresentation> resources = new LinkedList<>();
|
||||
private String clientId;
|
||||
private String userId;
|
||||
private List<String> roleIds;
|
||||
private List<String> roleIds = new LinkedList<>();
|
||||
private boolean entitlements;
|
||||
|
||||
public Map<String, Map<String, String>> getContext() {
|
||||
|
@ -44,11 +46,11 @@ public class PolicyEvaluationRequest {
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
public List<Resource> getResources() {
|
||||
public List<ResourceRepresentation> getResources() {
|
||||
return this.resources;
|
||||
}
|
||||
|
||||
public void setResources(List<Resource> resources) {
|
||||
public void setResources(List<ResourceRepresentation> resources) {
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
|
@ -84,7 +86,13 @@ public class PolicyEvaluationRequest {
|
|||
this.entitlements = entitlements;
|
||||
}
|
||||
|
||||
public static class Resource extends ResourceRepresentation {
|
||||
|
||||
public PolicyEvaluationRequest addResource(String name, String... scopes) {
|
||||
if (resources == null) {
|
||||
resources = new LinkedList<>();
|
||||
}
|
||||
resources.add(new ResourceRepresentation(name, scopes));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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 org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.DecisionEffect;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class PolicyEvaluationResponse {
|
||||
|
||||
private List<EvaluationResultRepresentation> results;
|
||||
private boolean entitlements;
|
||||
private DecisionEffect status;
|
||||
private AccessToken rpt;
|
||||
|
||||
public List<EvaluationResultRepresentation> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public DecisionEffect getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public boolean isEntitlements() {
|
||||
return entitlements;
|
||||
}
|
||||
|
||||
public AccessToken getRpt() {
|
||||
return rpt;
|
||||
}
|
||||
|
||||
public void setResults(List<EvaluationResultRepresentation> results) {
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
public void setEntitlements(boolean entitlements) {
|
||||
this.entitlements = entitlements;
|
||||
}
|
||||
|
||||
public void setStatus(DecisionEffect status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setRpt(AccessToken rpt) {
|
||||
this.rpt = rpt;
|
||||
}
|
||||
|
||||
public static class EvaluationResultRepresentation {
|
||||
|
||||
private ResourceRepresentation resource;
|
||||
private List<ScopeRepresentation> scopes;
|
||||
private List<PolicyResultRepresentation> policies;
|
||||
private DecisionEffect status;
|
||||
private List<ScopeRepresentation> allowedScopes = new ArrayList<>();
|
||||
|
||||
public void setResource(final ResourceRepresentation resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public ResourceRepresentation getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setScopes(List<ScopeRepresentation> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public List<ScopeRepresentation> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
public void setPolicies(final List<PolicyResultRepresentation> policies) {
|
||||
this.policies = policies;
|
||||
}
|
||||
|
||||
public List<PolicyResultRepresentation> getPolicies() {
|
||||
return policies;
|
||||
}
|
||||
|
||||
public void setStatus(final DecisionEffect status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public DecisionEffect getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setAllowedScopes(List<ScopeRepresentation> allowedScopes) {
|
||||
this.allowedScopes = allowedScopes;
|
||||
}
|
||||
|
||||
public List<ScopeRepresentation> getAllowedScopes() {
|
||||
return allowedScopes;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PolicyResultRepresentation {
|
||||
|
||||
private PolicyRepresentation policy;
|
||||
private DecisionEffect status;
|
||||
private List<PolicyResultRepresentation> associatedPolicies;
|
||||
private List<ScopeRepresentation> scopes = new ArrayList<>();
|
||||
|
||||
public PolicyRepresentation getPolicy() {
|
||||
return policy;
|
||||
}
|
||||
|
||||
public void setPolicy(final PolicyRepresentation policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
public DecisionEffect getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(final DecisionEffect status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<PolicyResultRepresentation> getAssociatedPolicies() {
|
||||
return associatedPolicies;
|
||||
}
|
||||
|
||||
public void setAssociatedPolicies(final List<PolicyResultRepresentation> associatedPolicies) {
|
||||
this.associatedPolicies = associatedPolicies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.policy.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final PolicyResultRepresentation policy = (PolicyResultRepresentation) o;
|
||||
return this.policy.equals(policy.getPolicy());
|
||||
}
|
||||
|
||||
public void setScopes(List<ScopeRepresentation> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public List<ScopeRepresentation> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@ package org.keycloak.representations.idm.authorization;
|
|||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
@ -91,6 +93,15 @@ public class ResourceRepresentation {
|
|||
this(name, scopes, null, null, null);
|
||||
}
|
||||
|
||||
public ResourceRepresentation(String name, String... scopes) {
|
||||
this.name = name;
|
||||
this.scopes = new HashSet<>();
|
||||
for (String s : scopes) {
|
||||
ScopeRepresentation rep = new ScopeRepresentation(s);
|
||||
this.scopes.add(rep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package org.keycloak.admin.client.resource;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
|
||||
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
|
||||
|
@ -53,4 +55,11 @@ public interface PoliciesResource {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
List<PolicyProviderRepresentation> policyProviders();
|
||||
|
||||
@POST
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
@Path("evaluate")
|
||||
PolicyEvaluationResponse evaluate(PolicyEvaluationRequest evaluationRequest);
|
||||
|
||||
}
|
||||
|
|
|
@ -36,11 +36,15 @@ public interface UsersResource {
|
|||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<UserRepresentation> search(@QueryParam("username") String username,
|
||||
@QueryParam("firstName") String firstName,
|
||||
@QueryParam("lastName") String lastName,
|
||||
@QueryParam("email") String email,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults);
|
||||
@QueryParam("firstName") String firstName,
|
||||
@QueryParam("lastName") String lastName,
|
||||
@QueryParam("email") String email,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResults);
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<UserRepresentation> search(@QueryParam("username") String username);
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
*/
|
||||
package org.keycloak.authorization.admin;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -33,15 +33,13 @@ import java.util.stream.Stream;
|
|||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.container.AsyncResponse;
|
||||
import javax.ws.rs.container.Suspended;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.admin.representation.PolicyEvaluationRequest;
|
||||
import org.keycloak.authorization.admin.representation.PolicyEvaluationResponse;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
|
||||
import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
|
||||
import org.keycloak.authorization.attribute.Attributes;
|
||||
import org.keycloak.authorization.common.KeycloakEvaluationContext;
|
||||
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||
|
@ -58,15 +56,13 @@ import org.keycloak.authorization.util.Permissions;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.ProtocolMapper;
|
||||
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.resources.admin.RealmAuth;
|
||||
|
@ -89,32 +85,45 @@ 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 void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) {
|
||||
public Response evaluate(PolicyEvaluationRequest evaluationRequest) throws Throwable {
|
||||
this.auth.requireView();
|
||||
KeycloakIdentity identity = createIdentity(evaluationRequest);
|
||||
EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity);
|
||||
authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(authorization, identity, asyncResponse));
|
||||
}
|
||||
|
||||
private DecisionResultCollector createDecisionCollector(AuthorizationProvider authorization, KeycloakIdentity identity, AsyncResponse asyncResponse) {
|
||||
return new DecisionResultCollector() {
|
||||
@Override
|
||||
protected void onComplete(List<Result> results) {
|
||||
try {
|
||||
asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(results, resourceServer, authorization, identity)).build());
|
||||
} catch (Throwable cause) {
|
||||
asyncResponse.resume(cause);
|
||||
}
|
||||
CloseableKeycloakIdentity identity = createIdentity(evaluationRequest);
|
||||
try {
|
||||
EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity);
|
||||
Decision decisionCollector = new Decision();
|
||||
authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(decisionCollector);
|
||||
if (decisionCollector.error != null) {
|
||||
throw decisionCollector.error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable cause) {
|
||||
asyncResponse.resume(cause);
|
||||
}
|
||||
};
|
||||
return Response.ok(PolicyEvaluationResponseBuilder.build(decisionCollector.results, resourceServer, authorization, identity)).build();
|
||||
} finally {
|
||||
identity.close();
|
||||
}
|
||||
}
|
||||
|
||||
private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation, KeycloakIdentity identity) {
|
||||
|
@ -144,11 +153,11 @@ public class PolicyEvaluationService {
|
|||
}
|
||||
|
||||
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
|
||||
List<PolicyEvaluationRequest.Resource> resources = representation.getResources();
|
||||
return resources.stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
|
||||
List<ResourceRepresentation> resources = representation.getResources();
|
||||
return resources.stream().flatMap((Function<ResourceRepresentation, Stream<ResourcePermission>>) resource -> {
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
if (resource == null) {
|
||||
resource = new PolicyEvaluationRequest.Resource();
|
||||
resource = new ResourceRepresentation();
|
||||
}
|
||||
|
||||
Set<ScopeRepresentation> givenScopes = resource.getScopes();
|
||||
|
@ -180,16 +189,77 @@ public class PolicyEvaluationService {
|
|||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private KeycloakIdentity createIdentity(PolicyEvaluationRequest representation) {
|
||||
private static class CloseableKeycloakIdentity extends KeycloakIdentity {
|
||||
private UserSessionModel userSession;
|
||||
private ClientSessionModel clientSession;
|
||||
|
||||
public CloseableKeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession, UserSessionModel userSession, ClientSessionModel clientSession) {
|
||||
super(accessToken, keycloakSession);
|
||||
this.userSession = userSession;
|
||||
this.clientSession = clientSession;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (clientSession != null) {
|
||||
keycloakSession.sessions().removeClientSession(realm, clientSession);
|
||||
}
|
||||
|
||||
if (userSession != null) {
|
||||
keycloakSession.sessions().removeUserSession(realm, userSession);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private CloseableKeycloakIdentity createIdentity(PolicyEvaluationRequest representation) {
|
||||
KeycloakSession keycloakSession = this.authorization.getKeycloakSession();
|
||||
RealmModel realm = keycloakSession.getContext().getRealm();
|
||||
AccessToken accessToken = new AccessToken();
|
||||
AccessToken accessToken = null;
|
||||
|
||||
accessToken.subject(representation.getUserId());
|
||||
accessToken.issuedFor(representation.getClientId());
|
||||
accessToken.audience(representation.getClientId());
|
||||
accessToken.issuer(Urls.realmIssuer(keycloakSession.getContext().getUri().getBaseUri(), realm.getName()));
|
||||
accessToken.setRealmAccess(new AccessToken.Access());
|
||||
|
||||
String subject = representation.getUserId();
|
||||
|
||||
ClientSessionModel clientSession = null;
|
||||
UserSessionModel userSession = null;
|
||||
if (subject != null) {
|
||||
UserModel userModel = keycloakSession.users().getUserById(subject, realm);
|
||||
|
||||
if (userModel != null) {
|
||||
String clientId = representation.getClientId();
|
||||
|
||||
if (clientId == null) {
|
||||
clientId = resourceServer.getClientId();
|
||||
}
|
||||
|
||||
if (clientId != null) {
|
||||
ClientModel clientModel = realm.getClientById(clientId);
|
||||
clientSession = keycloakSession.sessions().createClientSession(realm, clientModel);
|
||||
userSession = keycloakSession.sessions().createUserSession(realm, userModel, userModel.getUsername(), "127.0.0.1", "passwd", false, null, null);
|
||||
|
||||
new TokenManager().attachClientSession(userSession, clientSession);
|
||||
|
||||
Set<RoleModel> requestedRoles = new HashSet<>();
|
||||
for (String roleId : clientSession.getRoles()) {
|
||||
RoleModel role = realm.getRoleById(roleId);
|
||||
if (role != null) {
|
||||
requestedRoles.add(role);
|
||||
}
|
||||
}
|
||||
accessToken = new TokenManager().createClientAccessToken(keycloakSession, requestedRoles, realm, clientModel, userModel, userSession, clientSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (accessToken == null) {
|
||||
accessToken = new AccessToken();
|
||||
|
||||
accessToken.subject(representation.getUserId());
|
||||
accessToken.issuedFor(representation.getClientId());
|
||||
accessToken.audience(representation.getClientId());
|
||||
accessToken.issuer(Urls.realmIssuer(keycloakSession.getContext().getUri().getBaseUri(), realm.getName()));
|
||||
accessToken.setRealmAccess(new AccessToken.Access());
|
||||
|
||||
}
|
||||
|
||||
AccessToken.Access realmAccess = accessToken.getRealmAccess();
|
||||
Map<String, Object> claims = accessToken.getOtherClaims();
|
||||
|
@ -199,69 +269,11 @@ public class PolicyEvaluationService {
|
|||
givenAttributes.forEach((key, value) -> claims.put(key, asList(value)));
|
||||
}
|
||||
|
||||
String subject = accessToken.getSubject();
|
||||
|
||||
if (subject != null) {
|
||||
UserModel userModel = keycloakSession.users().getUserById(subject, realm);
|
||||
|
||||
if (userModel != null) {
|
||||
userModel.getAttributes().forEach(claims::put);
|
||||
|
||||
userModel.getRoleMappings().stream().map(RoleModel::getName).forEach(roleName -> realmAccess.addRole(roleName));
|
||||
|
||||
String clientId = representation.getClientId();
|
||||
|
||||
if (clientId == null) {
|
||||
clientId = resourceServer.getClientId();
|
||||
}
|
||||
|
||||
if (clientId != null) {
|
||||
ClientModel clientModel = realm.getClientById(clientId);
|
||||
ClientSessionModel clientSession = null;
|
||||
UserSessionModel userSession = null;
|
||||
try {
|
||||
clientSession = keycloakSession.sessions().createClientSession(realm, clientModel);
|
||||
userSession = keycloakSession.sessions().createUserSession(realm, userModel, userModel.getUsername(), "127.0.0.1", "passwd", false, null, null);
|
||||
|
||||
UserSessionModel finalUserSession = userSession;
|
||||
ClientSessionModel finalClientSession = clientSession;
|
||||
|
||||
for (ProtocolMapperModel mapping : clientModel.getProtocolMappers()) {
|
||||
KeycloakSessionFactory sessionFactory = keycloakSession.getKeycloakSessionFactory();
|
||||
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
|
||||
|
||||
if (mapper != null && (mapper instanceof OIDCAccessTokenMapper)) {
|
||||
accessToken = ((OIDCAccessTokenMapper)mapper).transformAccessToken(accessToken, mapping, keycloakSession, finalUserSession, finalClientSession);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (clientSession != null) {
|
||||
keycloakSession.sessions().removeClientSession(realm, clientSession);
|
||||
}
|
||||
|
||||
if (userSession != null) {
|
||||
keycloakSession.sessions().removeUserSession(realm, userSession);
|
||||
}
|
||||
}
|
||||
|
||||
AccessToken.Access clientAccess = accessToken.addAccess(clientModel.getClientId());
|
||||
clientAccess.roles(new HashSet<>());
|
||||
|
||||
userModel.getClientRoleMappings(clientModel).stream().map(RoleModel::getName).forEach(roleName -> clientAccess.addRole(roleName));
|
||||
|
||||
ClientModel resourceServerClient = realm.getClientById(resourceServer.getClientId());
|
||||
AccessToken.Access resourceServerAccess = accessToken.addAccess(resourceServerClient.getClientId());
|
||||
resourceServerAccess.roles(new HashSet<>());
|
||||
|
||||
userModel.getClientRoleMappings(resourceServerClient).stream().map(RoleModel::getName).forEach(roleName -> resourceServerAccess.addRole(roleName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (representation.getRoleIds() != null) {
|
||||
representation.getRoleIds().forEach(roleName -> realmAccess.addRole(roleName));
|
||||
}
|
||||
|
||||
return new KeycloakIdentity(accessToken, keycloakSession);
|
||||
return new CloseableKeycloakIdentity(accessToken, keycloakSession, userSession, clientSession);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2016 Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags.
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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,
|
||||
|
@ -15,9 +14,24 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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.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.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.DecisionEffect;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
@ -29,56 +43,37 @@ import java.util.function.Function;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.Decision.Effect;
|
||||
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||
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.policy.evaluation.Result.PolicyResult;
|
||||
import org.keycloak.authorization.util.Permissions;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class PolicyEvaluationResponse {
|
||||
|
||||
private List<EvaluationResultRepresentation> results;
|
||||
private boolean entitlements;
|
||||
private Effect status;
|
||||
private AccessToken rpt;
|
||||
|
||||
private PolicyEvaluationResponse() {
|
||||
|
||||
}
|
||||
|
||||
public class PolicyEvaluationResponseBuilder {
|
||||
public static PolicyEvaluationResponse build(List<Result> results, ResourceServer resourceServer, AuthorizationProvider authorization, KeycloakIdentity identity) {
|
||||
PolicyEvaluationResponse response = new PolicyEvaluationResponse();
|
||||
List<EvaluationResultRepresentation> resultsRep = new ArrayList<>();
|
||||
List<PolicyEvaluationResponse.EvaluationResultRepresentation> resultsRep = new ArrayList<>();
|
||||
AccessToken accessToken = identity.getAccessToken();
|
||||
AccessToken.Authorization authorizationData = new AccessToken.Authorization();
|
||||
|
||||
authorizationData.setPermissions(Permissions.permits(results, authorization, resourceServer.getId()));
|
||||
accessToken.setAuthorization(authorizationData);
|
||||
|
||||
response.rpt = accessToken;
|
||||
response.setRpt(accessToken);
|
||||
|
||||
if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Effect.DENY))) {
|
||||
response.status = Effect.DENY;
|
||||
if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Decision.Effect.DENY))) {
|
||||
response.setStatus(DecisionEffect.DENY);
|
||||
} else {
|
||||
response.status = Effect.PERMIT;
|
||||
response.setStatus(DecisionEffect.PERMIT);
|
||||
}
|
||||
|
||||
for (Result result : results) {
|
||||
EvaluationResultRepresentation rep = new EvaluationResultRepresentation();
|
||||
PolicyEvaluationResponse.EvaluationResultRepresentation rep = new PolicyEvaluationResponse.EvaluationResultRepresentation();
|
||||
|
||||
rep.setStatus(result.getEffect());
|
||||
if (result.getEffect() == Decision.Effect.DENY) {
|
||||
rep.setStatus(DecisionEffect.DENY);
|
||||
} else {
|
||||
rep.setStatus(DecisionEffect.PERMIT);
|
||||
|
||||
}
|
||||
resultsRep.add(rep);
|
||||
|
||||
if (result.getPermission().getResource() != null) {
|
||||
|
@ -105,9 +100,9 @@ public class PolicyEvaluationResponse {
|
|||
return representation;
|
||||
}).collect(Collectors.toList()));
|
||||
|
||||
List<PolicyResultRepresentation> policies = new ArrayList<>();
|
||||
List<PolicyEvaluationResponse.PolicyResultRepresentation> policies = new ArrayList<>();
|
||||
|
||||
for (PolicyResult policy : result.getResults()) {
|
||||
for (Result.PolicyResult policy : result.getResults()) {
|
||||
policies.add(toRepresentation(policy, authorization));
|
||||
}
|
||||
|
||||
|
@ -116,10 +111,10 @@ public class PolicyEvaluationResponse {
|
|||
|
||||
resultsRep.sort(Comparator.comparing(o -> o.getResource().getName()));
|
||||
|
||||
Map<String, EvaluationResultRepresentation> groupedResults = new HashMap<>();
|
||||
Map<String, PolicyEvaluationResponse.EvaluationResultRepresentation> groupedResults = new HashMap<>();
|
||||
|
||||
resultsRep.forEach(evaluationResultRepresentation -> {
|
||||
EvaluationResultRepresentation result = groupedResults.get(evaluationResultRepresentation.getResource().getId());
|
||||
PolicyEvaluationResponse.EvaluationResultRepresentation result = groupedResults.get(evaluationResultRepresentation.getResource().getId());
|
||||
ResourceRepresentation resource = evaluationResultRepresentation.getResource();
|
||||
|
||||
if (result == null) {
|
||||
|
@ -127,8 +122,8 @@ public class PolicyEvaluationResponse {
|
|||
result = evaluationResultRepresentation;
|
||||
}
|
||||
|
||||
if (result.getStatus().equals(Effect.PERMIT) || (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT) && result.getStatus().equals(Effect.DENY))) {
|
||||
result.setStatus(Effect.PERMIT);
|
||||
if (result.getStatus().equals(DecisionEffect.PERMIT) || (evaluationResultRepresentation.getStatus().equals(DecisionEffect.PERMIT) && result.getStatus().equals(DecisionEffect.DENY))) {
|
||||
result.setStatus(DecisionEffect.PERMIT);
|
||||
}
|
||||
|
||||
List<ScopeRepresentation> scopes = result.getScopes();
|
||||
|
@ -146,15 +141,15 @@ public class PolicyEvaluationResponse {
|
|||
if (!scopes.contains(scope)) {
|
||||
scopes.add(scope);
|
||||
}
|
||||
if (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT)) {
|
||||
if (evaluationResultRepresentation.getStatus().equals(Decision.Effect.PERMIT)) {
|
||||
if (!allowedScopes.contains(scope)) {
|
||||
allowedScopes.add(scope);
|
||||
}
|
||||
} else {
|
||||
evaluationResultRepresentation.getPolicies().forEach(new Consumer<PolicyResultRepresentation>() {
|
||||
evaluationResultRepresentation.getPolicies().forEach(new Consumer<PolicyEvaluationResponse.PolicyResultRepresentation>() {
|
||||
@Override
|
||||
public void accept(PolicyResultRepresentation policyResultRepresentation) {
|
||||
if (policyResultRepresentation.getStatus().equals(Effect.PERMIT)) {
|
||||
public void accept(PolicyEvaluationResponse.PolicyResultRepresentation policyResultRepresentation) {
|
||||
if (policyResultRepresentation.getStatus().equals(Decision.Effect.PERMIT)) {
|
||||
if (!allowedScopes.contains(scope)) {
|
||||
allowedScopes.add(scope);
|
||||
}
|
||||
|
@ -176,16 +171,16 @@ public class PolicyEvaluationResponse {
|
|||
result.getResource().setName("Any Resource with Scopes " + scopes.stream().flatMap((Function<ScopeRepresentation, Stream<?>>) scopeRepresentation -> Arrays.asList(scopeRepresentation.getName()).stream()).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
List<PolicyResultRepresentation> policies = result.getPolicies();
|
||||
List<PolicyEvaluationResponse.PolicyResultRepresentation> policies = result.getPolicies();
|
||||
|
||||
for (PolicyResultRepresentation policy : new ArrayList<>(evaluationResultRepresentation.getPolicies())) {
|
||||
for (PolicyEvaluationResponse.PolicyResultRepresentation policy : new ArrayList<>(evaluationResultRepresentation.getPolicies())) {
|
||||
if (!policies.contains(policy)) {
|
||||
policies.add(policy);
|
||||
} else {
|
||||
policy = policies.get(policies.indexOf(policy));
|
||||
}
|
||||
|
||||
if (policy.getStatus().equals(Effect.DENY)) {
|
||||
if (policy.getStatus().equals(Decision.Effect.DENY)) {
|
||||
Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId(), resourceServer.getId());
|
||||
for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scopeModel -> ModelToRepresentation.toRepresentation(scopeModel, authorization)).collect(Collectors.toList())) {
|
||||
if (!policy.getScopes().contains(scope) && policyModel.getScopes().stream().filter(policyScope -> policyScope.getId().equals(scope.getId())).findFirst().isPresent()) {
|
||||
|
@ -197,13 +192,13 @@ public class PolicyEvaluationResponse {
|
|||
}
|
||||
});
|
||||
|
||||
response.results = groupedResults.values().stream().collect(Collectors.toList());
|
||||
response.setResults(groupedResults.values().stream().collect(Collectors.toList()));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static PolicyResultRepresentation toRepresentation(PolicyResult policy, AuthorizationProvider authorization) {
|
||||
PolicyResultRepresentation policyResultRep = new PolicyResultRepresentation();
|
||||
private static PolicyEvaluationResponse.PolicyResultRepresentation toRepresentation(Result.PolicyResult policy, AuthorizationProvider authorization) {
|
||||
PolicyEvaluationResponse.PolicyResultRepresentation policyResultRep = new PolicyEvaluationResponse.PolicyResultRepresentation();
|
||||
|
||||
PolicyRepresentation representation = new PolicyRepresentation();
|
||||
|
||||
|
@ -213,127 +208,15 @@ public class PolicyEvaluationResponse {
|
|||
representation.setDecisionStrategy(policy.getPolicy().getDecisionStrategy());
|
||||
|
||||
policyResultRep.setPolicy(representation);
|
||||
policyResultRep.setStatus(policy.getStatus());
|
||||
if (policy.getStatus() == Decision.Effect.DENY) {
|
||||
policyResultRep.setStatus(DecisionEffect.DENY);
|
||||
} else {
|
||||
policyResultRep.setStatus(DecisionEffect.PERMIT);
|
||||
|
||||
}
|
||||
|
||||
policyResultRep.setAssociatedPolicies(policy.getAssociatedPolicies().stream().map(result -> toRepresentation(result, authorization)).collect(Collectors.toList()));
|
||||
|
||||
return policyResultRep;
|
||||
}
|
||||
|
||||
public List<EvaluationResultRepresentation> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public Effect getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public boolean isEntitlements() {
|
||||
return entitlements;
|
||||
}
|
||||
|
||||
public AccessToken getRpt() {
|
||||
return rpt;
|
||||
}
|
||||
|
||||
public static class EvaluationResultRepresentation {
|
||||
|
||||
private ResourceRepresentation resource;
|
||||
private List<ScopeRepresentation> scopes;
|
||||
private List<PolicyResultRepresentation> policies;
|
||||
private Effect status;
|
||||
private List<ScopeRepresentation> allowedScopes = new ArrayList<>();
|
||||
|
||||
public void setResource(final ResourceRepresentation resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public ResourceRepresentation getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setScopes(List<ScopeRepresentation> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public List<ScopeRepresentation> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
public void setPolicies(final List<PolicyResultRepresentation> policies) {
|
||||
this.policies = policies;
|
||||
}
|
||||
|
||||
public List<PolicyResultRepresentation> getPolicies() {
|
||||
return policies;
|
||||
}
|
||||
|
||||
public void setStatus(final Effect status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Effect getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setAllowedScopes(List<ScopeRepresentation> allowedScopes) {
|
||||
this.allowedScopes = allowedScopes;
|
||||
}
|
||||
|
||||
public List<ScopeRepresentation> getAllowedScopes() {
|
||||
return allowedScopes;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PolicyResultRepresentation {
|
||||
|
||||
private PolicyRepresentation policy;
|
||||
private Effect status;
|
||||
private List<PolicyResultRepresentation> associatedPolicies;
|
||||
private List<ScopeRepresentation> scopes = new ArrayList<>();
|
||||
|
||||
public PolicyRepresentation getPolicy() {
|
||||
return policy;
|
||||
}
|
||||
|
||||
public void setPolicy(final PolicyRepresentation policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
public Effect getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(final Effect status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<PolicyResultRepresentation> getAssociatedPolicies() {
|
||||
return associatedPolicies;
|
||||
}
|
||||
|
||||
public void setAssociatedPolicies(final List<PolicyResultRepresentation> associatedPolicies) {
|
||||
this.associatedPolicies = associatedPolicies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.policy.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final PolicyResultRepresentation policy = (PolicyResultRepresentation) o;
|
||||
return this.policy.equals(policy.getPolicy());
|
||||
}
|
||||
|
||||
public void setScopes(List<ScopeRepresentation> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public List<ScopeRepresentation> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.common;
|
||||
|
||||
import org.keycloak.authorization.attribute.Attributes;
|
||||
import org.keycloak.authorization.identity.Identity;
|
||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public abstract class AbstractEvaluationContext implements EvaluationContext {
|
||||
|
||||
private final KeycloakSession keycloakSession;
|
||||
|
||||
public AbstractEvaluationContext(KeycloakSession keycloakSession) {
|
||||
this.keycloakSession = keycloakSession;
|
||||
}
|
||||
|
||||
public Map<String, Collection<String>> getBaseAttributes() {
|
||||
HashMap<String, Collection<String>> attributes = new HashMap<>();
|
||||
|
||||
attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("MM/dd/yyyy hh:mm:ss").format(new Date())));
|
||||
attributes.put("kc.client.network.ip_address", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteAddr()));
|
||||
attributes.put("kc.client.network.host", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteHost()));
|
||||
|
||||
List<String> userAgents = this.keycloakSession.getContext().getRequestHeaders().getRequestHeader("User-Agent");
|
||||
|
||||
if (userAgents != null) {
|
||||
attributes.put("kc.client.user_agent", userAgents);
|
||||
}
|
||||
|
||||
attributes.put("kc.realm.name", Arrays.asList(this.keycloakSession.getContext().getRealm().getName()));
|
||||
|
||||
return attributes;
|
||||
}
|
||||
}
|
|
@ -45,10 +45,10 @@ import java.util.Map;
|
|||
*/
|
||||
public class KeycloakIdentity implements Identity {
|
||||
|
||||
private final AccessToken accessToken;
|
||||
private final RealmModel realm;
|
||||
private final KeycloakSession keycloakSession;
|
||||
private final Attributes attributes;
|
||||
protected final AccessToken accessToken;
|
||||
protected final RealmModel realm;
|
||||
protected final KeycloakSession keycloakSession;
|
||||
protected final Attributes attributes;
|
||||
|
||||
public KeycloakIdentity(KeycloakSession keycloakSession) {
|
||||
this(Tokens.getAccessToken(keycloakSession), keycloakSession);
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.authorization.common;
|
||||
|
||||
import org.keycloak.authorization.attribute.Attributes;
|
||||
import org.keycloak.authorization.identity.Identity;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserModelIdentity implements Identity {
|
||||
protected RealmModel realm;
|
||||
protected UserModel user;
|
||||
|
||||
public UserModelIdentity(UserModel user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return user.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attributes getAttributes() {
|
||||
Map attr = user.getAttributes();
|
||||
return Attributes.from(attr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRealmRole(String roleName) {
|
||||
RoleModel role = realm.getRole(roleName);
|
||||
if (role == null) return false;
|
||||
return user.hasRole(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasClientRole(String clientId, String roleName) {
|
||||
ClientModel client = realm.getClientByClientId(clientId);
|
||||
RoleModel role = client.getRole(roleName);
|
||||
if (role == null) return false;
|
||||
return user.hasRole(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String roleName) {
|
||||
throw new RuntimeException("Should not execute");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasClientRole(String roleName) {
|
||||
throw new RuntimeException("Should not execute");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.admin;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
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.models.AdminRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionEffect;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Ignore
|
||||
public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation testRealmRep = new RealmRepresentation();
|
||||
testRealmRep.setId(TEST);
|
||||
testRealmRep.setRealm(TEST);
|
||||
testRealmRep.setEnabled(true);
|
||||
testRealms.add(testRealmRep);
|
||||
}
|
||||
|
||||
public static void setupDefaults(KeycloakSession session) {
|
||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||
|
||||
ClientModel client = realm.getClientByClientId("realm-management");
|
||||
|
||||
AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
|
||||
ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId());
|
||||
Scope mapRoleScope = authz.getStoreFactory().getScopeStore().create("map-role", resourceServer);
|
||||
Scope manageScope = authz.getStoreFactory().getScopeStore().create("manage", resourceServer);
|
||||
|
||||
Policy manageUsersPolicy = null;
|
||||
Policy manageClientsPolicy = null;
|
||||
for (RoleModel role : client.getRoles()) {
|
||||
Policy policy = createRolePolicy(authz, resourceServer, role);
|
||||
if (role.getName().equals(AdminRoles.MANAGE_USERS)) {
|
||||
manageUsersPolicy = policy;
|
||||
} else if (role.getName().equals(AdminRoles.MANAGE_CLIENTS)) {
|
||||
manageClientsPolicy = policy;
|
||||
}
|
||||
Resource resource = createRoleResource(authz, resourceServer, role);
|
||||
Set<Scope> scopeset = new HashSet<>();
|
||||
scopeset.add(mapRoleScope);
|
||||
resource.updateScopes(scopeset);
|
||||
|
||||
|
||||
String name = "map.role.permission." + client.getClientId() + "." + role.getName();
|
||||
Policy permission = addScopePermission(authz, resourceServer, name, resource, mapRoleScope, policy);
|
||||
|
||||
}
|
||||
Resource usersResource = authz.getStoreFactory().getResourceStore().create("Users", resourceServer, resourceServer.getClientId());
|
||||
Set<Scope> scopeset = new HashSet<>();
|
||||
scopeset.add(manageScope);
|
||||
usersResource.updateScopes(scopeset);
|
||||
addScopePermission(authz, resourceServer, "Users.manage.permission", usersResource, manageScope, manageUsersPolicy);
|
||||
}
|
||||
|
||||
private static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) {
|
||||
Policy permission = authz.getStoreFactory().getPolicyStore().create(name, "scope", resourceServer);
|
||||
String resources = "[\"" + resource.getId() + "\"]";
|
||||
String scopes = "[\"" + scope.getId() + "\"]";
|
||||
String applyPolicies = "[\"" + policy.getId() + "\"]";
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("resources", resources);
|
||||
config.put("scopes", scopes);
|
||||
config.put("applyPolicies", applyPolicies);
|
||||
permission.setConfig(config);
|
||||
permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
|
||||
permission.setLogic(Logic.POSITIVE);
|
||||
permission.addResource(resource);
|
||||
permission.addScope(scope);
|
||||
permission.addAssociatedPolicy(policy);
|
||||
return permission;
|
||||
}
|
||||
|
||||
private static Resource createRoleResource(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
|
||||
String roleName = getRoleResourceName(role);
|
||||
Resource resource = authz.getStoreFactory().getResourceStore().create(roleName, resourceServer, resourceServer.getClientId());
|
||||
resource.setType("Role");
|
||||
return resource;
|
||||
}
|
||||
|
||||
private static String getRoleResourceName(RoleModel role) {
|
||||
String roleName = "realm";
|
||||
if (role.getContainer() instanceof ClientModel) {
|
||||
ClientModel client = (ClientModel)role.getContainer();
|
||||
roleName = client.getClientId();
|
||||
}
|
||||
roleName = "role.resource." + roleName + "." + role.getName();
|
||||
return roleName;
|
||||
}
|
||||
|
||||
|
||||
private static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
|
||||
String roleName = "realm";
|
||||
if (role.getContainer() instanceof ClientModel) {
|
||||
ClientModel client = (ClientModel) role.getContainer();
|
||||
roleName = client.getClientId() ;
|
||||
}
|
||||
roleName = "role.policy." + roleName + "." + role.getName();
|
||||
Policy policy = authz.getStoreFactory().getPolicyStore().create(roleName, "role", resourceServer);
|
||||
|
||||
String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]";
|
||||
policy.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
|
||||
policy.setLogic(Logic.POSITIVE);
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("roles", roleValues);
|
||||
policy.setConfig(config);
|
||||
return policy;
|
||||
}
|
||||
|
||||
public static void setupUsers(KeycloakSession session) {
|
||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||
ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
||||
UserModel admin = session.users().addUser(realm, "admin");
|
||||
admin.grantRole(client.getRole(AdminRoles.REALM_ADMIN));
|
||||
UserModel manageUserOnlyUser = session.users().addUser(realm, "manage-user");
|
||||
RoleModel manageUsersRole = client.getRole(AdminRoles.MANAGE_USERS);
|
||||
manageUserOnlyUser.grantRole(manageUsersRole);
|
||||
UserModel manageRealmUser = session.users().addUser(realm, "manage-realm");
|
||||
manageRealmUser.grantRole(manageUsersRole);
|
||||
RoleModel manageRealmRole = client.getRole(AdminRoles.MANAGE_REALM);
|
||||
manageRealmUser.grantRole(manageRealmRole);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUI() throws Exception {
|
||||
testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
|
||||
testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
|
||||
//Thread.sleep(1000000000);
|
||||
}
|
||||
|
||||
public static void evaluateAdminHasManageRealmPermissions(KeycloakSession session) {
|
||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||
UserModel admin = session.users().getUserByUsername("admin", realm);
|
||||
|
||||
AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
|
||||
ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
||||
ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
|
||||
|
||||
RoleModel manageRealmRole = client.getRole(AdminRoles.MANAGE_REALM);
|
||||
Resource roleResource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(manageRealmRole), resourceServer.getId());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEvaluation() throws Exception {
|
||||
testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
|
||||
testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
|
||||
|
||||
RealmResource realm = adminClient.realm(TEST);
|
||||
String resourceServerId = realm.clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0).getId();
|
||||
UserRepresentation admin = realm.users().search("admin").get(0);
|
||||
UserRepresentation manageUser = realm.users().search("manage-user").get(0);
|
||||
UserRepresentation manageRealm = realm.users().search("manage-realm").get(0);
|
||||
|
||||
PolicyEvaluationRequest request = new PolicyEvaluationRequest();
|
||||
request.setUserId(admin.getId());
|
||||
request.setClientId(resourceServerId);
|
||||
request.addResource("role.resource." + Constants.REALM_MANAGEMENT_CLIENT_ID + "." + AdminRoles.MANAGE_REALM,
|
||||
"map-role");
|
||||
PolicyEvaluationResponse result = realm.clients().get(resourceServerId).authorization().policies().evaluate(request);
|
||||
Assert.assertEquals(result.getStatus(), DecisionEffect.PERMIT);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.authz;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
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.models.AdminRoles;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionEffect;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class PolicyEvaluationCompositeRoleTest extends AbstractKeycloakTest {
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation testRealmRep = new RealmRepresentation();
|
||||
testRealmRep.setId(TEST);
|
||||
testRealmRep.setRealm(TEST);
|
||||
testRealmRep.setEnabled(true);
|
||||
testRealms.add(testRealmRep);
|
||||
}
|
||||
|
||||
public static void setup(KeycloakSession session) {
|
||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||
ClientModel client = session.realms().addClient(realm, "myclient");
|
||||
RoleModel role1 = client.addRole("client-role1");
|
||||
|
||||
|
||||
AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
|
||||
ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId());
|
||||
Policy policy = createRolePolicy(authz, resourceServer, role1);
|
||||
|
||||
Scope scope = authz.getStoreFactory().getScopeStore().create("myscope", resourceServer);
|
||||
Resource resource = authz.getStoreFactory().getResourceStore().create("myresource", resourceServer, resourceServer.getClientId());
|
||||
addScopePermission(authz, resourceServer, "mypermission", resource, scope, policy);
|
||||
|
||||
RoleModel composite = realm.addRole("composite");
|
||||
composite.addCompositeRole(role1);
|
||||
|
||||
UserModel user = session.users().addUser(realm, "user");
|
||||
user.grantRole(composite);
|
||||
}
|
||||
|
||||
private static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) {
|
||||
Policy permission = authz.getStoreFactory().getPolicyStore().create(name, "scope", resourceServer);
|
||||
String resources = "[\"" + resource.getId() + "\"]";
|
||||
String scopes = "[\"" + scope.getId() + "\"]";
|
||||
String applyPolicies = "[\"" + policy.getId() + "\"]";
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("resources", resources);
|
||||
config.put("scopes", scopes);
|
||||
config.put("applyPolicies", applyPolicies);
|
||||
permission.setConfig(config);
|
||||
permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
|
||||
permission.setLogic(Logic.POSITIVE);
|
||||
permission.addResource(resource);
|
||||
permission.addScope(scope);
|
||||
permission.addAssociatedPolicy(policy);
|
||||
return permission;
|
||||
}
|
||||
|
||||
|
||||
private static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
|
||||
Policy policy = authz.getStoreFactory().getPolicyStore().create(role.getName(), "role", resourceServer);
|
||||
|
||||
String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]";
|
||||
policy.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
|
||||
policy.setLogic(Logic.POSITIVE);
|
||||
Map<String, String> config = new HashMap<>();
|
||||
config.put("roles", roleValues);
|
||||
policy.setConfig(config);
|
||||
return policy;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
testingClient.server().run(PolicyEvaluationCompositeRoleTest::setup);
|
||||
|
||||
RealmResource realm = adminClient.realm(TEST);
|
||||
String resourceServerId = realm.clients().findByClientId("myclient").get(0).getId();
|
||||
UserRepresentation user = realm.users().search("user").get(0);
|
||||
|
||||
PolicyEvaluationRequest request = new PolicyEvaluationRequest();
|
||||
request.setUserId(user.getId());
|
||||
request.setClientId(resourceServerId);
|
||||
request.addResource("myresource", "myscope");
|
||||
PolicyEvaluationResponse result = realm.clients().get(resourceServerId).authorization().policies().evaluate(request);
|
||||
Assert.assertEquals(result.getStatus(), DecisionEffect.PERMIT);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue