Merge pull request #3085 from pedroigor/master
[KEYCLOAK-3376] - Show authorization data when evaluating authorization requests
This commit is contained in:
commit
159b752fb0
5 changed files with 59 additions and 26 deletions
|
@ -87,16 +87,17 @@ public class PolicyEvaluationService {
|
||||||
@Consumes("application/json")
|
@Consumes("application/json")
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
public void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) {
|
public void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) {
|
||||||
EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest);
|
KeycloakIdentity identity = createIdentity(evaluationRequest);
|
||||||
authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(evaluationRequest, authorization, asyncResponse));
|
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() {
|
return new DecisionResultCollector() {
|
||||||
@Override
|
@Override
|
||||||
protected void onComplete(List<Result> results) {
|
protected void onComplete(List<Result> results) {
|
||||||
try {
|
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) {
|
} catch (Throwable cause) {
|
||||||
asyncResponse.resume(cause);
|
asyncResponse.resume(cause);
|
||||||
}
|
}
|
||||||
|
@ -109,8 +110,8 @@ public class PolicyEvaluationService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation) {
|
private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation, KeycloakIdentity identity) {
|
||||||
return new KeycloakEvaluationContext(createIdentity(representation), this.authorization.getKeycloakSession()) {
|
return new KeycloakEvaluationContext(identity, this.authorization.getKeycloakSession()) {
|
||||||
@Override
|
@Override
|
||||||
public Attributes getAttributes() {
|
public Attributes getAttributes() {
|
||||||
Map<String, Collection<String>> attributes = new HashMap<>(super.getAttributes().toMap());
|
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) {
|
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
|
||||||
List<PolicyEvaluationRequest.Resource> resources = representation.getResources();
|
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 -> {
|
return resources.stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
|
||||||
Set<String> givenScopes = resource.getScopes();
|
Set<String> givenScopes = resource.getScopes();
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,15 @@ package org.keycloak.authorization.admin.representation;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.Decision.Effect;
|
import org.keycloak.authorization.Decision.Effect;
|
||||||
import org.keycloak.authorization.admin.util.Models;
|
import org.keycloak.authorization.admin.util.Models;
|
||||||
|
import org.keycloak.authorization.common.KeycloakIdentity;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.authorization.policy.evaluation.Result;
|
import org.keycloak.authorization.policy.evaluation.Result;
|
||||||
import org.keycloak.authorization.policy.evaluation.Result.PolicyResult;
|
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.PolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||||
|
@ -42,14 +47,22 @@ public class PolicyEvaluationResponse {
|
||||||
private List<EvaluationResultRepresentation> results;
|
private List<EvaluationResultRepresentation> results;
|
||||||
private boolean entitlements;
|
private boolean entitlements;
|
||||||
private Effect status;
|
private Effect status;
|
||||||
|
private AccessToken rpt;
|
||||||
|
|
||||||
private PolicyEvaluationResponse() {
|
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();
|
PolicyEvaluationResponse response = new PolicyEvaluationResponse();
|
||||||
List<EvaluationResultRepresentation> resultsRep = new ArrayList<>();
|
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))) {
|
if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Effect.DENY))) {
|
||||||
response.status = Effect.DENY;
|
response.status = Effect.DENY;
|
||||||
|
@ -124,6 +137,10 @@ public class PolicyEvaluationResponse {
|
||||||
return entitlements;
|
return entitlements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccessToken getRpt() {
|
||||||
|
return rpt;
|
||||||
|
}
|
||||||
|
|
||||||
public static class EvaluationResultRepresentation {
|
public static class EvaluationResultRepresentation {
|
||||||
|
|
||||||
private ResourceRepresentation resource;
|
private ResourceRepresentation resource;
|
||||||
|
|
|
@ -1076,10 +1076,11 @@ authz-permission-scope-scope.tooltip=Specifies that this permission must be appl
|
||||||
# Authz Evaluation
|
# Authz Evaluation
|
||||||
authz-evaluation-identity-information=Identity Information
|
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-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-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-role.tooltip=Select the roles you want to associate with the selected user.
|
||||||
authz-evaluation-new=New Evaluation
|
authz-evaluation-new=New Evaluation
|
||||||
|
authz-evaluation-re-evaluate=Re-Evaluate
|
||||||
authz-evaluation-previous=Previous Evaluation
|
authz-evaluation-previous=Previous Evaluation
|
||||||
authz-evaluation-contextual-info=Contextual Information
|
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.
|
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-result.tooltip=The overall result for this permission request.
|
||||||
authz-evaluation-scopes.tooltip=The requested scopes.
|
authz-evaluation-scopes.tooltip=The requested scopes.
|
||||||
authz-evaluation-policies.tooltip=Details about which policies were evaluated and their decisions.
|
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
|
||||||
|
|
|
@ -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.save = function() {
|
||||||
$scope.authzRequest.entitlements = false;
|
$scope.authzRequest.entitlements = false;
|
||||||
if ($scope.applyResourceType) {
|
if ($scope.applyResourceType) {
|
||||||
|
@ -1356,10 +1368,12 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
|
||||||
|
|
||||||
$scope.showResultTab = function() {
|
$scope.showResultTab = function() {
|
||||||
$scope.showResult = true;
|
$scope.showResult = true;
|
||||||
|
$scope.showRpt = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.showRequestTab = function() {
|
$scope.showRequestTab = function() {
|
||||||
$scope.showResult = false;
|
$scope.showResult = false;
|
||||||
|
$scope.showRpt = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
User.query({realm: $route.current.params.realm}, function(data) {
|
User.query({realm: $route.current.params.realm}, function(data) {
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
<div data-ng-show="showResult">
|
<div data-ng-show="showResult">
|
||||||
<br>
|
<br>
|
||||||
<a href="" data-ng-click="showRequestTab()">{{:: 'authz-evaluation-new' | translate}}</a>
|
<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>
|
||||||
|
|
||||||
<div data-ng-show="evaluationResult && !showResult">
|
<div data-ng-show="evaluationResult && !showResult">
|
||||||
|
@ -19,6 +23,16 @@
|
||||||
<a href="" data-ng-click="showResultTab()">{{:: 'authz-evaluation-previous' | translate}}</a>
|
<a href="" data-ng-click="showResultTab()">{{:: 'authz-evaluation-previous' | translate}}</a>
|
||||||
</div>
|
</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">
|
<div data-ng-hide="showResult">
|
||||||
<form class="form-horizontal" name="clientForm" novalidate>
|
<form class="form-horizontal" name="clientForm" novalidate>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
@ -56,12 +70,6 @@
|
||||||
<kc-tooltip>{{:: 'authz-evaluation-user.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-evaluation-user.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</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">
|
<div class="form-group clearfix">
|
||||||
<label class="col-md-2 control-label" for="reqActions">{{:: 'roles' | translate}} <span class="required"
|
<label class="col-md-2 control-label" for="reqActions">{{:: 'roles' | translate}} <span class="required"
|
||||||
data-ng-show="!authzRequest.userId || authzRequest.userId == null">*</span></label>
|
data-ng-show="!authzRequest.userId || authzRequest.userId == null">*</span></label>
|
||||||
|
@ -268,7 +276,7 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div data-ng-include="resultUrl" data-ng-show="showResult"/>
|
<div data-ng-include="resultUrl" data-ng-show="showResult && !showRpt"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<kc-menu></kc-menu>
|
<kc-menu></kc-menu>
|
Loading…
Reference in a new issue