/*
* Copyright 2002-2011 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.flex.security3;
import java.beans.PropertyDescriptor;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.flex.core.ExceptionTranslator;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import flex.messaging.FlexSession;
/**
* Infrastructure class for setting up the necessary integration points with Spring Security.
*
* <p>Given existing Spring Security configuration for implementations of {@link SessionAuthenticationStrategy}, {@link RememberMeServices},
* will configure the {@link SpringSecurityLoginCommand} and {@link FilterChainProxy} to work with those services as necessary. An existing
* <code>SessionAuthentiactionStrategy</code> will be wrapped with a {@link FlexSessionAwareSessionAuthenticationStrategy} in order to ensure
* proper handling of the {@link FlexSession}.
*
* <p>This class will be configured automatically through use of the XML namespace tags.
*
* @author Jeremy Grelle
*/
public class SecurityConfigurationPostProcessor implements MergedBeanDefinitionPostProcessor, InitializingBean, ApplicationContextAware {
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
private RememberMeServices rememberMeServices;
private FilterChainProxy filterChainProxy;
private ApplicationContext context;
@SuppressWarnings({ "rawtypes", "unchecked" })
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
if (SpringSecurityLoginCommand.class.isAssignableFrom(beanType)) {
MutablePropertyValues pv = beanDefinition.getPropertyValues();
boolean rememberMeServicesConfigured = (pv.getPropertyValue("rememberMeServices") != null);
if (this.sessionAuthenticationStrategy != null && pv.getPropertyValue("sessionAuthenticationStrategy") == null) {
pv.add("sessionAuthenticationStrategy", this.sessionAuthenticationStrategy);
}
if (this.rememberMeServices != null && !rememberMeServicesConfigured) {
pv.add("rememberMeServices", this.rememberMeServices);
}
if (pv.getPropertyValue("logoutHandlers") == null) {
ManagedList handlers = new ManagedList();
SecurityContextLogoutHandler contextHandler = new SecurityContextLogoutHandler();
boolean invalidateHttpSession = (Boolean) beanDefinition.getAttribute("invalidateHttpSession");
contextHandler.setInvalidateHttpSession(invalidateHttpSession);
handlers.add(contextHandler);
if (this.rememberMeServices != null && !rememberMeServicesConfigured && ClassUtils.isAssignableValue(LogoutHandler.class, this.rememberMeServices)) {
handlers.add(this.rememberMeServices);
}
pv.add("logoutHandlers", handlers);
}
}
}
public void afterPropertiesSet() throws Exception {
if (this.sessionAuthenticationStrategy != null) {
this.sessionAuthenticationStrategy = new FlexSessionAwareSessionAuthenticationStrategy(this.sessionAuthenticationStrategy);
Set<Filter> allFilters = new HashSet<Filter>(BeanFactoryUtils.beansOfTypeIncludingAncestors(context, Filter.class, false, false).values());
if (this.filterChainProxy != null) {
allFilters.addAll(new FilterChainAccessor(this.filterChainProxy).getFilters());
}
for (Filter filter : allFilters) {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(filter);
for (PropertyDescriptor pd : bw.getPropertyDescriptors()) {
if (ClassUtils.isAssignableValue(pd.getPropertyType(), this.sessionAuthenticationStrategy) && bw.isWritableProperty(pd.getName())) {
bw.setPropertyValue(pd.getName(), this.sessionAuthenticationStrategy);
}
}
}
}
Map<String, ExceptionTranslator> exceptionTranslators = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ExceptionTranslator.class);
if (!CollectionUtils.isEmpty(exceptionTranslators)) {
Map<String, FlexAuthenticationEntryPoint> entryPoints = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, FlexAuthenticationEntryPoint.class);
for (FlexAuthenticationEntryPoint entryPoint: entryPoints.values()) {
if (CollectionUtils.isEmpty(entryPoint.getExceptionTranslators())) {
entryPoint.setExceptionTranslators(new HashSet<ExceptionTranslator>(exceptionTranslators.values()));
}
}
}
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
public void setFilterChainProxy(FilterChainProxy filterChainProxy) {
this.filterChainProxy = filterChainProxy;
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
public void setSessionAuthenticationStrategy(
SessionAuthenticationStrategy sessionAuthenticationStrategy) {
this.sessionAuthenticationStrategy = sessionAuthenticationStrategy;
}
private static final class FilterChainAccessor {
private final Set<Filter> filters;
public FilterChainAccessor(FilterChainProxy proxy) {
this.filters = new LinkedHashSet<Filter>();
for (List<Filter> filters : proxy.getFilterChainMap().values()) {
this.filters.addAll(filters);
}
}
public Set<Filter> getFilters() {
return this.filters;
}
}
}