/*
* Copyright 2007 Ivan Dubrov
* Copyright 2007, 2008, 2009 Robin Helgelin
* Copyright 2009 Michael Gerzabek
*
* 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 nu.localhost.tapestry5.springsecurity.services;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import nu.localhost.tapestry5.springsecurity.services.internal.HttpServletRequestFilterWrapper;
import nu.localhost.tapestry5.springsecurity.services.internal.LogoutServiceImpl;
import nu.localhost.tapestry5.springsecurity.services.internal.RequestFilterWrapper;
import nu.localhost.tapestry5.springsecurity.services.internal.SaltSourceImpl;
import nu.localhost.tapestry5.springsecurity.services.internal.SecurityChecker;
import nu.localhost.tapestry5.springsecurity.services.internal.SpringSecurityExceptionTranslationFilter;
import nu.localhost.tapestry5.springsecurity.services.internal.SpringSecurityWorker;
import nu.localhost.tapestry5.springsecurity.services.internal.StaticSecurityChecker;
import nu.localhost.tapestry5.springsecurity.services.internal.T5AccessDeniedHandler;
import nu.localhost.tapestry5.springsecurity.services.internal.TapestryLogoutHandler;
import org.apache.tapestry5.ioc.Configuration;
import org.apache.tapestry5.ioc.MappedConfiguration;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.ServiceBinder;
import org.apache.tapestry5.ioc.annotations.Contribute;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.InjectService;
import org.apache.tapestry5.ioc.annotations.Marker;
import org.apache.tapestry5.ioc.annotations.Primary;
import org.apache.tapestry5.ioc.annotations.Value;
import org.apache.tapestry5.ioc.services.ServiceOverride;
import org.apache.tapestry5.services.HttpServletRequestFilter;
import org.apache.tapestry5.services.LibraryMapping;
import org.apache.tapestry5.services.RequestFilter;
import org.apache.tapestry5.services.RequestGlobals;
import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.memory.UserAttribute;
import org.springframework.security.core.userdetails.memory.UserAttributeEditor;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.util.RequestMatcher;
/**
* This module is automatically included as part of the Tapestry IoC Registry,
*
* @author Ivan Dubrov
* @author Robin Helgelin
* @author Michael Gerzabek
*/
public class SecurityModule {
@SuppressWarnings( "unchecked" )
public static void bind( final ServiceBinder binder ) {
binder.bind( LogoutService.class, LogoutServiceImpl.class ).withMarker( SpringSecurityServices.class );
binder.bind( AuthenticationTrustResolver.class, AuthenticationTrustResolverImpl.class ).withMarker(
SpringSecurityServices.class );
binder.bind( PasswordEncoder.class, PlaintextPasswordEncoder.class ).withMarker( SpringSecurityServices.class );
}
@Contribute(ServiceOverride.class)
public static void setupApplicationServiceOverrides(MappedConfiguration<Class<?>,Object> configuration,
@SpringSecurityServices SaltSourceService saltSource,
@SpringSecurityServices UsernamePasswordAuthenticationFilter authenticationProcessingFilter) {
configuration.add(SaltSourceService.class, saltSource);
configuration.add(UsernamePasswordAuthenticationFilter.class, authenticationProcessingFilter);
}
@Marker( SpringSecurityServices.class )
public static SaltSourceService buildSaltSource(
@Inject @Value( "${spring-security.password.salt}" ) final String salt ) throws Exception {
SaltSourceImpl saltSource = new SaltSourceImpl();
saltSource.setSystemWideSalt( salt );
saltSource.afterPropertiesSet();
return saltSource;
}
public static void contributeFactoryDefaults( final MappedConfiguration<String, String> configuration ) {
configuration.add( "spring-security.check.url", "/j_spring_security_check" );
configuration.add( "spring-security.failure.url", "/loginfailed" );
configuration.add( "spring-security.target.url", "/" );
configuration.add( "spring-security.afterlogout.url", "/" );
configuration.add( "spring-security.accessDenied.url", "" );
configuration.add( "spring-security.force.ssl.login", "false" );
configuration.add( "spring-security.rememberme.key", "REMEMBERMEKEY" );
configuration.add( "spring-security.loginform.url", "/loginpage" );
configuration.add( "spring-security.anonymous.key", "spring_anonymous" );
configuration.add( "spring-security.anonymous.attribute", "anonymous,ROLE_ANONYMOUS" );
configuration.add( "spring-security.password.salt", "DEADBEEF" );
configuration.add( "spring-security.always.use.target.url", "false" );
}
@Contribute(ComponentClassTransformWorker2.class)
@Primary
public static void addSpringSecurityWorker(
OrderedConfiguration<ComponentClassTransformWorker2> configuration,
SecurityChecker securityChecker ) {
configuration.add( "SpringSecurity", new SpringSecurityWorker( securityChecker ), "after:CleanupRender" );
}
public static void contributeHttpServletRequestHandler(
OrderedConfiguration<HttpServletRequestFilter> configuration,
@InjectService( "HttpSessionContextIntegrationFilter" ) HttpServletRequestFilter httpSessionContextIntegrationFilter,
@InjectService( "AuthenticationProcessingFilter" ) HttpServletRequestFilter authenticationProcessingFilter,
@InjectService( "RememberMeProcessingFilter" ) HttpServletRequestFilter rememberMeProcessingFilter,
@InjectService( "SecurityContextHolderAwareRequestFilter" ) HttpServletRequestFilter securityContextHolderAwareRequestFilter,
@InjectService( "AnonymousProcessingFilter" ) HttpServletRequestFilter anonymousProcessingFilter,
@InjectService( "FilterSecurityInterceptor" ) HttpServletRequestFilter filterSecurityInterceptor,
@InjectService( "SpringSecurityExceptionFilter" ) SpringSecurityExceptionTranslationFilter springSecurityExceptionFilter ) {
configuration.add(
"springSecurityHttpSessionContextIntegrationFilter",
httpSessionContextIntegrationFilter,
"before:ResteasyRequestFilter","before:springSecurity*" );
configuration.add( "springSecurityAuthenticationProcessingFilter", authenticationProcessingFilter );
configuration.add( "springSecurityRememberMeProcessingFilter", rememberMeProcessingFilter );
configuration.add(
"springSecuritySecurityContextHolderAwareRequestFilter",
securityContextHolderAwareRequestFilter,
"after:springSecurityRememberMeProcessingFilter" );
configuration.add(
"springSecurityAnonymousProcessingFilter",
anonymousProcessingFilter,
"after:springSecurityRememberMeProcessingFilter",
"after:springSecurityAuthenticationProcessingFilter" );
configuration.add( "springSecurityExceptionFilter", new HttpServletRequestFilterWrapper(
springSecurityExceptionFilter ), "before:springSecurityFilterSecurityInterceptor" );
configuration.add(
"springSecurityFilterSecurityInterceptor",
filterSecurityInterceptor,
"after:springSecurity*" );
}
@Marker( SpringSecurityServices.class )
public static HttpServletRequestFilter buildFilterSecurityInterceptor(
@SpringSecurityServices final AccessDecisionManager accessDecisionManager,
@SpringSecurityServices final AuthenticationManager manager,
final Collection<RequestInvocationDefinition> contributions ) throws Exception {
FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = convertCollectionToLinkedHashMap( contributions );
DefaultFilterInvocationSecurityMetadataSource source =
new DefaultFilterInvocationSecurityMetadataSource(requestMap);
interceptor.setAccessDecisionManager( accessDecisionManager );
interceptor.setAlwaysReauthenticate( false );
interceptor.setAuthenticationManager( manager );
interceptor.setSecurityMetadataSource(source);
interceptor.setValidateConfigAttributes( true );
interceptor.afterPropertiesSet();
return new HttpServletRequestFilterWrapper( interceptor );
}
static LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> convertCollectionToLinkedHashMap(
Collection<RequestInvocationDefinition> urls ) {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
for ( RequestInvocationDefinition url : urls ) {
requestMap.put( url.getRequestMatcher(), url.getConfigAttributeDefinition() );
}
return requestMap;
}
@Marker( SpringSecurityServices.class )
public static HttpServletRequestFilter buildHttpSessionContextIntegrationFilter() throws Exception {
SecurityContextPersistenceFilter filter = new SecurityContextPersistenceFilter();
filter.setForceEagerSessionCreation( false );
filter.afterPropertiesSet();
return new HttpServletRequestFilterWrapper( filter );
}
@Marker( SpringSecurityServices.class )
public static UsernamePasswordAuthenticationFilter buildRealAuthenticationProcessingFilter(
@SpringSecurityServices final AuthenticationManager manager,
@SpringSecurityServices final RememberMeServices rememberMeServices,
@Inject @Value( "${spring-security.check.url}" ) final String authUrl,
@Inject @Value( "${spring-security.target.url}" ) final String targetUrl,
@Inject @Value( "${spring-security.failure.url}" ) final String failureUrl,
@Inject @Value( "${spring-security.always.use.target.url}" ) final String alwaysUseTargetUrl ) throws Exception {
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager( manager );
filter.setPostOnly(false);
filter.setAuthenticationFailureHandler( new SimpleUrlAuthenticationFailureHandler(failureUrl) );
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setDefaultTargetUrl(targetUrl);
successHandler.setAlwaysUseDefaultTargetUrl( Boolean.parseBoolean( alwaysUseTargetUrl ) );
filter.setAuthenticationSuccessHandler( successHandler);
filter.setFilterProcessesUrl(targetUrl);
filter.setFilterProcessesUrl( authUrl );
filter.setRememberMeServices( rememberMeServices );
filter.afterPropertiesSet();
return filter;
}
@Marker( SpringSecurityServices.class )
public static HttpServletRequestFilter buildAuthenticationProcessingFilter(
final UsernamePasswordAuthenticationFilter filter ) throws Exception {
return new HttpServletRequestFilterWrapper( filter );
}
@Marker( SpringSecurityServices.class )
public static HttpServletRequestFilter buildRememberMeProcessingFilter(
@SpringSecurityServices final RememberMeServices rememberMe,
@SpringSecurityServices final AuthenticationManager authManager ) throws Exception {
final RememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter(authManager,rememberMe);
filter.afterPropertiesSet();
return new HttpServletRequestFilterWrapper( filter );
}
@Marker( SpringSecurityServices.class )
public static HttpServletRequestFilter buildSecurityContextHolderAwareRequestFilter() {
return new HttpServletRequestFilterWrapper( new SecurityContextHolderAwareRequestFilter() );
}
@Marker( SpringSecurityServices.class )
public static HttpServletRequestFilter buildAnonymousProcessingFilter(
@Inject @Value( "${spring-security.anonymous.attribute}" ) final String anonymousAttr,
@Inject @Value( "${spring-security.anonymous.key}" ) final String anonymousKey ) throws Exception {
final UserAttributeEditor attrEditor = new UserAttributeEditor();
attrEditor.setAsText( anonymousAttr );
final UserAttribute attr = (UserAttribute) attrEditor.getValue();
final AnonymousAuthenticationFilter filter = new AnonymousAuthenticationFilter(anonymousKey,"anonymousUser",attr.getAuthorities());
filter.afterPropertiesSet();
return new HttpServletRequestFilterWrapper( filter );
}
@Marker( SpringSecurityServices.class )
public static RememberMeServices build(
final UserDetailsService userDetailsService,
@Inject @Value( "${spring-security.rememberme.key}" ) final String rememberMeKey ) {
final TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(rememberMeKey,userDetailsService);
return rememberMe;
}
@Marker( SpringSecurityServices.class )
public static LogoutHandler buildRememberMeLogoutHandler(
final UserDetailsService userDetailsService,
@Inject @Value( "${spring-security.rememberme.key}" ) final String rememberMeKey ) throws Exception {
final TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(rememberMeKey,userDetailsService);
rememberMe.afterPropertiesSet();
return rememberMe;
}
public static void contributeLogoutService(
final OrderedConfiguration<LogoutHandler> cfg,
@Inject RequestGlobals globals,
@InjectService( "RememberMeLogoutHandler" ) final LogoutHandler rememberMeLogoutHandler ) {
cfg.add( "securityContextLogoutHandler", new SecurityContextLogoutHandler() );
cfg.add( "rememberMeLogoutHandler", rememberMeLogoutHandler );
cfg.add( "tapestryLogoutHandler", new TapestryLogoutHandler( globals ) ,new String[0]);
}
@Marker( SpringSecurityServices.class )
public static AuthenticationManager buildAuthenticationManager( final List<AuthenticationProvider> providers )
throws Exception {
final ProviderManager manager = new ProviderManager(providers);
manager.afterPropertiesSet();
return manager;
}
@Marker( SpringSecurityServices.class )
public final AuthenticationProvider buildAnonymousAuthenticationProvider(
@Inject @Value( "${spring-security.anonymous.key}" ) final String anonymousKey ) throws Exception {
final AnonymousAuthenticationProvider provider = new AnonymousAuthenticationProvider(anonymousKey);
provider.afterPropertiesSet();
return provider;
}
@Marker( SpringSecurityServices.class )
public final AuthenticationProvider buildRememberMeAuthenticationProvider(
@Inject @Value( "${spring-security.rememberme.key}" ) final String rememberMeKey ) throws Exception {
final RememberMeAuthenticationProvider provider = new RememberMeAuthenticationProvider(rememberMeKey);
provider.afterPropertiesSet();
return provider;
}
@Marker( SpringSecurityServices.class )
public final AuthenticationProvider buildDaoAuthenticationProvider(
final UserDetailsService userDetailsService,
final PasswordEncoder passwordEncoder,
final SaltSourceService saltSource ) throws Exception {
final DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService( userDetailsService );
provider.setPasswordEncoder( passwordEncoder );
provider.setSaltSource( saltSource );
provider.afterPropertiesSet();
return provider;
}
public final void contributeAuthenticationManager(
final OrderedConfiguration<AuthenticationProvider> configuration,
@InjectService( "AnonymousAuthenticationProvider" ) final AuthenticationProvider anonymousAuthenticationProvider,
@InjectService( "RememberMeAuthenticationProvider" ) final AuthenticationProvider rememberMeAuthenticationProvider ) {
configuration.add( "anonymousAuthenticationProvider", anonymousAuthenticationProvider );
configuration.add( "rememberMeAuthenticationProvider", rememberMeAuthenticationProvider );
}
@Marker( SpringSecurityServices.class )
public final AccessDecisionManager buildAccessDecisionManager( final List<AccessDecisionVoter> voters )
throws Exception {
final AffirmativeBased manager = new AffirmativeBased(voters);
manager.afterPropertiesSet();
return manager;
}
public final void contributeAccessDecisionManager( final OrderedConfiguration<AccessDecisionVoter> configuration ) {
configuration.add( "RoleVoter", new RoleVoter() );
}
@Marker( SpringSecurityServices.class )
public static SecurityChecker buildSecurityChecker(
@SpringSecurityServices final AccessDecisionManager accessDecisionManager,
@SpringSecurityServices final AuthenticationManager authenticationManager ) throws Exception {
final StaticSecurityChecker checker = new StaticSecurityChecker();
checker.setAccessDecisionManager( accessDecisionManager );
checker.setAuthenticationManager( authenticationManager );
checker.afterPropertiesSet();
return checker;
}
@Marker( SpringSecurityServices.class )
public static AuthenticationEntryPoint buildAuthenticationEntryPoint(
@Inject @Value( "${spring-security.loginform.url}" ) final String loginFormUrl,
@Inject @Value( "${spring-security.force.ssl.login}" ) final String forceHttps ) throws Exception {
final LoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint(loginFormUrl);
entryPoint.afterPropertiesSet();
boolean forceSSL = Boolean.parseBoolean( forceHttps );
entryPoint.setForceHttps( forceSSL );
return entryPoint;
}
public static SpringSecurityExceptionTranslationFilter buildSpringSecurityExceptionFilter(
final AuthenticationEntryPoint aep,
@Inject @Value( "${spring-security.accessDenied.url}" ) final String accessDeniedUrl ) throws Exception {
SpringSecurityExceptionTranslationFilter filter = new SpringSecurityExceptionTranslationFilter();
filter.setAuthenticationEntryPoint( aep );
if ( !accessDeniedUrl.equals( "" ) ) {
T5AccessDeniedHandler accessDeniedHandler = new T5AccessDeniedHandler();
accessDeniedHandler.setErrorPage( accessDeniedUrl );
filter.setAccessDeniedHandler( accessDeniedHandler );
}
filter.afterPropertiesSet();
return filter;
}
public static void contributeRequestHandler(
final OrderedConfiguration<RequestFilter> configuration,
final RequestGlobals globals,
@InjectService( "SpringSecurityExceptionFilter" ) final SpringSecurityExceptionTranslationFilter springSecurityExceptionFilter ) {
configuration.add( "SpringSecurityExceptionFilter", new RequestFilterWrapper(
globals,
springSecurityExceptionFilter ), "after:ErrorFilter" );
}
// Contribute three aspects of module: presentation, entities and
// configuration
public static void contributeComponentClassResolver( final Configuration<LibraryMapping> configuration ) {
configuration.add( new LibraryMapping( "security", "nu.localhost.tapestry5.springsecurity" ) );
}
}