/* * 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.admin.representation; 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; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> */ public class PolicyEvaluationResponse { private List<EvaluationResultRepresentation> results; private boolean entitlements; private Effect status; private AccessToken rpt; private PolicyEvaluationResponse() { } public static PolicyEvaluationResponse build(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, authorization)); accessToken.setAuthorization(authorizationData); response.rpt = accessToken; if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Effect.DENY))) { response.status = Effect.DENY; } else { response.status = Effect.PERMIT; } for (Result result : results) { EvaluationResultRepresentation rep = new EvaluationResultRepresentation(); rep.setStatus(result.getEffect()); resultsRep.add(rep); if (result.getPermission().getResource() != null) { rep.setResource(ModelToRepresentation.toRepresentation(result.getPermission().getResource(), resourceServer, authorization)); } 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 -> ModelToRepresentation.toRepresentation(scope, authorization)).collect(Collectors.toList())); List<PolicyResultRepresentation> policies = new ArrayList<>(); for (PolicyResult policy : result.getResults()) { policies.add(toRepresentation(policy, authorization)); } rep.setPolicies(policies); } resultsRep.sort((o1, o2) -> o1.getResource().getName().compareTo(o2.getResource().getName())); Map<String, EvaluationResultRepresentation> groupedResults = new HashMap<>(); resultsRep.forEach(evaluationResultRepresentation -> { 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(Effect.PERMIT) || (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT) && result.getStatus().equals(Effect.DENY))) { result.setStatus(Effect.PERMIT); } List<ScopeRepresentation> scopes = result.getScopes(); if (scopes == null) { scopes = new ArrayList<>(); result.setScopes(scopes); } List<ScopeRepresentation> currentScopes = evaluationResultRepresentation.getScopes(); if (currentScopes != null) { for (ScopeRepresentation scope : currentScopes) { if (!scopes.contains(scope)) { scopes.add(scope); } if (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT)) { List<ScopeRepresentation> allowedScopes = result.getAllowedScopes(); if (!allowedScopes.contains(scope)) { allowedScopes.add(scope); } } } } 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<PolicyResultRepresentation> policies = result.getPolicies(); for (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)) { Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId()); for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scopeModel -> ModelToRepresentation.toRepresentation(scopeModel, authorization)).collect(Collectors.toList())) { if (!policy.getScopes().contains(scope)) { policy.getScopes().add(scope); } } for (ScopeRepresentation scope : currentScopes) { if (!policy.getScopes().contains(scope)) { policy.getScopes().add(scope); } } } } }); response.results = groupedResults.values().stream().collect(Collectors.toList()); return response; } private static PolicyResultRepresentation toRepresentation(PolicyResult policy, AuthorizationProvider authorization) { PolicyResultRepresentation policyResultRep = new PolicyResultRepresentation(); policyResultRep.setPolicy(ModelToRepresentation.toRepresentation(policy.getPolicy(), authorization)); policyResultRep.setStatus(policy.getStatus()); 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; } } }