Skip to content

Commit

Permalink
SHIBUI-1774
Browse files Browse the repository at this point in the history
Updated libs for pac4j to current and updated related configurations
  • Loading branch information
chasegawa committed Jun 26, 2021
1 parent c24e581 commit 3e29f81
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 82 deletions.
7 changes: 3 additions & 4 deletions pac4j-module/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@
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<SAML2Profile> {
public class SAML2ModelAuthorizationGenerator implements AuthorizationGenerator {
private final UserRepository userRepository;

public SAML2ModelAuthorizationGenerator(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public SAML2Profile generate(WebContext context, SAML2Profile profile) {
public Optional<UserProfile> generate(WebContext context, SessionStore sessionStore, UserProfile profile) {
Optional<User> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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> 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;
Expand All @@ -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);
Expand Down

0 comments on commit 3e29f81

Please sign in to comment.