/******************************************************************************* * Copyright (c) 2012 GigaSpaces Technologies Ltd. All rights reserved * * 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.cloudifysource.security; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.PostConstruct; import org.aopalliance.intercept.MethodInvocation; import org.cloudifysource.dsl.internal.CloudifyConstants; import org.cloudifysource.dsl.rest.response.ApplicationDescription; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.security.access.expression.ExpressionUtils; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolverImpl; import org.springframework.security.core.Authentication; /** * An extended MethodSecurityExpressionHandler. * @author noak * @since 2.3.1 * */ public class ExtendedMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler { private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); // base class does not have a getter so we had to copy the same code here private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); @Override public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { this.parameterNameDiscoverer = parameterNameDiscoverer; } private Logger logger = java.util.logging.Logger.getLogger(ExtendedMethodSecurityExpressionHandler.class.getName()); @PostConstruct public void overrideDefaults() { setPermissionEvaluator(new CustomDenyAllPermissionEvaluator()); } /** * Uses a {@link CustomMethodSecurityEvaluationContext} as the * <tt>EvaluationContext</tt> implementation and configures it with a * {@link CustomMethodSecurityExpressionRoot} instance as the expression * root object. * @param auth The {@link Authentication} object of the current user * @param mi The attempted method invocation * @return EvaluationContext, containing the permission evaluator to be used */ @Override public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) { return new CustomMethodSecurityEvaluationContext(auth, mi, parameterNameDiscoverer); } /** * Creates the root object for expression evaluation. * @param authentication The {@link Authentication} object of the current user * @param invocation The attempted method invocation * @return The custom {@link CustomMethodSecurityExpressionRoot} as the implementation of * {@link MethodSecurityExpressionOperations} to be used. */ protected MethodSecurityExpressionOperations createSecurityExpressionRoot(final Authentication authentication, final MethodInvocation invocation) { CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication); root.setThis(invocation.getThis()); root.setPermissionEvaluator(getPermissionEvaluator()); root.setTrustResolver(trustResolver); root.setRoleHierarchy(getRoleHierarchy()); return root; } @Override public void setReturnObject(final Object returnObject, final EvaluationContext ctx) { ((CustomMethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject); } @Override public Object filter(final Object filterTarget, final Expression filterExpression, final EvaluationContext ctx) { CustomMethodSecurityExpressionRoot rootObject = (CustomMethodSecurityExpressionRoot) ctx .getRootObject().getValue(); if (logger.isLoggable(Level.FINE)) { logger.fine("Filtering with expression: " + filterExpression.getExpressionString()); } // This comment is left here temporarily until we are absolutely sure it is not required [noak]. /*if (filterTarget instanceof Collection || filterTarget.getClass().isArray()) { return super.filter(filterTarget, filterExpression, ctx); }*/ if (filterTarget instanceof ApplicationDescription) { rootObject.setFilterObject(((ApplicationDescription) filterTarget).getAuthGroups()); if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) { return filterTarget; } else { return null; } } else if (filterTarget instanceof List) { @SuppressWarnings("unchecked") List<Object> objectsList = (List<Object>) filterTarget; List<Object> retainList = new ArrayList<Object>(); for (Object objectToFilter : objectsList) { if (objectToFilter instanceof ApplicationDescription) { rootObject.setFilterObject(((ApplicationDescription) objectToFilter).getAuthGroups()); if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) { retainList.add(objectToFilter); } } } return retainList; } else if (filterTarget instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> returnValue = (Map<String, Object>) filterTarget; if (CloudifyConstants.SUCCESS_STATUS.equals(returnValue.get(CloudifyConstants.STATUS_KEY))) { Object responseObject = returnValue.get(CloudifyConstants.RESPONSE_KEY); if (responseObject instanceof Map) { @SuppressWarnings("unchecked") Map<String, String> objectsMap = (Map<String, String>) responseObject; Map<Object, String> retainMap = new HashMap<Object, String>(); for (Map.Entry<String, String> entry : objectsMap.entrySet()) { String filterObject = entry.getValue(); rootObject.setFilterObject(filterObject); if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) { retainMap.put(entry.getKey(), entry.getValue()); } } returnValue.put(CloudifyConstants.RESPONSE_KEY, retainMap); } else if (responseObject instanceof List) { @SuppressWarnings("unchecked") List<Object> objectsList = (List<Object>) responseObject; List<Object> retainList = new ArrayList<Object>(); for (Object object : objectsList) { if (object instanceof ApplicationDescription) { rootObject.setFilterObject(((ApplicationDescription) object).getAuthGroups()); if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) { retainList.add(object); } } } returnValue.put(CloudifyConstants.RESPONSE_KEY, retainList); } } return returnValue; } throw new IllegalArgumentException( "Filter target must be a collection, an array or an ApplicationDescription, but was " + filterTarget); } }