Java tutorial
/** * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2018 * * 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 it.infn.mw.iam.config.saml; import static com.google.common.collect.Sets.newHashSet; import static it.infn.mw.iam.authn.saml.util.Saml2Attribute.EPPN; import static it.infn.mw.iam.authn.saml.util.Saml2Attribute.EPTID; import static it.infn.mw.iam.authn.saml.util.Saml2Attribute.EPUID; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.concurrent.TimeUnit; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.protocol.Protocol; import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import org.apache.velocity.app.VelocityEngine; import org.opensaml.saml2.core.NameIDType; import org.opensaml.saml2.metadata.provider.FileBackedHTTPMetadataProvider; import org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataFilter; import org.opensaml.saml2.metadata.provider.MetadataFilterChain; import org.opensaml.saml2.metadata.provider.MetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataProviderException; import org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider; import org.opensaml.util.resource.ClasspathResource; import org.opensaml.util.resource.ResourceException; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.parse.ParserPool; import org.opensaml.xml.parse.StaticBasicParserPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.saml.SAMLAuthenticationProvider; import org.springframework.security.saml.SAMLBootstrap; import org.springframework.security.saml.SAMLEntryPoint; import org.springframework.security.saml.SAMLLogoutFilter; import org.springframework.security.saml.SAMLLogoutProcessingFilter; import org.springframework.security.saml.SAMLProcessingFilter; import org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter; import org.springframework.security.saml.context.SAMLContextProvider; import org.springframework.security.saml.context.SAMLContextProviderImpl; import org.springframework.security.saml.context.SAMLContextProviderLB; import org.springframework.security.saml.key.JKSKeyManager; import org.springframework.security.saml.key.KeyManager; import org.springframework.security.saml.log.SAMLDefaultLogger; import org.springframework.security.saml.metadata.CachingMetadataManager; import org.springframework.security.saml.metadata.ExtendedMetadata; import org.springframework.security.saml.metadata.MetadataDisplayFilter; import org.springframework.security.saml.metadata.MetadataGenerator; import org.springframework.security.saml.metadata.MetadataGeneratorFilter; import org.springframework.security.saml.parser.ParserPoolHolder; import org.springframework.security.saml.processor.HTTPArtifactBinding; import org.springframework.security.saml.processor.HTTPPAOS11Binding; import org.springframework.security.saml.processor.HTTPPostBinding; import org.springframework.security.saml.processor.HTTPRedirectDeflateBinding; import org.springframework.security.saml.processor.HTTPSOAP11Binding; import org.springframework.security.saml.processor.SAMLBinding; import org.springframework.security.saml.processor.SAMLProcessor; import org.springframework.security.saml.processor.SAMLProcessorImpl; import org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer; import org.springframework.security.saml.trust.httpclient.TLSProtocolSocketFactory; import org.springframework.security.saml.userdetails.SAMLUserDetailsService; import org.springframework.security.saml.websso.ArtifactResolutionProfile; import org.springframework.security.saml.websso.ArtifactResolutionProfileImpl; import org.springframework.security.saml.websso.SingleLogoutProfile; import org.springframework.security.saml.websso.SingleLogoutProfileImpl; import org.springframework.security.saml.websso.WebSSOProfile; import org.springframework.security.saml.websso.WebSSOProfileConsumer; import org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl; import org.springframework.security.saml.websso.WebSSOProfileConsumerImpl; import org.springframework.security.saml.websso.WebSSOProfileImpl; import org.springframework.security.saml.websso.WebSSOProfileOptions; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.access.channel.ChannelProcessingFilter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import com.google.common.base.Strings; import it.infn.mw.iam.api.account.AccountUtils; import it.infn.mw.iam.api.aup.AUPSignatureCheckService; import it.infn.mw.iam.authn.EnforceAupSignatureSuccessHandler; import it.infn.mw.iam.authn.ExternalAuthenticationFailureHandler; import it.infn.mw.iam.authn.ExternalAuthenticationSuccessHandler; import it.infn.mw.iam.authn.InactiveAccountAuthenticationHander; import it.infn.mw.iam.authn.RootIsDashboardSuccessHandler; import it.infn.mw.iam.authn.saml.CleanInactiveProvisionedAccounts; import it.infn.mw.iam.authn.saml.DefaultSAMLUserDetailsService; import it.infn.mw.iam.authn.saml.IamCachingMetadataManader; import it.infn.mw.iam.authn.saml.IamExtendedMetadataDelegate; import it.infn.mw.iam.authn.saml.IamSamlAuthenticationProvider; import it.infn.mw.iam.authn.saml.JustInTimeProvisioningSAMLUserDetailsService; import it.infn.mw.iam.authn.saml.MetadataLookupService; import it.infn.mw.iam.authn.saml.SamlExceptionMessageHelper; import it.infn.mw.iam.authn.saml.util.FirstApplicableChainedSamlIdResolver; import it.infn.mw.iam.authn.saml.util.SamlIdResolvers; import it.infn.mw.iam.authn.saml.util.SamlUserIdentifierResolver; import it.infn.mw.iam.authn.saml.util.metadata.ResearchAndScholarshipMetadataFilter; import it.infn.mw.iam.authn.saml.util.metadata.SirtfiAttributeMetadataFilter; import it.infn.mw.iam.config.saml.SamlConfig.IamProperties; import it.infn.mw.iam.config.saml.SamlConfig.ServerProperties; import it.infn.mw.iam.core.time.SystemTimeProvider; import it.infn.mw.iam.core.user.IamAccountService; import it.infn.mw.iam.persistence.repository.IamAccountRepository; @Configuration @Order(value = Ordered.LOWEST_PRECEDENCE) @Profile("saml") @EnableConfigurationProperties({ IamSamlProperties.class, IamSamlJITAccountProvisioningProperties.class, IamProperties.class, ServerProperties.class }) @EnableScheduling public class SamlConfig extends WebSecurityConfigurerAdapter implements SchedulingConfigurer { public static final Logger LOG = LoggerFactory.getLogger(SamlConfig.class); @Autowired ResourceLoader resourceLoader; @Autowired SamlUserIdentifierResolver resolver; @Autowired IamAccountRepository repo; @Autowired IamAccountService accountService; @Autowired IamProperties iamProperties; @Autowired IamSamlProperties samlProperties; @Autowired IamSamlJITAccountProvisioningProperties jitProperties; @Autowired ServerProperties serverProperties; @Autowired InactiveAccountAuthenticationHander inactiveAccountHandler; @Autowired MetadataLookupService metadataLookupService; @Autowired VelocityEngine velocityEngine; @Autowired private AUPSignatureCheckService aupSignatureCheckService; @Autowired private AccountUtils accountUtils; Timer metadataFetchTimer = new Timer(); BasicParserPool basicParserPool = new BasicParserPool(); @ConfigurationProperties(prefix = "iam") public static class IamProperties { private String baseUrl; public String getBaseUrl() { return baseUrl; } public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; } } @ConfigurationProperties(prefix = "server") public static class ServerProperties { private boolean useForwardHeaders; public boolean isUseForwardHeaders() { return useForwardHeaders; } public void setUseForwardHeaders(boolean useForwardHeaders) { this.useForwardHeaders = useForwardHeaders; } } @Configuration @EnableConfigurationProperties({ IamSamlProperties.class }) public static class IamSamlConfig { protected static final String[] DEFAULT_ID_RESOLVERS = { EPUID.getAlias(), EPTID.getAlias(), EPPN.getAlias() }; @Autowired IamSamlProperties samlProperties; private String[] resolverNames() { if (Strings.isNullOrEmpty(samlProperties.getIdResolvers())) { return DEFAULT_ID_RESOLVERS; } return samlProperties.getIdResolvers().split(","); } @Bean public SamlUserIdentifierResolver resolver() { List<SamlUserIdentifierResolver> resolvers = new ArrayList<>(); SamlIdResolvers resolverFactory = new SamlIdResolvers(); String[] resolverNames = resolverNames(); for (String n : resolverNames) { SamlUserIdentifierResolver r = resolverFactory.byName(n); if (r != null) { resolvers.add(r); } else { LOG.warn("Unsupported saml id resolver: {}", n); } } if (resolvers.isEmpty()) { throw new IllegalStateException("Could not configure SAML id resolvers"); } return new FirstApplicableChainedSamlIdResolver(resolvers); } } @Bean public SAMLUserDetailsService samlUserDetailsService(SamlUserIdentifierResolver resolver, IamAccountRepository accountRepo, InactiveAccountAuthenticationHander handler) { if (jitProperties.getEnabled()) { return new JustInTimeProvisioningSAMLUserDetailsService(resolver, accountService, handler, accountRepo, jitProperties.getTrustedIdpsAsOptionalSet()); } return new DefaultSAMLUserDetailsService(resolver, accountRepo, handler); } // XML parser pool needed for OpenSAML parsing @Bean(initMethod = "initialize") public StaticBasicParserPool parserPool() { return new StaticBasicParserPool(); } @Bean(name = "parserPoolHolder") public ParserPoolHolder parserPoolHolder() { return new ParserPoolHolder(); } // Bindings, encoders and decoders used for creating and parsing messages @Bean public MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager() { return new MultiThreadedHttpConnectionManager(); } @Bean public HttpClient httpClient() { return new HttpClient(multiThreadedHttpConnectionManager()); } @Bean public SAMLAuthenticationProvider samlAuthenticationProvider(SamlUserIdentifierResolver resolver, IamAccountRepository accountRepo, InactiveAccountAuthenticationHander handler) { IamSamlAuthenticationProvider samlAuthenticationProvider = new IamSamlAuthenticationProvider(resolver); samlAuthenticationProvider.setUserDetails(samlUserDetailsService(resolver, accountRepo, handler)); samlAuthenticationProvider.setForcePrincipalAsString(false); return samlAuthenticationProvider; } @Bean public SAMLContextProvider contextProvider() { if (serverProperties.isUseForwardHeaders()) { SAMLContextProviderLB cp = new SAMLContextProviderLB(); // Assume https when sitting behind a reverse proxy cp.setScheme("https"); // FIXME: find more reliable way of extracting the host name cp.setServerName(iamProperties.getBaseUrl().substring(8)); cp.setServerPort(443); cp.setIncludeServerPortInRequestURL(false); cp.setContextPath("/"); return cp; } return new SAMLContextProviderImpl(); } // Initialization of OpenSAML library @Bean public static SAMLBootstrap sAMLBootstrap() { return new SAMLBootstrap(); } // Logger for SAML messages and events @Bean public SAMLDefaultLogger samlLogger() { return new SAMLDefaultLogger(); } private WebSSOProfileConsumerImpl setAssertionTimeChecks(WebSSOProfileConsumerImpl impl) { impl.setMaxAssertionTime(samlProperties.getMaxAssertionTimeSec()); impl.setMaxAuthenticationAge(samlProperties.getMaxAuthenticationAgeSec()); return impl; } // SAML 2.0 WebSSO Assertion Consumer @Bean public WebSSOProfileConsumer webSSOprofileConsumer() { return setAssertionTimeChecks(new WebSSOProfileConsumerImpl()); } // SAML 2.0 Holder-of-Key WebSSO Assertion Consumer @Bean public WebSSOProfileConsumer hokWebSSOprofileConsumer() { return setAssertionTimeChecks(new WebSSOProfileConsumerHoKImpl()); } // SAML 2.0 Web SSO profile @Bean public WebSSOProfile webSSOprofile() { return new WebSSOProfileImpl(); } @Bean public SingleLogoutProfile logoutprofile() { return new SingleLogoutProfileImpl(); } @Bean public KeyManager keyManager() { Map<String, String> passwords = new HashMap<>(); passwords.put(samlProperties.getKeyId(), samlProperties.getKeyPassword()); DefaultResourceLoader loader = new DefaultResourceLoader(); Resource storeFile = loader.getResource(samlProperties.getKeystore()); return new JKSKeyManager(storeFile, samlProperties.getKeystorePassword(), passwords, samlProperties.getKeyId()); } // // Setup TLS Socket Factory @Bean public TLSProtocolConfigurer tlsProtocolConfigurer() { return new TLSProtocolConfigurer(); } @Bean public ProtocolSocketFactory socketFactory() { return new TLSProtocolSocketFactory(keyManager(), null, "default"); } @Bean public Protocol socketFactoryProtocol() { return new Protocol("https", socketFactory(), 443); } @Bean public WebSSOProfileOptions defaultWebSSOProfileOptions() { WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions(); webSSOProfileOptions.setIncludeScoping(false); webSSOProfileOptions.setNameID(NameIDType.PERSISTENT); return webSSOProfileOptions; } // Entry point to initialize authentication, default values taken from // properties file @Bean public SAMLEntryPoint samlEntryPoint() { SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint(); samlEntryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions()); return samlEntryPoint; } @Bean public ExtendedMetadata extendedMetadata() { final String discoveryUrl = String.format("%s/saml/discovery", iamProperties.getBaseUrl()); ExtendedMetadata extendedMetadata = new ExtendedMetadata(); extendedMetadata.setIdpDiscoveryEnabled(true); extendedMetadata.setIdpDiscoveryURL(discoveryUrl); extendedMetadata.setSignMetadata(false); return extendedMetadata; } private void configureMetadataFiltering(MetadataProvider p, IamSamlIdpMetadataProperties props) { List<MetadataFilter> filters = new ArrayList<>(); if (props.getRequireRs()) { filters.add(new ResearchAndScholarshipMetadataFilter()); } if (props.getRequireSirtfi()) { filters.add(new SirtfiAttributeMetadataFilter()); } if (!filters.isEmpty()) { try { MetadataFilterChain chain = new MetadataFilterChain(); chain.setFilters(filters); p.setMetadataFilter(chain); } catch (MetadataProviderException e) { LOG.error(e.getMessage(), e); } } } private IamExtendedMetadataDelegate metadataDelegate(MetadataProvider p, IamSamlIdpMetadataProperties props) { configureMetadataFiltering(p, props); IamExtendedMetadataDelegate extendedMetadataDelegate = new IamExtendedMetadataDelegate(p, extendedMetadata()); extendedMetadataDelegate.setMetadataTrustCheck(true); extendedMetadataDelegate.setRequireValidMetadata(true); if (props.getKeyAlias() != null) { extendedMetadataDelegate.setMetadataTrustedKeys(newHashSet(props.getKeyAlias())); } extendedMetadataDelegate.setMetadataRequireSignature(props.getRequireValidSignature()); return extendedMetadataDelegate; } private List<MetadataProvider> metadataProviders() throws MetadataProviderException, IOException, ResourceException { List<MetadataProvider> providers = new ArrayList<>(); for (IamSamlIdpMetadataProperties p : samlProperties.getIdpMetadata()) { String trimmedMedataUrl = p.getMetadataUrl().trim(); if (trimmedMedataUrl.startsWith("classpath:")) { LOG.info("Adding classpath based metadata provider for URL: {}", trimmedMedataUrl); ClasspathResource cpMetadataResources = new ClasspathResource( trimmedMedataUrl.replaceFirst("classpath:", "")); ResourceBackedMetadataProvider metadataProvider = new ResourceBackedMetadataProvider( metadataFetchTimer, cpMetadataResources); metadataProvider.setParserPool(basicParserPool); providers.add(metadataDelegate(metadataProvider, p)); } else if (trimmedMedataUrl.startsWith("file:")) { LOG.info("Adding File based metadata provider for URL: {}", trimmedMedataUrl); Resource metadataResource = resourceLoader.getResource(trimmedMedataUrl); FilesystemMetadataProvider metadataProvider = new FilesystemMetadataProvider( metadataResource.getFile()); metadataProvider.setParserPool(basicParserPool); providers.add(metadataDelegate(metadataProvider, p)); } else if (trimmedMedataUrl.startsWith("http")) { LOG.info("Adding HTTP metadata provider for URL: {}", trimmedMedataUrl); File metadataBackupFile = Files.createTempFile("metadata", "xml").toFile(); metadataBackupFile.deleteOnExit(); FileBackedHTTPMetadataProvider metadataProvider = new FileBackedHTTPMetadataProvider( metadataFetchTimer, httpClient(), trimmedMedataUrl, metadataBackupFile.getAbsolutePath()); metadataProvider.setParserPool(basicParserPool); providers.add(metadataDelegate(metadataProvider, p)); } else { LOG.error("Skipping invalid saml.idp-metatadata value: {}", trimmedMedataUrl); } } if (providers.isEmpty()) { String message = "Empty SAML metadata providers after initialization"; LOG.error(message); throw new IllegalStateException(message); } return providers; } @Bean @Qualifier("metadata") public CachingMetadataManager metadata() throws MetadataProviderException, IOException, ResourceException { CachingMetadataManager manager = new IamCachingMetadataManader(metadataProviders()); manager.setKeyManager(keyManager()); manager.refreshMetadata(); return manager; } @Bean public MetadataGenerator metadataGenerator() { MetadataGenerator metadataGenerator = new MetadataGenerator(); metadataGenerator.setEntityId(samlProperties.getEntityId()); metadataGenerator.setExtendedMetadata(extendedMetadata()); metadataGenerator.setIncludeDiscoveryExtension(false); metadataGenerator.setKeyManager(keyManager()); metadataGenerator.setEntityBaseURL(iamProperties.getBaseUrl()); return metadataGenerator; } @Bean public MetadataDisplayFilter metadataDisplayFilter() { return new MetadataDisplayFilter(); } @Bean public AuthenticationSuccessHandler samlAuthenticationSuccessHandler() { RootIsDashboardSuccessHandler sa = new RootIsDashboardSuccessHandler(iamProperties.getBaseUrl(), new HttpSessionRequestCache()); EnforceAupSignatureSuccessHandler aup = new EnforceAupSignatureSuccessHandler(sa, aupSignatureCheckService, accountUtils, repo); return new ExternalAuthenticationSuccessHandler(aup, "/"); } @Bean public AuthenticationFailureHandler authenticationFailureHandler() { return new ExternalAuthenticationFailureHandler(new SamlExceptionMessageHelper()); } @Bean public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception { SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter(); samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(samlAuthenticationSuccessHandler()); samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationManager()); samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler()); return samlWebSSOHoKProcessingFilter; } @Bean public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception { SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter(); samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager()); samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(samlAuthenticationSuccessHandler()); samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler()); return samlWebSSOProcessingFilter; } @Bean public MetadataGeneratorFilter metadataGeneratorFilter() { return new MetadataGeneratorFilter(metadataGenerator()); } @Bean public SimpleUrlLogoutSuccessHandler successLogoutHandler() { SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler(); successLogoutHandler.setDefaultTargetUrl("/"); return successLogoutHandler; } @Bean public SecurityContextLogoutHandler logoutHandler() { SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); logoutHandler.setInvalidateHttpSession(true); logoutHandler.setClearAuthentication(true); return logoutHandler; } @Bean public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() { return new SAMLLogoutProcessingFilter(successLogoutHandler(), logoutHandler()); } @Bean public SAMLLogoutFilter samlLogoutFilter() { return new SAMLLogoutFilter(successLogoutHandler(), new LogoutHandler[] { logoutHandler() }, new LogoutHandler[] { logoutHandler() }); } private ArtifactResolutionProfile artifactResolutionProfile() { final ArtifactResolutionProfileImpl artifactResolutionProfile = new ArtifactResolutionProfileImpl( httpClient()); artifactResolutionProfile.setProcessor(new SAMLProcessorImpl(soapBinding())); return artifactResolutionProfile; } @Bean public HTTPArtifactBinding artifactBinding(ParserPool parserPool, VelocityEngine velocityEngine) { return new HTTPArtifactBinding(parserPool, velocityEngine, artifactResolutionProfile()); } @Bean public HTTPSOAP11Binding soapBinding() { return new HTTPSOAP11Binding(parserPool()); } @Bean public HTTPPostBinding httpPostBinding() { return new HTTPPostBinding(parserPool(), velocityEngine); } @Bean public HTTPRedirectDeflateBinding httpRedirectDeflateBinding() { return new HTTPRedirectDeflateBinding(parserPool()); } @Bean public HTTPSOAP11Binding httpSOAP11Binding() { return new HTTPSOAP11Binding(parserPool()); } @Bean public HTTPPAOS11Binding httpPAOS11Binding() { return new HTTPPAOS11Binding(parserPool()); } @Bean public SAMLProcessor processor() { Collection<SAMLBinding> bindings = new ArrayList<>(); bindings.add(httpRedirectDeflateBinding()); bindings.add(httpPostBinding()); bindings.add(artifactBinding(parserPool(), velocityEngine)); bindings.add(httpSOAP11Binding()); bindings.add(httpPAOS11Binding()); return new SAMLProcessorImpl(bindings); } /** * Define the security filter chain in order to support SSO Auth by using SAML 2.0 * * @return Filter chain proxy @throws Exception */ @Bean public FilterChainProxy samlFilter() throws Exception { List<SecurityFilterChain> chains = new ArrayList<>(); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"), samlEntryPoint())); chains.add( new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"), samlLogoutFilter())); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"), metadataDisplayFilter())); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"), samlWebSSOProcessingFilter())); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"), samlWebSSOHoKProcessingFilter())); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"), samlLogoutProcessingFilter())); return new FilterChainProxy(chains); } @Override protected void configure(HttpSecurity http) throws Exception { String pattern = "/saml/**"; http.antMatcher(pattern); http.csrf().ignoringAntMatchers(pattern); http.authorizeRequests().antMatchers(pattern).permitAll(); http.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class).addFilterAfter(samlFilter(), BasicAuthenticationFilter.class); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(samlAuthenticationProvider(resolver, repo, inactiveAccountHandler)); } private void scheduleMetadataLookupServiceRefresh(ScheduledTaskRegistrar taskRegistrar) { LOG.info("Scheduling metadata lookup service refresh task to run every {} seconds.", samlProperties.getMetadataLookupServiceRefreshPeriodSec()); taskRegistrar.addFixedRateTask(() -> metadataLookupService.refreshMetadata(), TimeUnit.SECONDS.toMillis(samlProperties.getMetadataLookupServiceRefreshPeriodSec())); } private void scheduleProvisionedAccountsCleanup(final ScheduledTaskRegistrar taskRegistrar) { if (!jitProperties.getEnabled()) { LOG.info("Just-in-time account provisioning for SAML is DISABLED."); return; } if (!jitProperties.getCleanupTaskEnabled()) { LOG.info("Cleanup for SAML JIT account provisioning is DISABLED."); return; } LOG.info( "Scheduling Just-in-time provisioned account cleanup task to run every {} seconds. Accounts inactive for {} " + "days will be deleted", jitProperties.getCleanupTaskPeriodSec(), jitProperties.getInactiveAccountLifetimeDays()); taskRegistrar.addFixedRateTask( new CleanInactiveProvisionedAccounts(new SystemTimeProvider(), accountService, jitProperties.getInactiveAccountLifetimeDays()), TimeUnit.SECONDS.toMillis(jitProperties.getCleanupTaskPeriodSec())); } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { scheduleProvisionedAccountsCleanup(taskRegistrar); scheduleMetadataLookupServiceRefresh(taskRegistrar); } }