Skip to content

Commit

Permalink
[KEYCLOAK-4902] - Using streams to process requested permissions and …
Browse files Browse the repository at this point in the history
…limit support for scope responses
  • Loading branch information
pedroigor committed Aug 17, 2018
1 parent e406e8f commit 625f613
Show file tree
Hide file tree
Showing 10 changed files with 621 additions and 104 deletions.
Expand Up @@ -39,23 +39,19 @@ public void evaluate(Evaluation evaluation) {
Map<Object, Decision.Effect> decisions = decisionCache.computeIfAbsent(policy, p -> new HashMap<>());
ResourcePermission permission = evaluation.getPermission();

for (Scope scope : permission.getScopes()) {
Decision.Effect effect = decisions.get(scope);
Decision.Effect effect = decisions.get(permission);

if (effect != null) {
defaultEvaluation.setEffect(effect);
return;
}
if (effect != null) {
defaultEvaluation.setEffect(effect);
return;
}

Decision.Effect decision = defaultEvaluation.getEffect();

if (decision == null) {
super.evaluate(evaluation);

for (Scope scope : policy.getScopes()) {
decisions.put(scope, defaultEvaluation.getEffect());
}
decisions.put(permission, defaultEvaluation.getEffect());
}
}
}
Expand Up @@ -22,6 +22,7 @@
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -48,6 +49,10 @@ public ResourcePermission(Resource resource, List<Scope> scopes, ResourceServer
this(resource, scopes, resourceServer, null);
}

public ResourcePermission(Resource resource, ResourceServer resourceServer, Map<String, ? extends Collection<String>> claims) {
this(resource, new ArrayList<>(resource.getScopes()), resourceServer, claims);
}

public ResourcePermission(Resource resource, List<Scope> scopes, ResourceServer resourceServer, Map<String, ? extends Collection<String>> claims) {
this.resource = resource;
this.scopes = scopes;
Expand Down Expand Up @@ -125,4 +130,23 @@ public void removeClaim(String name) {
claims.remove(name);
}
}

public void addScope(Scope scope) {
if (resource != null) {
if (!resource.getScopes().contains(scope)) {
return;
}
}

if (!scopes.contains(scope)) {
scopes.add(scope);
}
}

