/* * 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.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; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class PolicyEvaluationResponseBuilder { public static PolicyEvaluationResponse build(List<Result> results, ResourceServer resourceServer, AuthorizationProvider authorization, KeycloakIdentity identity) { PolicyEvaluationResponse response = new PolicyEvaluationResponse(); 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.setRpt(accessToken); if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Decision.Effect.DENY))) { response.setStatus(DecisionEffect.DENY); } else { response.setStatus(DecisionEffect.PERMIT); } for (Result result : results) { PolicyEvaluationResponse.EvaluationResultRepresentation rep = new PolicyEvaluationResponse.EvaluationResultRepresentation(); if (result.getEffect() == Decision.Effect.DENY) { rep.setStatus(DecisionEffect.DENY); } else { rep.setStatus(DecisionEffect.PERMIT); } resultsRep.add(rep); if (result.getPermission().getResource() != null) { ResourceRepresentation resource = new ResourceRepresentation(); resource.setId(result.getPermission().getResource().getId()); resource.setName(result.getPermission().getResource().getName()); rep.setResource(resource); } else { ResourceRepresentation resource = new ResourceRepresentation(); resource.setName("Any Resource with Scopes " + result.getPermission().getScopes().stream().map(Scope::getName).collect(Collectors.toList())); rep.setResource(resource); } rep.setScopes(result.getPermission().getScopes().stream().map(scope -> { ScopeRepresentation representation = new ScopeRepresentation(); representation.setId(scope.getId()); representation.setName(scope.getName()); return representation; }).collect(Collectors.toList())); List<PolicyEvaluationResponse.PolicyResultRepresentation> policies = new ArrayList<>(); for (Result.PolicyResult policy : result.getResults()) { policies.add(toRepresentation(policy, authorization)); } rep.setPolicies(policies); } resultsRep.sort(Comparator.comparing(o -> o.getResource().getName())); Map<String, PolicyEvaluationResponse.EvaluationResultRepresentation> groupedResults = new HashMap<>(); resultsRep.forEach(evaluationResultRepresentation -> { PolicyEvaluationResponse.EvaluationResultRepresentation result = groupedResults.get(evaluationResultRepresentation.getResource().getId()); ResourceRepresentation resource = evaluationResultRepresentation.getResource(); if (result == null) { groupedResults.put(resource.getId(), evaluationResultRepresentation); result = evaluationResultRepresentation; } 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(); if (scopes == null) { scopes = new ArrayList<>(); result.setScopes(scopes); } List<ScopeRepresentation> currentScopes = evaluationResultRepresentation.getScopes(); if (currentScopes != null) { List<ScopeRepresentation> allowedScopes = result.getAllowedScopes(); for (ScopeRepresentation scope : currentScopes) { if (!scopes.contains(scope)) { scopes.add(scope); } if (evaluationResultRepresentation.getStatus().equals(Decision.Effect.PERMIT)) { if (!allowedScopes.contains(scope)) { allowedScopes.add(scope); } } else { evaluationResultRepresentation.getPolicies().forEach(new Consumer<PolicyEvaluationResponse.PolicyResultRepresentation>() { @Override public void accept(PolicyEvaluationResponse.PolicyResultRepresentation policyResultRepresentation) { if (policyResultRepresentation.getStatus().equals(Decision.Effect.PERMIT)) { if (!allowedScopes.contains(scope)) { allowedScopes.add(scope); } } } }); } } result.setAllowedScopes(allowedScopes); } if (resource.getId() != null) { if (!scopes.isEmpty()) { result.getResource().setName(evaluationResultRepresentation.getResource().getName() + " with scopes " + scopes.stream().flatMap((Function<ScopeRepresentation, Stream<?>>) scopeRepresentation -> Arrays.asList(scopeRepresentation.getName()).stream()).collect(Collectors.toList())); } else { result.getResource().setName(evaluationResultRepresentation.getResource().getName()); } } else { result.getResource().setName("Any Resource with Scopes " + scopes.stream().flatMap((Function<ScopeRepresentation, Stream<?>>) scopeRepresentation -> Arrays.asList(scopeRepresentation.getName()).stream()).collect(Collectors.toList())); } List<PolicyEvaluationResponse.PolicyResultRepresentation> policies = result.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(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()) { result.getAllowedScopes().remove(scope); policy.getScopes().add(scope); } } } else {} } }); response.setResults(groupedResults.values().stream().collect(Collectors.toList())); return response; } private static PolicyEvaluationResponse.PolicyResultRepresentation toRepresentation(Result.PolicyResult policy, AuthorizationProvider authorization) { PolicyEvaluationResponse.PolicyResultRepresentation policyResultRep = new PolicyEvaluationResponse.PolicyResultRepresentation(); PolicyRepresentation representation = new PolicyRepresentation(); representation.setId(policy.getPolicy().getId()); representation.setName(policy.getPolicy().getName()); representation.setType(policy.getPolicy().getType()); representation.setDecisionStrategy(policy.getPolicy().getDecisionStrategy()); policyResultRep.setPolicy(representation); 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; } }