From 127bfcf405808fbb07dd37960dc3660e253f9435 Mon Sep 17 00:00:00 2001 From: Bill Smith Date: Fri, 25 Jan 2019 12:28:26 -0700 Subject: [PATCH] [SHIBUI-1029] Updated tests with support for new custom user mappings. --- .../unicon/shibui/pac4j/AddNewUserFilter.java | 37 ++++++++++++++----- .../pac4j/CustomPropertiesConfiguration.java | 4 +- .../src/main/resources/application.yml | 4 +- .../shibui/pac4j/AddNewUserFilterTests.groovy | 28 +++++++++----- 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java index d30acf5bd..af369937f 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java @@ -7,11 +7,13 @@ 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.apache.commons.lang3.RandomStringUtils; import org.pac4j.saml.profile.SAML2Profile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCrypt; import javax.mail.MessagingException; import javax.servlet.Filter; @@ -22,6 +24,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -54,18 +57,22 @@ public AddNewUserFilter(CustomPropertiesConfiguration customPropertiesConfigurat public void init(FilterConfig filterConfig) throws ServletException { } - private User buildAndPersistNewUserFromProfile(Map attributes) { + private User buildAndPersistNewUserFromProfile(SAML2Profile profile) { Role noRole = roleRepository.findByName(ROLE_NONE).orElse(new Role(ROLE_NONE)); roleRepository.save(noRole); User user = new User(); user.getRoles().add(noRole); - user.setUsername((String) attributes.get(saml2ProfileMapping.get("username"))); - user.setFirstName((String) attributes.get(saml2ProfileMapping.get("firstName"))); - user.setLastName((String) attributes.get(saml2ProfileMapping.get("lastName"))); - user.setEmailAddress((String) attributes.get(saml2ProfileMapping.get("email"))); - userRepository.save(user); - return user; + user.setUsername(getAttributeFromProfile(profile, "username")); + user.setPassword(BCrypt.hashpw(RandomStringUtils.randomAlphanumeric(20), BCrypt.gensalt())); + user.setFirstName(getAttributeFromProfile(profile, "firstName")); + user.setLastName(getAttributeFromProfile(profile, "lastName")); + user.setEmailAddress(getAttributeFromProfile(profile, "email")); + User persistedUser = userRepository.save(user); + if (logger.isDebugEnabled()) { + logger.debug("Persisted new user:\n" + user); + } + return persistedUser; } @Override @@ -73,12 +80,12 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); SAML2Profile profile = (SAML2Profile) authentication.getPrincipal(); if (profile != null) { - String username = (String) profile.getAttribute(saml2ProfileMapping.get("username")); + String username = getAttributeFromProfile(profile, "username"); if (username != null) { Optional persistedUser = userRepository.findByUsername(username); User user; if (!persistedUser.isPresent()) { - user = buildAndPersistNewUserFromProfile(profile.getAttributes()); + user = buildAndPersistNewUserFromProfile(profile); try { emailService.sendNewUserMail(username); } catch (MessagingException e) { @@ -100,6 +107,18 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha public void destroy() { } + private String getAttributeFromProfile(SAML2Profile profile, String stringKey) { + String mappingKey = saml2ProfileMapping.get(stringKey); + List attributeList = (List) profile.getAttribute(mappingKey); + String attribute = null; + if (attributeList.size() > 0) { + if (attributeList.size() != 1) { + logger.warn(String.format("More than one attribute was found for key [%s]", stringKey)); + } + attribute = attributeList.get(0); + } + return attribute; + } private byte[] getJsonResponseBytes(ErrorResponse eErrorResponse) throws IOException { String errorResponseJson = new ObjectMapper().writeValueAsString(eErrorResponse); return errorResponseJson.getBytes(); diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/CustomPropertiesConfiguration.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/CustomPropertiesConfiguration.java index 9d0f745b6..793b43793 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/CustomPropertiesConfiguration.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/CustomPropertiesConfiguration.java @@ -1,10 +1,9 @@ package net.unicon.shibui.pac4j; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.stereotype.Component; -import java.util.HashMap; import java.util.Map; /** @@ -12,6 +11,7 @@ */ @Component @ConfigurationProperties(prefix="custom") +@EnableConfigurationProperties public class CustomPropertiesConfiguration { private Map saml2ProfileMapping; diff --git a/pac4j-module/src/main/resources/application.yml b/pac4j-module/src/main/resources/application.yml index 7ffeb8bf9..ae30333c7 100644 --- a/pac4j-module/src/main/resources/application.yml +++ b/pac4j-module/src/main/resources/application.yml @@ -1,6 +1,6 @@ custom: saml2ProfileMapping: username: urn:oid:0.9.2342.19200300.100.1.3 - firstname: givenName - lastname: sn + firstName: givenName + lastName: sn email: mail \ No newline at end of file diff --git a/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy b/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy index 526785d72..652b38783 100644 --- a/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy +++ b/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy @@ -5,13 +5,13 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.User 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.saml.profile.SAML2Profile import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.context.SpringBootTest -import org.springframework.context.annotation.ComponentScan import org.springframework.security.core.Authentication import org.springframework.security.core.context.SecurityContext import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.test.context.ContextConfiguration import spock.lang.Specification import spock.lang.Subject @@ -22,9 +22,8 @@ import javax.servlet.http.HttpServletResponse /** * @author Bill Smith (wsmith@unicon.net) */ -@SpringBootTest -@ComponentScan("net.unicon.shibui.pac4j") -@ContextConfiguration(classes=[CustomPropertiesConfiguration]) +@SpringBootTest(classes = [CustomPropertiesConfiguration]) +@EnableConfigurationProperties([CustomPropertiesConfiguration]) class AddNewUserFilterTests extends Specification { UserRepository userRepository = Mock() @@ -37,20 +36,31 @@ class AddNewUserFilterTests extends Specification { SecurityContext securityContext = Mock() Authentication authentication = Mock() + SAML2Profile saml2Profile = Mock() + @Autowired CustomPropertiesConfiguration customPropertiesConfiguration + Map userAttributeMapping + @Subject - AddNewUserFilter addNewUserFilter = new AddNewUserFilter(customPropertiesConfiguration, userRepository, roleRepository, emailService) + AddNewUserFilter addNewUserFilter def setup() { SecurityContextHolder.setContext(securityContext) securityContext.getAuthentication() >> authentication + authentication.getPrincipal() >> saml2Profile + + addNewUserFilter = new AddNewUserFilter(customPropertiesConfiguration, userRepository, roleRepository, emailService) + userAttributeMapping = customPropertiesConfiguration.saml2ProfileMapping } def "new users are redirected"() { given: - authentication.getName() >> 'newUser' + saml2Profile.getAttribute(userAttributeMapping.get('username')) >> ['newUser'] + saml2Profile.getAttribute(userAttributeMapping.get('firstName')) >> ['New'] + saml2Profile.getAttribute(userAttributeMapping.get('lastName')) >> ['User'] + saml2Profile.getAttribute(userAttributeMapping.get('email')) >> ['newuser@institution.edu'] userRepository.findByUsername('newUser') >> Optional.empty() roleRepository.findByName('ROLE_NONE') >> Optional.of(new Role('ROLE_NONE')) @@ -59,14 +69,14 @@ class AddNewUserFilterTests extends Specification { then: 1 * roleRepository.save(_) - 1 * userRepository.save(_) + 1 * userRepository.save(_ as User) >> { User user -> user } 1 * emailService.sendNewUserMail('newUser') 1 * response.sendRedirect("/static.html") } def "existing users are not redirected"() { given: - authentication.getName() >> 'existingUser' + saml2Profile.getAttribute(userAttributeMapping.get('username')) >> ['existingUser'] userRepository.findByUsername('existingUser') >> Optional.of(new User().with { it.username = 'existingUser' it.roles = [new Role('ROLE_USER')]