public void addClaims(Map<String, Set<String>> claims) {
if (this.claims == null) {
this.claims = new HashMap<>();
}
this.claims.putAll(claims);
}
}
Expand Up @@ -25,6 +25,7 @@
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
Expand All @@ -36,25 +37,39 @@ public abstract class AbstractDecisionCollector implements Decision<DefaultEvalu
@Override
public void onDecision(DefaultEvaluation evaluation) {
Policy parentPolicy = evaluation.getParentPolicy();
ResourcePermission permission = evaluation.getPermission();

if (parentPolicy != null) {
if (parentPolicy.equals(evaluation.getPolicy())) {
Result.PolicyResult cached = results.computeIfAbsent(evaluation.getPermission(), permission -> new Result(permission, evaluation)).policy(parentPolicy);
results.computeIfAbsent(permission, permission1 -> {
for (Result result : results.values()) {
Result.PolicyResult policyResult = result.getPolicy(parentPolicy);

for (Result result : results.values()) {
Result.PolicyResult policyResult = result.getPolicy(parentPolicy);
if (policyResult != null) {
Result newResult = new Result(permission1, evaluation);
Result.PolicyResult newPolicyResult = newResult.policy(parentPolicy);

if (policyResult != null) {
for (Result.PolicyResult associatePolicy : policyResult.getAssociatedPolicies()) {
cached.policy(associatePolicy.getPolicy(), associatePolicy.getEffect());
for (Result.PolicyResult associatePolicy : policyResult.getAssociatedPolicies()) {
newPolicyResult.policy(associatePolicy.getPolicy(), associatePolicy.getEffect());
}

Map<String, Set<String>> claims = result.getPermission().getClaims();

if (!claims.isEmpty()) {
permission1.addClaims(claims);
}

return newResult;
}
}
}

return null;
}).policy(parentPolicy);
} else {
results.computeIfAbsent(evaluation.getPermission(), permission -> new Result(permission, evaluation)).policy(parentPolicy).policy(evaluation.getPolicy(), evaluation.getEffect());
results.computeIfAbsent(permission, p -> new Result(p, evaluation)).policy(parentPolicy).policy(evaluation.getPolicy(), evaluation.getEffect());
}
} else {
results.computeIfAbsent(evaluation.getPermission(), permission -> new Result(permission, evaluation)).setStatus(evaluation.getEffect());
results.computeIfAbsent(permission, p -> new Result(p, evaluation)).setStatus(evaluation.getEffect());
}
}

Expand Down
Expand Up @@ -86,11 +86,7 @@ public PolicyResult(Policy policy) {
}

public PolicyResult policy(Policy policy, Effect effect) {
PolicyResult result = associatedPolicies.computeIfAbsent(policy.getId(), id -> new PolicyResult(policy, effect));

result.setEffect(effect);

return result;
return associatedPolicies.computeIfAbsent(policy.getId(), id -> new PolicyResult(policy, effect));
}

public Policy getPolicy() {
Expand Down
Expand Up @@ -176,9 +176,9 @@ private List<ResourcePermission> createPermissions(PolicyEvaluationRequest repre

if (resource.getId() != null) {
Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId(), resourceServer.getId());
return new ArrayList<>(Arrays.asList(Permissions.createResourcePermissions(resourceModel, scopes.stream().map(Scope::getName).collect(Collectors.toSet()), authorization, request))).stream();
return new ArrayList<>(Arrays.asList(Permissions.createResourcePermissions(resourceModel, scopes, authorization, request))).stream();
} else if (resource.getType() != null) {
return storeFactory.getResourceStore().findByType(resource.getType(), resourceServer.getId()).stream().map(resource1 -> Permissions.createResourcePermissions(resource1, scopes.stream().map(Scope::getName).collect(Collectors.toSet()), authorization, request));
return storeFactory.getResourceStore().findByType(resource.getType(), resourceServer.getId()).stream().map(resource1 -> Permissions.createResourcePermissions(resource1, scopes, authorization, request));
} else {
if (scopes.isEmpty()) {
return Permissions.all(resourceServer, evaluationContext.getIdentity(), authorization, request).stream();
Expand All @@ -191,7 +191,7 @@ private List<ResourcePermission> createPermissions(PolicyEvaluationRequest repre
}


return resources.stream().map(resource12 -> Permissions.createResourcePermissions(resource12, scopes.stream().map(Scope::getName).collect(Collectors.toSet()), authorization, request));
return resources.stream().map(resource12 -> Permissions.createResourcePermissions(resource12, scopes, authorization, request));
}
}).collect(Collectors.toList());
}
Expand Down
Expand Up @@ -28,6 +28,8 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -355,10 +357,10 @@ private Collection<ResourcePermission> createPermissions(PermissionTicketToken t
ResourceStore resourceStore = storeFactory.getResourceStore();
ScopeStore scopeStore = storeFactory.getScopeStore();
Metadata metadata = request.getMetadata();
Integer limit = metadata != null ? metadata.getLimit() : null;
final AtomicInteger limit = metadata != null && metadata.getLimit() != null ? new AtomicInteger(metadata.getLimit()) : null;

for (Permission permission : ticket.getPermissions()) {
if (limit != null && limit <= 0) {
if (limit != null && limit.get() <= 0) {
break;
}

Expand All @@ -368,7 +370,7 @@ private Collection<ResourcePermission> createPermissions(PermissionTicketToken t
requestedScopes = new HashSet<>();
}

List<Resource> existingResources = new ArrayList<>();
List<Resource> requestedResources = new ArrayList<>();
String resourceId = permission.getResourceId();

if (resourceId != null) {
Expand All @@ -379,22 +381,22 @@ private Collection<ResourcePermission> createPermissions(PermissionTicketToken t
}

if (resource != null) {
existingResources.add(resource);
requestedResources.add(resource);
} else {
String resourceName = resourceId;
Resource ownerResource = resourceStore.findByName(resourceName, identity.getId(), resourceServer.getId());

if (ownerResource != null) {
permission.setResourceId(ownerResource.getId());
existingResources.add(ownerResource);
requestedResources.add(ownerResource);
}

if (!identity.isResourceServer()) {
Resource serverResource = resourceStore.findByName(resourceName, resourceServer.getId());

if (serverResource != null) {
permission.setResourceId(serverResource.getId());
existingResources.add(serverResource);
requestedResources.add(serverResource);
}
}
}
Expand All @@ -406,45 +408,66 @@ private Collection<ResourcePermission> createPermissions(PermissionTicketToken t
requestedScopes.addAll(Arrays.asList(clientAdditionalScopes.split(" ")));
}

List<Scope> requestedScopesModel = requestedScopes.stream().map(s -> scopeStore.findByName(s, resourceServer.getId())).filter(Objects::nonNull).collect(Collectors.toList());
Set<Scope> requestedScopesModel = requestedScopes.stream().map(s -> scopeStore.findByName(s, resourceServer.getId())).filter(Objects::nonNull).collect(Collectors.toSet());

if (resourceId != null && existingResources.isEmpty()) {
if (resourceId != null && requestedResources.isEmpty()) {
throw new CorsErrorResponseException(request.getCors(), "invalid_resource", "Resource with id [" + resourceId + "] does not exist.", Status.BAD_REQUEST);
}

if ((permission.getScopes() != null && !permission.getScopes().isEmpty()) && requestedScopesModel.isEmpty()) {
throw new CorsErrorResponseException(request.getCors(), "invalid_scope", "One of the given scopes " + permission.getScopes() + " are invalid", Status.BAD_REQUEST);
if (!requestedScopes.isEmpty() && requestedScopesModel.isEmpty()) {
throw new CorsErrorResponseException(request.getCors(), "invalid_scope", "One of the given scopes " + permission.getScopes() + " is invalid", Status.BAD_REQUEST);
}

if (!existingResources.isEmpty()) {
for (Resource resource : existingResources) {
if (!requestedResources.isEmpty()) {
for (Resource resource : requestedResources) {
if (limit != null && limit.get() <= 0) {
break;
}
ResourcePermission perm = permissionsToEvaluate.get(resource.getId());

if (perm == null) {
perm = Permissions.createResourcePermissions(resource, requestedScopes, authorization, request);
perm = Permissions.createResourcePermissions(resource, requestedScopesModel, authorization, request);
permissionsToEvaluate.put(resource.getId(), perm);
if (limit != null) {
limit--;
limit.decrementAndGet();
}
} else {
for (Scope scope : requestedScopesModel) {
if (!perm.getScopes().contains(scope)) {
perm.getScopes().add(scope);
}
perm.addScope(scope);
}
}
}
} else {
List<Resource> resources = resourceStore.findByScope(requestedScopesModel.stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId());
AtomicBoolean processed = new AtomicBoolean();

if (resources.isEmpty()) {
permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", new ResourcePermission(null, requestedScopesModel, resourceServer, request.getClaims()));
} else {
for (Resource resource : resources) {
permissionsToEvaluate.put(resource.getId(), Permissions.createResourcePermissions(resource, requestedScopes, authorization, request));
resourceStore.findByScope(requestedScopesModel.stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId(), resource -> {
if (limit != null && limit.get() <= 0) {
return;
}

ResourcePermission perm = permissionsToEvaluate.get(resource.getId());

if (perm == null) {
perm = Permissions.createResourcePermissions(resource, requestedScopesModel, authorization, request);
permissionsToEvaluate.put(resource.getId(), perm);
if (limit != null) {
limit--;
limit.decrementAndGet();
}
} else {
for (Scope scope : requestedScopesModel) {
perm.addScope(scope);
}
}

processed.compareAndSet(false, true);
});

if (!processed.get()) {
for (Scope scope : requestedScopesModel) {
if (limit != null && limit.getAndDecrement() <= 0) {
break;
}
permissionsToEvaluate.computeIfAbsent(scope.getId(), s -> new ResourcePermission(null, new ArrayList<>(Arrays.asList(scope)), resourceServer, request.getClaims()));
}
}
}
Expand All @@ -460,7 +483,7 @@ private Collection<ResourcePermission> createPermissions(PermissionTicketToken t

if (permissions != null) {
for (Permission grantedPermission : permissions) {
if (limit != null && limit <= 0) {
if (limit != null && limit.get() <= 0) {
break;
}

Expand All @@ -473,7 +496,7 @@ private Collection<ResourcePermission> createPermissions(PermissionTicketToken t
permission = new ResourcePermission(resource, new ArrayList<>(), resourceServer, grantedPermission.getClaims());
permissionsToEvaluate.put(resource.getId(), permission);
if (limit != null) {
limit--;
limit.decrementAndGet();
}
} else {
if (grantedPermission.getClaims() != null) {
Expand Down

0 comments on commit 625f613

Please sign in to comment.