org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler Maven / Gradle / Ivy
/*
* Copyright 2002-2020 the original author or authors.
*
* 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
*
* https://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.springframework.security.access.expression.method;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.log.LogMessage;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.access.PermissionCacheOptimizer;
import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer;
import org.springframework.util.Assert;
/**
* The standard implementation of {@code MethodSecurityExpressionHandler}.
*
* A single instance should usually be shared amongst the beans that require expression
* support.
*
* @author Luke Taylor
* @since 3.0
*/
public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler
implements MethodSecurityExpressionHandler {
protected final Log logger = LogFactory.getLog(getClass());
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultSecurityParameterNameDiscoverer();
private PermissionCacheOptimizer permissionCacheOptimizer = null;
private String defaultRolePrefix = "ROLE_";
public DefaultMethodSecurityExpressionHandler() {
}
/**
* Uses a {@link MethodSecurityEvaluationContext} as the EvaluationContext
* implementation.
*/
@Override
public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi) {
return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
}
/**
* Creates the root object for expression evaluation.
*/
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
MethodInvocation invocation) {
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(getTrustResolver());
root.setRoleHierarchy(getRoleHierarchy());
root.setDefaultRolePrefix(getDefaultRolePrefix());
return root;
}
/**
* Filters the {@code filterTarget} object (which must be either a collection, array,
* map or stream), by evaluating the supplied expression.
*
* If a {@code Collection} or {@code Map} is used, the original instance will be
* modified to contain the elements for which the permission expression evaluates to
* {@code true}. For an array, a new array instance will be returned.
*/
@Override
public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
MethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations) ctx.getRootObject()
.getValue();
this.logger.debug(LogMessage.format("Filtering with expression: %s", filterExpression.getExpressionString()));
if (filterTarget instanceof Collection) {
return filterCollection((Collection) filterTarget, filterExpression, ctx, rootObject);
}
if (filterTarget.getClass().isArray()) {
return filterArray((Object[]) filterTarget, filterExpression, ctx, rootObject);
}
if (filterTarget instanceof Map) {
return filterMap((Map) filterTarget, filterExpression, ctx, rootObject);
}
if (filterTarget instanceof Stream) {
return filterStream((Stream) filterTarget, filterExpression, ctx, rootObject);
}
throw new IllegalArgumentException(
"Filter target must be a collection, array, map or stream type, but was " + filterTarget);
}
private Object filterCollection(Collection filterTarget, Expression filterExpression, EvaluationContext ctx,
MethodSecurityExpressionOperations rootObject) {
this.logger.debug(LogMessage.format("Filtering collection with %s elements", filterTarget.size()));
List retain = new ArrayList<>(filterTarget.size());
if (this.permissionCacheOptimizer != null) {
this.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(), filterTarget);
}
for (T filterObject : filterTarget) {
rootObject.setFilterObject(filterObject);
if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {
retain.add(filterObject);
}
}
this.logger.debug(LogMessage.format("Retaining elements: %s", retain));
filterTarget.clear();
filterTarget.addAll(retain);
return filterTarget;
}
private Object filterArray(Object[] filterTarget, Expression filterExpression, EvaluationContext ctx,
MethodSecurityExpressionOperations rootObject) {
List