View Javadoc
1   package com.github.choonchernlim.security.adfs.saml2;
2   
3   import static com.github.choonchernlim.betterPreconditions.preconditions.PreconditionFactory.expect;
4   import com.google.common.collect.ImmutableList;
5   import com.google.common.collect.ImmutableMap;
6   import org.apache.commons.httpclient.HttpClient;
7   import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
8   import org.apache.commons.httpclient.protocol.Protocol;
9   import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
10  import org.apache.velocity.app.VelocityEngine;
11  import org.opensaml.common.xml.SAMLConstants;
12  import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider;
13  import org.opensaml.saml2.metadata.provider.MetadataProvider;
14  import org.opensaml.saml2.metadata.provider.MetadataProviderException;
15  import org.opensaml.xml.parse.StaticBasicParserPool;
16  import org.springframework.beans.factory.annotation.Autowired;
17  import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
18  import org.springframework.context.annotation.Bean;
19  import org.springframework.core.env.Environment;
20  import org.springframework.security.authentication.AuthenticationManager;
21  import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
22  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
23  import org.springframework.security.config.annotation.web.builders.WebSecurity;
24  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
25  import org.springframework.security.core.userdetails.User;
26  import org.springframework.security.saml.SAMLAuthenticationProvider;
27  import org.springframework.security.saml.SAMLBootstrap;
28  import org.springframework.security.saml.SAMLDiscovery;
29  import org.springframework.security.saml.SAMLEntryPoint;
30  import org.springframework.security.saml.SAMLLogoutFilter;
31  import org.springframework.security.saml.SAMLLogoutProcessingFilter;
32  import org.springframework.security.saml.SAMLProcessingFilter;
33  import org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter;
34  import org.springframework.security.saml.context.SAMLContextProviderLB;
35  import org.springframework.security.saml.key.JKSKeyManager;
36  import org.springframework.security.saml.key.KeyManager;
37  import org.springframework.security.saml.log.SAMLDefaultLogger;
38  import org.springframework.security.saml.metadata.CachingMetadataManager;
39  import org.springframework.security.saml.metadata.ExtendedMetadataDelegate;
40  import org.springframework.security.saml.metadata.MetadataDisplayFilter;
41  import org.springframework.security.saml.metadata.MetadataGenerator;
42  import org.springframework.security.saml.metadata.MetadataGeneratorFilter;
43  import org.springframework.security.saml.parser.ParserPoolHolder;
44  import org.springframework.security.saml.processor.HTTPArtifactBinding;
45  import org.springframework.security.saml.processor.HTTPPAOS11Binding;
46  import org.springframework.security.saml.processor.HTTPPostBinding;
47  import org.springframework.security.saml.processor.HTTPRedirectDeflateBinding;
48  import org.springframework.security.saml.processor.HTTPSOAP11Binding;
49  import org.springframework.security.saml.processor.SAMLBinding;
50  import org.springframework.security.saml.processor.SAMLProcessorImpl;
51  import org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer;
52  import org.springframework.security.saml.trust.httpclient.TLSProtocolSocketFactory;
53  import org.springframework.security.saml.userdetails.SAMLUserDetailsService;
54  import org.springframework.security.saml.util.VelocityFactory;
55  import org.springframework.security.saml.websso.ArtifactResolutionProfileImpl;
56  import org.springframework.security.saml.websso.SingleLogoutProfile;
57  import org.springframework.security.saml.websso.SingleLogoutProfileImpl;
58  import org.springframework.security.saml.websso.WebSSOProfile;
59  import org.springframework.security.saml.websso.WebSSOProfileConsumer;
60  import org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl;
61  import org.springframework.security.saml.websso.WebSSOProfileConsumerImpl;
62  import org.springframework.security.saml.websso.WebSSOProfileECPImpl;
63  import org.springframework.security.saml.websso.WebSSOProfileImpl;
64  import org.springframework.security.saml.websso.WebSSOProfileOptions;
65  import org.springframework.security.web.DefaultSecurityFilterChain;
66  import org.springframework.security.web.FilterChainProxy;
67  import org.springframework.security.web.SecurityFilterChain;
68  import org.springframework.security.web.access.channel.ChannelProcessingFilter;
69  import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
70  import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
71  import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
72  import org.springframework.security.web.authentication.logout.LogoutHandler;
73  import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
74  import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
75  import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
76  import org.springframework.security.web.csrf.CsrfFilter;
77  import org.springframework.security.web.csrf.CsrfTokenRepository;
78  import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
79  import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
80  
81  import javax.annotation.PostConstruct;
82  import java.util.Timer;
83  
84  /**
85   * Spring Security configuration to authenticate against ADFS using SAML protocol.
86   * This class should be extended by Sp's Java-based Spring configuration for web security.
87   */
88  public abstract class SAMLWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
89  
90      /**
91       * Provides an opportunity for child class to access Spring environment, if needed.
92       */
93      @Autowired
94      protected Environment env;
95  
96      @Autowired
97      private SAMLAuthenticationProvider samlAuthenticationProvider;
98  
99      // Initialization of OpenSAML library, must be static to prevent "ObjectPostProcessor is a required bean" exception
100     // By default, Spring Security SAML uses SHA-1. So, use `DefaultSAMLBootstrap` to use SHA-256.
101     @Bean
102     public static SAMLBootstrap samlBootstrap() {
103         return new DefaultSAMLBootstrap();
104     }
105 
106     /**
107      * Sp's SAMLConfigBean to further customize this security configuration.
108      *
109      * @return SAML config bean
110      */
111     @Bean
112     protected abstract SAMLConfigBean samlConfigBean();
113 
114     /**
115      * Fluent API that pre-configures HttpSecurity with SAML specific configuration.
116      *
117      * @param http HttpSecurity instance
118      * @return Same HttpSecurity instance
119      * @throws Exception Exception
120      */
121     // CSRF must be disabled when processing /saml/** to prevent "Expected CSRF token not found" exception.
122     // See: http://stackoverflow.com/questions/26508835/spring-saml-extension-and-spring-security-csrf-protection-conflict/26560447
123     protected final HttpSecurity samlizedConfig(final HttpSecurity http) throws Exception {
124         http.httpBasic().authenticationEntryPoint(samlEntryPoint())
125                 .and()
126                 .csrf().ignoringAntMatchers("/saml/**")
127                 .and()
128                 .authorizeRequests().antMatchers("/saml/**").permitAll()
129                 .and()
130                 .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
131                 .addFilterAfter(filterChainProxy(), BasicAuthenticationFilter.class);
132 
133         // store CSRF token in cookie
134         if (samlConfigBean().getStoreCsrfTokenInCookie()) {
135             http.csrf()
136                     .csrfTokenRepository(csrfTokenRepository())
137                     .and()
138                     .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
139         }
140 
141         return http;
142     }
143 
144     /**
145      * Configure CSRF token repository to accept CSRF token from AngularJS friendly header.
146      *
147      * @return CsrfTokenRepository
148      */
149     private CsrfTokenRepository csrfTokenRepository() {
150         HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
151         repository.setHeaderName(CsrfHeaderFilter.HEADER_NAME);
152         return repository;
153     }
154 
155 
156     /**
157      * Mocks security by hardcoding a given user so that it will always appear that user is accessing the protected
158      * resources. This is useful to allow developer to bypass any web authentication against ADFS during rapid
159      * app development.
160      *
161      * @param http HttpSecurity instance
162      * @param user User instance
163      * @return HttpSecurity that will never authenticate against ADFS
164      */
165     protected final HttpSecurity mockSecurity(final HttpSecurity http, final User user) {
166         expect(user, "user").not().toBeNull().check();
167 
168         if (samlConfigBean().getSamlUserDetailsService() == null) {
169             throw new SpringSecurityAdfsSaml2Exception(
170                     "`samlConfigBean.samlUserDetailsService` cannot be null. " +
171                     "When mocking security, the given user details object will be set as principal. " +
172                     "Because setting `samlConfigBean.samlUserDetailsService` will set the user details object as principal, " +
173                     "this property must be configured to ensure the mock security mimics the actual security configuration."
174             );
175         }
176 
177         return http.addFilterBefore(new MockFilterSecurityInterceptor(user), FilterSecurityInterceptor.class);
178     }
179 
180     /**
181      * Fluent API that pre-configures WebSecurity with SAML specific configuration.
182      *
183      * @param web WebSecurity instance
184      * @return Same WebSecurity instance
185      * @throws Exception Exception
186      */
187     protected final WebSecurity samlizedConfig(final WebSecurity web) throws Exception {
188         web.ignoring().antMatchers(samlConfigBean().getSuccessLogoutUrl());
189         return web;
190     }
191 
192     // IDP metadata URL
193     private String getMetdataUrl() {
194         return String.format("https://%s/federationmetadata/2007-06/federationmetadata.xml",
195                              samlConfigBean().getIdpServerName());
196     }
197 
198     // Entry point to initialize authentication
199     @Bean
200     public SAMLEntryPoint samlEntryPoint() {
201         SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint();
202         samlEntryPoint.setDefaultProfileOptions(webSSOProfileOptions());
203         return samlEntryPoint;
204     }
205 
206     /**
207      * Customizing SAML request message to be sent to the IDP.
208      *
209      * @return WebSSOProfileOptions
210      */
211     @Bean
212     public WebSSOProfileOptions webSSOProfileOptions() {
213         WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions();
214 
215         // Disable element scoping when sending requests to IdP to prevent
216         // "Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null"
217         // exception
218         webSSOProfileOptions.setIncludeScoping(false);
219 
220         // Always use HTTP-Redirect instead of HTTP-Post, although both works with ADFS
221         webSSOProfileOptions.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
222 
223         // Force IdP to re-authenticate user if issued token is too old to prevent
224         // "Authentication statement is too old to be used with value" exception
225         // See: http://stackoverflow.com/questions/30528636/saml-login-errors
226         webSSOProfileOptions.setForceAuthN(true);
227 
228         // Determine what authentication method to use (WIA, user/password, etc).
229         // If not set, it will use authentication method order defined by IdP
230         if (!samlConfigBean().getAuthnContexts().isEmpty()) {
231             webSSOProfileOptions.setAuthnContexts(samlConfigBean().getAuthnContexts());
232         }
233 
234         return webSSOProfileOptions;
235     }
236 
237     // Filter automatically generates default SP metadata
238     @Bean
239     public MetadataGeneratorFilter metadataGeneratorFilter() {
240         // generates base URL that matches `SAMLContextProviderLB` configuration
241         // to ensure SAML endpoints work for server doing SSL termination
242         StringBuilder sb = new StringBuilder();
243         sb.append("https://").append(samlConfigBean().getSpServerName());
244         if (samlConfigBean().getSpHttpsPort() != 443) {
245             sb.append(":").append(samlConfigBean().getSpHttpsPort());
246         }
247         sb.append(samlConfigBean().getSpContextPath());
248         String entityBaseUrl = sb.toString();
249 
250         MetadataGenerator metadataGenerator = new MetadataGenerator();
251         metadataGenerator.setKeyManager(keyManager());
252         metadataGenerator.setEntityBaseURL(entityBaseUrl);
253         return new MetadataGeneratorFilter(metadataGenerator);
254     }
255 
256     // HTTP client
257     @Bean
258     public HttpClient httpClient() {
259         return new HttpClient(new MultiThreadedHttpConnectionManager());
260     }
261 
262     // Filters for processing of SAML messages
263     @Bean
264     public FilterChainProxy filterChainProxy() throws Exception {
265         //@formatter:off
266         return new FilterChainProxy(ImmutableList.<SecurityFilterChain>of(
267                 new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"), samlEntryPoint()),
268                 new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"), samlLogoutFilter()),
269                 new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"), metadataDisplayFilter()),
270                 new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"), samlProcessingFilter()),
271                 new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"), samlWebSSOHoKProcessingFilter()),
272                 new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"), samlLogoutProcessingFilter()),
273                 new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"), samlIDPDiscovery())
274         ));
275         //@formatter:on
276     }
277 
278     // Handler deciding where to redirect user after successful login
279     @Bean
280     public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {
281         SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler = new SavedRequestAwareAuthenticationSuccessHandler();
282         successRedirectHandler.setDefaultTargetUrl(samlConfigBean().getSuccessLoginDefaultUrl());
283         return successRedirectHandler;
284     }
285 
286     // Handler deciding where to redirect user after failed login
287     @Bean
288     public SimpleUrlAuthenticationFailureHandler failureRedirectHandler() {
289         SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
290 
291         // The precondition on `setDefaultFailureUrl(..)` will cause an exception if the value is null.
292         // So, only set this value if it is not null
293         if (!samlConfigBean().getFailedLoginDefaultUrl().isEmpty()) {
294             failureHandler.setDefaultFailureUrl(samlConfigBean().getFailedLoginDefaultUrl());
295         }
296 
297         return failureHandler;
298     }
299 
300     // Handler for successful logout
301     @Bean
302     public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
303         SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
304         successLogoutHandler.setDefaultTargetUrl(samlConfigBean().getSuccessLogoutUrl());
305         return successLogoutHandler;
306     }
307 
308     // Authentication manager
309     @Bean
310     @Override
311     public AuthenticationManager authenticationManagerBean() throws Exception {
312         return super.authenticationManagerBean();
313     }
314 
315     // Register authentication manager for SAML provider
316     @Override
317     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
318         auth.authenticationProvider(samlAuthenticationProvider);
319     }
320 
321     // Logger for SAML messages and events
322     @Bean
323     public SAMLDefaultLogger samlLogger() {
324         return new SAMLDefaultLogger();
325     }
326 
327     // Central storage of cryptographic keys
328     @Bean
329     public KeyManager keyManager() {
330         return new JKSKeyManager(samlConfigBean().getKeystoreResource(),
331                                  samlConfigBean().getKeystorePassword(),
332                                  ImmutableMap.of(samlConfigBean().getKeystoreAlias(),
333                                                  samlConfigBean().getKeystorePrivateKeyPassword()),
334                                  samlConfigBean().getKeystoreAlias());
335     }
336 
337     // IDP Discovery service
338     @Bean
339     public SAMLDiscovery samlIDPDiscovery() {
340         return new SAMLDiscovery();
341     }
342 
343     // The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there
344     @Bean
345     public MetadataDisplayFilter metadataDisplayFilter() {
346         return new MetadataDisplayFilter();
347     }
348 
349     // Configure HTTP Client to accept certificates from the keystore instead of JDK keystore  for HTTPS verification
350     @Bean
351     public TLSProtocolConfigurer tlsProtocolConfigurer() {
352         return new TLSProtocolConfigurer();
353     }
354 
355     // Configure TLSProtocolConfigurer
356     @Bean
357     public ProtocolSocketFactory protocolSocketFactory() {
358         return new TLSProtocolSocketFactory(keyManager(), null, "default");
359     }
360 
361     // Configure TLSProtocolConfigurer
362     @Bean
363     public Protocol protocol() {
364         return new Protocol("https", protocolSocketFactory(), 443);
365     }
366 
367     // Configure TLSProtocolConfigurer
368     @PostConstruct
369     public MethodInvokingFactoryBean socketFactoryInitialization() {
370         MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
371         methodInvokingFactoryBean.setTargetClass(Protocol.class);
372         methodInvokingFactoryBean.setTargetMethod("registerProtocol");
373         methodInvokingFactoryBean.setArguments(new Object[]{"https", protocol()});
374         return methodInvokingFactoryBean;
375     }
376 
377     // IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here
378     @Bean
379     public CachingMetadataManager metadata() throws MetadataProviderException {
380         HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(new Timer(true),
381                                                                              httpClient(),
382                                                                              getMetdataUrl());
383         httpMetadataProvider.setParserPool(parserPool());
384 
385         ExtendedMetadataDelegate extendedMetadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider);
386         // Disable metadata trust check to prevent "Signature trust establishment failed for metadata entry" exception
387         extendedMetadataDelegate.setMetadataTrustCheck(false);
388 
389         return new CachingMetadataManager(ImmutableList.<MetadataProvider>of(extendedMetadataDelegate));
390     }
391 
392     // SAML Authentication Provider responsible for validating of received SAML messages
393     @Bean
394     public SAMLAuthenticationProvider samlAuthenticationProvider() {
395         SAMLAuthenticationProvider samlAuthenticationProvider = new SAMLAuthenticationProvider();
396         SAMLUserDetailsService samlUserDetailsService = samlConfigBean().getSamlUserDetailsService();
397 
398         if (samlUserDetailsService != null) {
399             samlAuthenticationProvider.setUserDetails(samlUserDetailsService);
400 
401             // By default, `principal` is always going to be `NameID` even though the `Authentication` object
402             // contain `userDetails` object. So, if `userDetails` is provided, then don't force `principal` as
403             // string so that `principal` represents `userDetails` object.
404             // See: http://stackoverflow.com/questions/33786861/how-to-override-the-nameid-value-in-samlauthenticationprovider
405             samlAuthenticationProvider.setForcePrincipalAsString(false);
406         }
407 
408         return samlAuthenticationProvider;
409     }
410 
411     // In order to get SAML to work for Sp servers doing SSL termination, `SAMLContextProviderLB` has
412     // to be used instead of `SAMLContextProviderImpl` to prevent the following exception:-
413     //
414     // "SAML message intended destination endpoint 'https://server/app/saml/SSO' did not match the
415     // recipient endpoint 'http://server/app/saml/SSO'"
416     //
417     // This configuration will work for Sp servers (not) doing SSL termination.
418     @Bean
419     public SAMLContextProviderLB contextProvider() {
420         SAMLContextProviderLB contextProviderLB = new SAMLContextProviderLB();
421         contextProviderLB.setScheme("https");
422         contextProviderLB.setServerName(samlConfigBean().getSpServerName());
423         contextProviderLB.setServerPort(samlConfigBean().getSpHttpsPort());
424         contextProviderLB.setIncludeServerPortInRequestURL(samlConfigBean().getSpHttpsPort() != 443);
425         contextProviderLB.setContextPath(samlConfigBean().getSpContextPath());
426         return contextProviderLB;
427     }
428 
429     // Processing filter for WebSSO profile messages
430     @Bean
431     public SAMLProcessingFilter samlProcessingFilter() throws Exception {
432         SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter();
433         samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager());
434         samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
435         samlWebSSOProcessingFilter.setAuthenticationFailureHandler(failureRedirectHandler());
436         return samlWebSSOProcessingFilter;
437     }
438 
439     // Processing filter for WebSSO Holder-of-Key profile
440     @Bean
441     public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception {
442         SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter();
443         samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
444         samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationManager());
445         samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(failureRedirectHandler());
446         return samlWebSSOHoKProcessingFilter;
447     }
448 
449     // Logout handler terminating local session
450     @Bean
451     public SecurityContextLogoutHandler logoutHandler() {
452         SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();
453         logoutHandler.setInvalidateHttpSession(true);
454         logoutHandler.setClearAuthentication(true);
455         return logoutHandler;
456     }
457 
458     // Override default logout processing filter with the one processing SAML messages
459     @Bean
460     public SAMLLogoutFilter samlLogoutFilter() {
461         return new SAMLLogoutFilter(successLogoutHandler(),
462                                     new LogoutHandler[]{logoutHandler()},
463                                     new LogoutHandler[]{logoutHandler()});
464     }
465 
466     // Filter processing incoming logout messages
467     // First argument determines URL user will be redirected to after successful global logout
468     @Bean
469     public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() {
470         return new SAMLLogoutProcessingFilter(successLogoutHandler(), logoutHandler());
471     }
472 
473     // Class loading incoming SAML messages from httpRequest stream
474     @Bean
475     public SAMLProcessorImpl processor() {
476         return new SAMLProcessorImpl(ImmutableList.<SAMLBinding>of(redirectDeflateBinding(),
477                                                                    postBinding(),
478                                                                    artifactBinding(),
479                                                                    soapBinding(),
480                                                                    paosBinding()));
481     }
482 
483     // SAML 2.0 WebSSO Assertion Consumer
484     @Bean
485     public WebSSOProfileConsumer webSSOprofileConsumer() {
486         return new WebSSOProfileConsumerImpl();
487     }
488 
489     // SAML 2.0 Holder-of-Key WebSSO Assertion Consumer
490     @Bean
491     public WebSSOProfileConsumerHoKImpl hokWebSSOprofileConsumer() {
492         return new WebSSOProfileConsumerHoKImpl();
493     }
494 
495     // SAML 2.0 Web SSO profile
496     @Bean
497     public WebSSOProfile webSSOprofile() {
498         return new WebSSOProfileImpl();
499     }
500 
501     // SAML 2.0 Holder-of-Key Web SSO profile
502     @Bean
503     public WebSSOProfileConsumerHoKImpl hokWebSSOProfile() {
504         return new WebSSOProfileConsumerHoKImpl();
505     }
506 
507     // SAML 2.0 ECP profile
508     @Bean
509     public WebSSOProfileECPImpl ecpprofile() {
510         return new WebSSOProfileECPImpl();
511     }
512 
513     // SAML 2.0 Logout profile
514     @Bean
515     public SingleLogoutProfile logoutprofile() {
516         return new SingleLogoutProfileImpl();
517     }
518 
519     // Bindings, encoders and decoders used for creating and parsing messages
520     @Bean
521     public HTTPPostBinding postBinding() {
522         return new HTTPPostBinding(parserPool(), velocityEngine());
523     }
524 
525     @Bean
526     public HTTPRedirectDeflateBinding redirectDeflateBinding() {
527         return new HTTPRedirectDeflateBinding(parserPool());
528     }
529 
530     @Bean
531     public HTTPArtifactBinding artifactBinding() {
532         ArtifactResolutionProfileImpl artifactResolutionProfile = new ArtifactResolutionProfileImpl(httpClient());
533         artifactResolutionProfile.setProcessor(new SAMLProcessorImpl(soapBinding()));
534         return new HTTPArtifactBinding(parserPool(), velocityEngine(), artifactResolutionProfile);
535     }
536 
537     @Bean
538     public HTTPSOAP11Binding soapBinding() {
539         return new HTTPSOAP11Binding(parserPool());
540     }
541 
542     @Bean
543     public HTTPPAOS11Binding paosBinding() {
544         return new HTTPPAOS11Binding(parserPool());
545     }
546 
547     // Initialization of the velocity engine
548     @Bean
549     public VelocityEngine velocityEngine() {
550         return VelocityFactory.getEngine();
551     }
552 
553     // XML parser pool needed for OpenSAML parsing
554     @Bean(initMethod = "initialize")
555     public StaticBasicParserPool parserPool() {
556         return new StaticBasicParserPool();
557     }
558 
559     @Bean
560     public ParserPoolHolder parserPoolHolder() {
561         return new ParserPoolHolder();
562     }
563 }