Merge pull request #3085 from pedroigor/master

[KEYCLOAK-3376] - Show authorization data when evaluating authorization requests
This commit is contained in:
Marek Posolda 2016-08-01 09:09:55 +02:00 committed by GitHub
commit 159b752fb0
5 changed files with 59 additions and 26 deletions

View file

@ -87,16 +87,17 @@ public class PolicyEvaluationService {
@Consumes("application/json")
@Produces("application/json")
public void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) {
EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest);
authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(evaluationRequest, authorization, asyncResponse));
KeycloakIdentity identity = createIdentity(evaluationRequest);
EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity);
authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(evaluationRequest, authorization, identity, asyncResponse));
}
private DecisionResultCollector createDecisionCollector(PolicyEvaluationRequest evaluationRequest, AuthorizationProvider authorization, AsyncResponse asyncResponse) {
private DecisionResultCollector createDecisionCollector(PolicyEvaluationRequest evaluationRequest, AuthorizationProvider authorization, KeycloakIdentity identity, AsyncResponse asyncResponse) {
return new DecisionResultCollector() {
@Override
protected void onComplete(List<Result> results) {
try {
asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(evaluationRequest, results, resourceServer, authorization)).build());
asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(evaluationRequest, results, resourceServer, authorization, identity)).build());
} catch (Throwable cause) {
asyncResponse.resume(cause);
}
@ -109,8 +110,8 @@ public class PolicyEvaluationService {
};
}
private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation) {
return new KeycloakEvaluationContext(createIdentity(representation), this.authorization.getKeycloakSession()) {
private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation, KeycloakIdentity identity) {
return new KeycloakEvaluationContext(identity, this.authorization.getKeycloakSession()) {
@Override
public Attributes getAttributes() {
Map<String, Collection<String>> attributes = new HashMap<>(super.getAttributes().toMap());
@ -137,17 +138,6 @@ public class PolicyEvaluationService {
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
List<PolicyEvaluationRequest.Resource> resources = representation.getResources();
for (PolicyEvaluationRequest.Resource resource : new ArrayList<>(resources)) {
if (resource.getId() == null && (resource.getScopes() == null || resource.getScopes().isEmpty())) {
resources.remove(resource);
}
}
if (representation.isEntitlements() || resources.isEmpty()) {
return Permissions.all(this.resourceServer, evaluationContext.getIdentity(), authorization);
}
return resources.stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
Set<String> givenScopes = resource.getScopes();

View file

@ -21,10 +21,15 @@ package org.keycloak.authorization.admin.representation;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.Decision.Effect;
import org.keycloak.authorization.admin.util.Models;
import org.keycloak.authorization.common.KeycloakIdentity;
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.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@ -42,14 +47,22 @@ public class PolicyEvaluationResponse {
private List<EvaluationResultRepresentation> results;
private boolean entitlements;
private Effect status;
private AccessToken rpt;
private PolicyEvaluationResponse() {
}
public static PolicyEvaluationResponse build(PolicyEvaluationRequest evaluationRequest, List<Result> results, ResourceServer resourceServer, AuthorizationProvider authorization) {
public static PolicyEvaluationResponse build(PolicyEvaluationRequest evaluationRequest, List<Result> results, ResourceServer resourceServer, AuthorizationProvider authorization, KeycloakIdentity identity) {
PolicyEvaluationResponse response = new PolicyEvaluationResponse();
List<EvaluationResultRepresentation> resultsRep = new ArrayList<>();
AccessToken accessToken = identity.getAccessToken();
AccessToken.Authorization authorizationData = new AccessToken.Authorization();
authorizationData.setPermissions(Permissions.allPermits(results));
accessToken.setAuthorization(authorizationData);
response.rpt = accessToken;
if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Effect.DENY))) {
response.status = Effect.DENY;
@ -124,6 +137,10 @@ public class PolicyEvaluationResponse {
return entitlements;
}
public AccessToken getRpt() {
return rpt;
}
public static class EvaluationResultRepresentation {
private ResourceRepresentation resource;

View file

@ -1076,10 +1076,11 @@ authz-permission-scope-scope.tooltip=Specifies that this permission must be appl
# Authz Evaluation
authz-evaluation-identity-information=Identity Information
authz-evaluation-identity-information.tooltip=The available options to configure the identity information that will be used when evaluating policies.
authz-evaluation-client.tooltip=Select the client making this authorization request.
authz-evaluation-client.tooltip=Select the client making this authorization request. If not provided, authorization requests would be done based on the client you are in.
authz-evaluation-user.tooltip=Select an user whose identity is going to be used to query permissions from the server.
authz-evaluation-role.tooltip=Select the roles you want to associate with the selected user.
authz-evaluation-new=New Evaluation
authz-evaluation-re-evaluate=Re-Evaluate
authz-evaluation-previous=Previous Evaluation
authz-evaluation-contextual-info=Contextual Information
authz-evaluation-contextual-info.tooltip=The available options to configure any contextual information that will be used when evaluating policies.
@ -1093,3 +1094,6 @@ authz-evaluation-no-policies-resource=No policies were found for this resource.
authz-evaluation-result.tooltip=The overall result for this permission request.
authz-evaluation-scopes.tooltip=The requested scopes.
authz-evaluation-policies.tooltip=Details about which policies were evaluated and their decisions.
authz-evaluation-authorization-data=Response
authz-evaluation-authorization-data.tooltip=Represents a token carrying authorization data as a result of the processing of an authorization request. This representation is basically what Keycloak issues to clients asking for permissions. Check the 'authorization' claim for the permissions that were granted based on the current authorization request.
authz-show-authorization-data=Show Authorization Data

View file

@ -1329,6 +1329,18 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
}
}
$scope.reevaluate = function() {
if ($scope.authzRequest.entitlements) {
$scope.entitlements();
} else {
$scope.save();
}
}
$scope.showAuthzData = function() {
$scope.showRpt = true;
}
$scope.save = function() {
$scope.authzRequest.entitlements = false;
if ($scope.applyResourceType) {
@ -1356,10 +1368,12 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
$scope.showResultTab = function() {
$scope.showResult = true;
$scope.showRpt = false;
}
$scope.showRequestTab = function() {
$scope.showResult = false;
$scope.showRpt = false;
}
User.query({realm: $route.current.params.realm}, function(data) {

View file

@ -12,6 +12,10 @@
<div data-ng-show="showResult">
<br>
<a href="" data-ng-click="showRequestTab()">{{:: 'authz-evaluation-new' | translate}}</a>
|
<a href="" data-ng-click="reevaluate()">{{:: 'authz-evaluation-re-evaluate' | translate}}</a>
|
<a href="" data-ng-click="showAuthzData()">{{:: 'authz-show-authorization-data' | translate}}</a>
</div>
<div data-ng-show="evaluationResult && !showResult">
@ -19,6 +23,16 @@
<a href="" data-ng-click="showResultTab()">{{:: 'authz-evaluation-previous' | translate}}</a>
</div>
<div data-ng-show="showRpt">
<div class="form-group">
<label class="col-sm-1 control-label" for="rpt">{{:: 'authz-evaluation-authorization-data' | translate}}</label>
<div class="col-md-6">
<textarea id="rpt" class="form-control" rows="20">{{evaluationResult.rpt | json}}</textarea>
</div>
<kc-tooltip>{{:: 'authz-evaluation-authorization-data.tooltip' | translate}}</kc-tooltip>
</div>
</div>
<div data-ng-hide="showResult">
<form class="form-horizontal" name="clientForm" novalidate>
<fieldset>
@ -56,12 +70,6 @@
<kc-tooltip>{{:: 'authz-evaluation-user.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
<button class="btn btn-primary" data-ng-click="entitlements()" data-ng-disabled="authzRequest.userId == null || authzRequest.clientId == null">{{:: 'authz-entitlements' | translate}}</button>
</div>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="reqActions">{{:: 'roles' | translate}} <span class="required"
data-ng-show="!authzRequest.userId || authzRequest.userId == null">*</span></label>
@ -268,7 +276,7 @@
</fieldset>
</form>
</div>
<div data-ng-include="resultUrl" data-ng-show="showResult"/>
<div data-ng-include="resultUrl" data-ng-show="showResult && !showRpt"/>
</div>
<kc-menu></kc-menu>