/* * Copyright 2004-2015 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 * * 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.springframework.webflow.security; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.beans.DirectFieldAccessor; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.vote.AbstractAccessDecisionManager; import org.springframework.security.access.vote.AffirmativeBased; import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.access.vote.UnanimousBased; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.ClassUtils; import org.springframework.webflow.definition.FlowDefinition; import org.springframework.webflow.definition.StateDefinition; import org.springframework.webflow.definition.TransitionDefinition; import org.springframework.webflow.execution.EnterStateVetoException; import org.springframework.webflow.execution.FlowExecutionListenerAdapter; import org.springframework.webflow.execution.RequestContext; /** * Flow security integration with Spring Security * * @author Scott Andrews */ public class SecurityFlowExecutionListener extends FlowExecutionListenerAdapter { private static final boolean SPRING_SECURITY_3_PRESENT = ClassUtils.hasConstructor(AffirmativeBased.class); private AccessDecisionManager accessDecisionManager; /** * Get the access decision manager that makes flow authorization decisions. * @return the decision manager */ public AccessDecisionManager getAccessDecisionManager() { return this.accessDecisionManager; } /** * Set the access decision manager that makes flow authorization decisions. * @param accessDecisionManager the decision manager to user */ public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) { this.accessDecisionManager = accessDecisionManager; } public void sessionCreating(RequestContext context, FlowDefinition definition) { SecurityRule rule = (SecurityRule) definition.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME); if (rule != null) { decide(rule, definition); } } public void stateEntering(RequestContext context, StateDefinition state) throws EnterStateVetoException { SecurityRule rule = (SecurityRule) state.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME); if (rule != null) { decide(rule, state); } } public void transitionExecuting(RequestContext context, TransitionDefinition transition) { SecurityRule rule = (SecurityRule) transition.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME); if (rule != null) { decide(rule, transition); } } /** * Performs a Spring Security authorization decision. Decision will use the provided AccessDecisionManager. If no * AccessDecisionManager is provided a role based manager will be selected according to the comparison type of the * rule. * @param rule the rule to base the decision * @param object the execution listener phase */ protected void decide(SecurityRule rule, Object object) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Collection<ConfigAttribute> configAttributes = getConfigAttributes(rule); if (accessDecisionManager != null) { accessDecisionManager.decide(authentication, object, configAttributes); } else { AccessDecisionManager manager = (SPRING_SECURITY_3_PRESENT ? createManagerWithSpringSecurity3(rule) : createManager(rule)); manager.decide(authentication, object, configAttributes); } } private AbstractAccessDecisionManager createManager(SecurityRule rule) { List<AccessDecisionVoter<? extends Object>> voters = new ArrayList<AccessDecisionVoter<? extends Object>>(); voters.add(new RoleVoter()); if (rule.getComparisonType() == SecurityRule.COMPARISON_ANY) { return new AffirmativeBased(voters); } else if (rule.getComparisonType() == SecurityRule.COMPARISON_ALL) { return new UnanimousBased(voters); } else { throw new IllegalStateException("Unknown SecurityRule match type: " + rule.getComparisonType()); } } private AbstractAccessDecisionManager createManagerWithSpringSecurity3(SecurityRule rule) { List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>(); voters.add(new RoleVoter()); Class<?> managerType; if (rule.getComparisonType() == SecurityRule.COMPARISON_ANY) { managerType = AffirmativeBased.class; } else if (rule.getComparisonType() == SecurityRule.COMPARISON_ALL) { managerType = UnanimousBased.class; } else { throw new IllegalStateException("Unknown SecurityRule match type: " + rule.getComparisonType()); } try { Constructor<?> constructor = managerType.getConstructor(); AbstractAccessDecisionManager manager = (AbstractAccessDecisionManager) constructor.newInstance(); new DirectFieldAccessor(manager).setPropertyValue("decisionVoters", voters); return manager; } catch (Throwable ex) { throw new IllegalStateException("Failed to initialize AccessDecisionManager", ex); } } /** * Convert SecurityRule into a form understood by Spring Security * @param rule the rule to convert * @return list of ConfigAttributes for Spring Security */ protected Collection<ConfigAttribute> getConfigAttributes(SecurityRule rule) { List<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>(); for (String attribute : rule.getAttributes()) { configAttributes.add(new SecurityConfig(attribute)); } return configAttributes; } }