diff --git a/pac4j-module/build.gradle b/pac4j-module/build.gradle index 18eb7be38..9849aa614 100644 --- a/pac4j-module/build.gradle +++ b/pac4j-module/build.gradle @@ -25,10 +25,9 @@ dependencyManagement { dependencies { compileOnly project(':backend') - compile "org.pac4j:spring-security-pac4j:4.0.0" - compile "org.pac4j:pac4j-http:4.0.0" - compile "org.pac4j:pac4j-core:3.3.0" - compile "org.pac4j:pac4j-saml:3.3.0", { + compile "org.pac4j:spring-security-pac4j:6.0.0" + compile "org.pac4j:pac4j-http:5.1.0" + compile "org.pac4j:pac4j-saml:5.1.0", { // opensaml libraries are provided exclude group: 'org.opensaml' } diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jConfiguration.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jConfiguration.java index c885f0049..de8db70a7 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jConfiguration.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jConfiguration.java @@ -1,77 +1,108 @@ package net.unicon.shibui.pac4j; -import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; +import org.apache.commons.lang3.StringUtils; import org.pac4j.core.client.Clients; import org.pac4j.core.config.Config; import org.pac4j.core.context.WebContext; +import org.pac4j.core.context.session.SessionStore; import org.pac4j.core.credentials.Credentials; import org.pac4j.core.credentials.TokenCredentials; import org.pac4j.core.credentials.authenticator.Authenticator; import org.pac4j.core.exception.CredentialsException; -import org.pac4j.core.profile.CommonProfile; +import org.pac4j.core.matching.matcher.PathMatcher; import org.pac4j.core.profile.definition.CommonProfileDefinition; -import org.pac4j.http.client.direct.ParameterClient; +import org.pac4j.http.client.direct.HeaderClient; import org.pac4j.saml.client.SAML2Client; -import org.pac4j.saml.client.SAML2ClientConfiguration; +import org.pac4j.saml.config.SAML2Configuration; import org.pac4j.saml.credentials.authenticator.SAML2Authenticator; -import org.pac4j.http.client.direct.HeaderClient; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import com.google.common.collect.Lists; + +import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; + +/** + * Configuration setup here following readme from - https://github.com/pac4j/spring-security-pac4j/tree/5.0.x + * NOTE: matchers are now done as part of the config and have been moved over from the WebSecurity.java class of this package + * @see http://www.pac4j.org/docs/config.html + */ @Configuration @ConditionalOnProperty(name = "shibui.pac4j-enabled", havingValue = "true") public class Pac4jConfiguration { + public final static String PAC4J_CLIENT_NAME = "shibUIAuthClient"; + + /** + * Custom class that ensures we add the user's roles to the information when doing SAML2 auth + */ @Bean public SAML2ModelAuthorizationGenerator saml2ModelAuthorizationGenerator(UserRepository userRepository) { return new SAML2ModelAuthorizationGenerator(userRepository); } - + @Bean - public Config config(final Pac4jConfigurationProperties pac4jConfigurationProperties, - final SAML2ModelAuthorizationGenerator saml2ModelAuthorizationGenerator) { + public Config config(final Pac4jConfigurationProperties pac4jConfigProps, + final SAML2ModelAuthorizationGenerator saml2ModelAuthorizationGenerator) { - final Clients clients = new Clients(pac4jConfigurationProperties.getCallbackUrl()); + final Clients clients = new Clients(pac4jConfigProps.getCallbackUrl()); - if (pac4jConfigurationProperties.getTypeOfAuth().equals("SAML2")) { - final SAML2ClientConfiguration saml2ClientConfiguration = new SAML2ClientConfiguration(); - saml2ClientConfiguration.setKeystorePath(pac4jConfigurationProperties.getKeystorePath()); - saml2ClientConfiguration.setKeystorePassword(pac4jConfigurationProperties.getKeystorePassword()); - saml2ClientConfiguration.setPrivateKeyPassword(pac4jConfigurationProperties.getPrivateKeyPassword()); - saml2ClientConfiguration.setIdentityProviderMetadataPath(pac4jConfigurationProperties.getIdentityProviderMetadataPath()); - saml2ClientConfiguration.setMaximumAuthenticationLifetime(pac4jConfigurationProperties.getMaximumAuthenticationLifetime()); - saml2ClientConfiguration.setServiceProviderEntityId(pac4jConfigurationProperties.getServiceProviderEntityId()); - saml2ClientConfiguration.setServiceProviderMetadataPath(pac4jConfigurationProperties.getServiceProviderMetadataPath()); - saml2ClientConfiguration.setForceServiceProviderMetadataGeneration(pac4jConfigurationProperties.isForceServiceProviderMetadataGeneration()); - saml2ClientConfiguration.setWantsAssertionsSigned(pac4jConfigurationProperties.isWantAssertionsSigned()); - saml2ClientConfiguration.setAttributeAsId(pac4jConfigurationProperties.getSaml2ProfileMapping().getUsername()); + // Configure the client + switch (pac4jConfigProps.getTypeOfAuth()) { + case "SAML2": { + final SAML2Configuration saml2Config = new SAML2Configuration(); + saml2Config.setKeystorePath(pac4jConfigProps.getKeystorePath()); + saml2Config.setKeystorePassword(pac4jConfigProps.getKeystorePassword()); + saml2Config.setPrivateKeyPassword(pac4jConfigProps.getPrivateKeyPassword()); + saml2Config.setIdentityProviderMetadataPath(pac4jConfigProps.getIdentityProviderMetadataPath()); + saml2Config.setMaximumAuthenticationLifetime(pac4jConfigProps.getMaximumAuthenticationLifetime()); + saml2Config.setServiceProviderEntityId(pac4jConfigProps.getServiceProviderEntityId()); + saml2Config.setServiceProviderMetadataPath(pac4jConfigProps.getServiceProviderMetadataPath()); + saml2Config.setForceServiceProviderMetadataGeneration(pac4jConfigProps.isForceServiceProviderMetadataGeneration()); + saml2Config.setWantsAssertionsSigned(pac4jConfigProps.isWantAssertionsSigned()); + saml2Config.setAttributeAsId(pac4jConfigProps.getSaml2ProfileMapping().getUsername()); + //saml2Config.setPostLogoutURL(pac4jConfigProps.getPostLogoutURL()); // consideration needed? + //saml2Config.setSpLogoutRequestBindingType(pac4jConfigProps.getSpLogoutRequestBindingType()); - final SAML2Client saml2Client = new SAML2Client(saml2ClientConfiguration); + final SAML2Client saml2Client = new SAML2Client(saml2Config); saml2Client.setName("Saml2Client"); saml2Client.addAuthorizationGenerator(saml2ModelAuthorizationGenerator); - SAML2Authenticator saml2Authenticator = new SAML2Authenticator(saml2ClientConfiguration.getAttributeAsId(), - saml2ClientConfiguration.getMappedAttributes()); - saml2Authenticator.setProfileDefinition(new CommonProfileDefinition<>( - p -> new BetterSAML2Profile(pac4jConfigurationProperties.getSaml2ProfileMapping().getUsername()))); + SAML2Authenticator saml2Authenticator = new SAML2Authenticator(saml2Config.getAttributeAsId(), saml2Config.getMappedAttributes()); +// saml2Authenticator.setProfileDefinition(new CommonProfileDefinition<>(p -> new BetterSAML2Profile(pac4jConfigProps.getSaml2ProfileMapping().getUsername()))); saml2Client.setAuthenticator(saml2Authenticator); + saml2Client.setName(PAC4J_CLIENT_NAME); clients.setClients(saml2Client); - } else if (pac4jConfigurationProperties.getTypeOfAuth().equals("HEADER")) { - HeaderClient headerClient = new HeaderClient(pac4jConfigurationProperties.getAuthenticationHeader(), new Authenticator() { - @Override - public void validate(Credentials credentials, WebContext context) { - if (credentials instanceof TokenCredentials) { - TokenCredentials creds = (TokenCredentials) credentials; - String remoteUser = creds.getToken(); - } else { - throw new CredentialsException("Invalid Credentials object generated by HeaderClient"); - } - } - }); + } + case "HEADER": { + HeaderClient headerClient = new HeaderClient(pac4jConfigProps.getAuthenticationHeader(), + new Authenticator() { + @Override + public void validate(Credentials credentials, WebContext context, SessionStore sessionStore) { + if (credentials instanceof TokenCredentials) { + TokenCredentials creds = (TokenCredentials) credentials; + String token = creds.getToken(); + if (StringUtils.isAllBlank(token)) { + throw new CredentialsException("Supplied token value in header was missing or blank"); + } + } else { + throw new CredentialsException("Invalid Credentials object generated by HeaderClient"); + } + // must set user profile on credentials in order to continue. + // credentials.setUserProfile(userProfile); + } + }); + headerClient.setName(PAC4J_CLIENT_NAME); clients.setClients(headerClient); } - + } final Config config = new Config(clients); + + // configure the matcher for bypassing auth checks + PathMatcher pm = new PathMatcher(); + pm.setExcludedPaths(Lists.newArrayList("/favicon.ico", "/unsecured/**/*", "/error")); + config.addMatcher("exclude-paths-matcher", pm); + return config; } } diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/SAML2ModelAuthorizationGenerator.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/SAML2ModelAuthorizationGenerator.java index 165477627..4286d4e38 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/SAML2ModelAuthorizationGenerator.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/SAML2ModelAuthorizationGenerator.java @@ -4,11 +4,13 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import org.pac4j.core.authorization.generator.AuthorizationGenerator; import org.pac4j.core.context.WebContext; +import org.pac4j.core.context.session.SessionStore; +import org.pac4j.core.profile.UserProfile; import org.pac4j.saml.profile.SAML2Profile; import java.util.Optional; -public class SAML2ModelAuthorizationGenerator implements AuthorizationGenerator { +public class SAML2ModelAuthorizationGenerator implements AuthorizationGenerator { private final UserRepository userRepository; public SAML2ModelAuthorizationGenerator(UserRepository userRepository) { @@ -16,9 +18,9 @@ public SAML2ModelAuthorizationGenerator(UserRepository userRepository) { } @Override - public SAML2Profile generate(WebContext context, SAML2Profile profile) { + public Optional generate(WebContext context, SessionStore sessionStore, UserProfile profile) { Optional user = userRepository.findByUsername(profile.getUsername()); - user.ifPresent( u -> profile.addRole(u.getRole())); - return profile; + user.ifPresent( u -> ((SAML2Profile)profile).addRole(u.getRole())); + return Optional.of(profile); } } diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/WebSecurity.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/WebSecurity.java index 812a20e09..7e17031f5 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/WebSecurity.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/WebSecurity.java @@ -4,9 +4,11 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository; import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import edu.internet2.tier.shibboleth.admin.ui.service.EmailService; + import org.pac4j.core.config.Config; import org.pac4j.springframework.security.web.CallbackFilter; import org.pac4j.springframework.security.web.SecurityFilter; + import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -20,50 +22,19 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.firewall.StrictHttpFirewall; -import javax.swing.text.html.Option; import java.util.Optional; @Configuration @AutoConfigureOrder(-1) @ConditionalOnProperty(name = "shibui.pac4j-enabled", havingValue = "true") @AutoConfigureAfter(EmailConfiguration.class) - public class WebSecurity { + @Bean("webSecurityConfig") public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter(final Config config, UserRepository userRepository, RoleRepository roleRepository, Optional emailService, Pac4jConfigurationProperties pac4jConfigurationProperties) { return new Pac4jWebSecurityConfigurerAdapter(config, userRepository, roleRepository, emailService, pac4jConfigurationProperties); } - @Configuration - @Order(0) - @ConditionalOnProperty(name = "shibui.pac4j-enabled", havingValue = "true") - public static class FaviconSecurityConfiguration extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/favicon.ico").authorizeRequests().antMatchers("/favicon.ico").permitAll(); - } - } - - @Configuration - @Order(1) - @ConditionalOnProperty(name = "shibui.pac4j-enabled", havingValue = "true") - public static class UnsecuredSecurityConfiguration extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/unsecured/**/*").authorizeRequests().antMatchers("/unsecured/**/*").permitAll(); - } - } - - @Configuration - @Order(2) - @ConditionalOnProperty(name = "shibui.pac4j-enabled", havingValue = "true") - public static class ErrorSecurityConfiguration extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/error").authorizeRequests().antMatchers("/error").permitAll(); - } - } - @Order(100) public static class Pac4jWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { private final Config config; @@ -82,12 +53,12 @@ public Pac4jWebSecurityConfigurerAdapter(final Config config, UserRepository use @Override protected void configure(HttpSecurity http) throws Exception { - final SecurityFilter securityFilterForHeader = new SecurityFilter(this.config, "HeaderClient"); + final SecurityFilter securityFilterForHeader = new SecurityFilter(this.config, Pac4jConfiguration.PAC4J_CLIENT_NAME); final CallbackFilter callbackFilter = new CallbackFilter(this.config); http.antMatcher("/**").addFilterBefore(callbackFilter, BasicAuthenticationFilter.class) - .addFilterBefore(securityFilterForHeader, BasicAuthenticationFilter.class) //xxx check on this - .addFilterAfter(new AddNewUserFilter(pac4jConfigurationProperties, userRepository, roleRepository, emailService), SecurityFilter.class); + .addFilterBefore(securityFilterForHeader, BasicAuthenticationFilter.class) //xxx check on this + .addFilterAfter(new AddNewUserFilter(pac4jConfigurationProperties, userRepository, roleRepository, emailService), SecurityFilter.class); http.authorizeRequests().anyRequest().fullyAuthenticated(); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);