Skip to content

Commit

Permalink
Merged in bug/shibui-2015 (pull request #516)
Browse files Browse the repository at this point in the history
Bug/shibui 2015

Approved-by: Jonathan Johnson
Approved-by: Bill Smith
  • Loading branch information
chasegawa authored and Jonathan Johnson committed Sep 2, 2021
2 parents 12f7c03 + d6df5f9 commit b5aaf82
Show file tree
Hide file tree
Showing 11 changed files with 358 additions and 119 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
package edu.internet2.tier.shibboleth.admin.ui.security.service;

import java.util.*;

import edu.internet2.tier.shibboleth.admin.ui.domain.EntityDescriptor;
import edu.internet2.tier.shibboleth.admin.ui.domain.IActivatable;
import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.security.exception.GroupExistsConflictException;
import edu.internet2.tier.shibboleth.admin.ui.security.exception.OwnershipConflictException;
Expand All @@ -23,9 +14,21 @@
import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository;
import static edu.internet2.tier.shibboleth.admin.ui.security.service.UserAccess.ADMIN;
import static edu.internet2.tier.shibboleth.admin.ui.security.service.UserAccess.GROUP;
import static edu.internet2.tier.shibboleth.admin.ui.security.service.UserAccess.NONE;
import lombok.NoArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static edu.internet2.tier.shibboleth.admin.ui.security.service.UserAccess.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Service
@NoArgsConstructor
Expand Down Expand Up @@ -81,7 +84,7 @@ public boolean currentUserIsAdmin() {
public void delete(String username) throws EntityNotFoundException, OwnershipConflictException {
Optional<User> userToRemove = userRepository.findByUsername(username);
if (userToRemove.isEmpty()) throw new EntityNotFoundException("User does not exist");
if (!ownershipRepository.findOwnedByUser(username).isEmpty()) throw new OwnershipConflictException("User ["+username+"] has ownership of entities in the system. Please remove all items before attemtping to delete the user.");
if (!ownershipRepository.findOwnedByUser(username).isEmpty()) throw new OwnershipConflictException("User ["+username+"] has ownership of entities in the system. Please remove all items before attempting to delete the user.");

// ok, user exists and doesn't own anything in the system, so delete them
// If the user is owned by anything, clear that first
Expand All @@ -90,6 +93,10 @@ public void delete(String username) throws EntityNotFoundException, OwnershipCon
userRepository.delete(user);
}

public Optional<User> findByUsername(String username) {
return userRepository.findByUsername(username);
}

public User getCurrentUser() {
//TODO: Consider returning an Optional here
User user = null;
Expand Down Expand Up @@ -187,7 +194,7 @@ public User save(User user) {
if (g == null) {
try {
Group newGroup = ug;
Ownership o = ownershipRepository.saveAndFlush(new Ownership(newGroup, user));
ownershipRepository.saveAndFlush(new Ownership(newGroup, user));
g = groupService.createGroup(newGroup);
}
catch (GroupExistsConflictException e) {
Expand Down Expand Up @@ -219,7 +226,7 @@ public void updateUserRole(User user) {
throw new RuntimeException(String.format("User with username [%s] is defined with role [%s] which does not exist in the system!", user.getUsername(), user.getRole()));
}
} else {
throw new RuntimeException(String.format("User with username [%s] has no role defined and therefor cannot be updated!", user.getUsername()));
throw new RuntimeException(String.format("User with username [%s] has no role defined and therefore cannot be updated!", user.getUsername()));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,69 +1,60 @@
package net.unicon.shibui.pac4j;

import com.fasterxml.jackson.databind.ObjectMapper;
import edu.internet2.tier.shibboleth.admin.ui.controller.ErrorResponse;
import edu.internet2.tier.shibboleth.admin.ui.security.exception.GroupExistsConflictException;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Group;
import edu.internet2.tier.shibboleth.admin.ui.security.model.Role;
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.security.service.IGroupService;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.ui.service.EmailService;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.pac4j.core.context.JEEContext;
import org.pac4j.core.context.session.JEESessionStore;
import org.pac4j.core.matching.matcher.Matcher;
import org.pac4j.core.profile.CommonProfile;
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;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;

import java.io.IOException;
import java.util.List;
import java.util.Optional;

import lombok.extern.slf4j.Slf4j;
import java.util.*;

@Slf4j
public class AddNewUserFilter implements Filter {
private static final String ROLE_NONE = "ROLE_NONE";

private Optional<EmailService> emailService;
private IGroupService groupService;
private Matcher matcher;
private Pac4jConfigurationProperties pac4jConfigurationProperties;
private RoleRepository roleRepository;
private Pac4jConfigurationProperties.SimpleProfileMapping simpleProfileMapping;
private UserRepository userRepository;
private Matcher matcher;
private UserService userService;

public AddNewUserFilter(Pac4jConfigurationProperties pac4jConfigurationProperties, UserRepository userRepository, RoleRepository roleRepository, Matcher matcher, Optional<EmailService> emailService) {
this.userRepository = userRepository;
public AddNewUserFilter(Pac4jConfigurationProperties pac4jConfigurationProperties, UserService userService, RoleRepository roleRepository, Matcher matcher, IGroupService groupService, Optional<EmailService> emailService) {
this.userService = userService;
this.roleRepository = roleRepository;
this.emailService = emailService;
this.pac4jConfigurationProperties = pac4jConfigurationProperties;
this.matcher = matcher;
this.groupService = groupService;
simpleProfileMapping = this.pac4jConfigurationProperties.getSimpleProfileMapping();
}

@Transactional
private User buildAndPersistNewUserFromProfile(CommonProfile profile) {
User buildAndPersistNewUserFromProfile(CommonProfile profile) {
Optional<Role> noRole = roleRepository.findByName(ROLE_NONE);
Role newUserRole;
if (noRole.isEmpty()) {
newUserRole = new Role(ROLE_NONE);
newUserRole = roleRepository.save(newUserRole);
roleRepository.save(newUserRole);
}
newUserRole = noRole.get();

Expand All @@ -74,7 +65,23 @@ private User buildAndPersistNewUserFromProfile(CommonProfile profile) {
user.setFirstName(profile.getFirstName());
user.setLastName(profile.getFamilyName());
user.setEmailAddress(profile.getEmail());
User persistedUser = userRepository.save(user);

// get profile attribute for groups
Object obj = profile.getAttribute(simpleProfileMapping.getGroups());
if (obj != null) {
final ArrayList<String> groupNames = new ArrayList<>();
if (obj instanceof String) {
groupNames.add(obj.toString());
}
if (obj instanceof List) {
((List)obj).forEach(val -> groupNames.add(val.toString()));
}
if (!groupNames.isEmpty()) {
user.setUserGroups(findOrCreateGroups(groupNames));
}
}

User persistedUser = userService.save(user);
if (log.isDebugEnabled()) {
log.debug("Persisted new user:\n" + user);
}
Expand All @@ -97,7 +104,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
if (profile != null) {
String username = profile.getUsername();
if (username != null) {
Optional<User> persistedUser = userRepository.findByUsername(username);
Optional<User> persistedUser = userService.findByUsername(username);
User user;
if (persistedUser.isEmpty()) {
user = buildAndPersistNewUserFromProfile(profile);
Expand All @@ -122,7 +129,26 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
}
}

private Set<Group> findOrCreateGroups(ArrayList<String> groupNames) {
final HashSet<Group> result = new HashSet<>();
groupNames.forEach(name -> {
Group g = groupService.find(name);
if (g == null) {
g = new Group();
g.setResourceId(name);
g.setName(name);
try {
groupService.createGroup(g);
}
catch (GroupExistsConflictException shouldntHappen) {
}
}
result.add(g);
});
return result;
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package net.unicon.shibui.pac4j;

import org.pac4j.saml.profile.SAML2Profile;

import net.unicon.shibui.pac4j.Pac4jConfigurationProperties.SimpleProfileMapping;
import org.pac4j.saml.profile.SAML2Profile;

import java.util.Collection;
import java.util.List;

public class BetterSAML2Profile extends SAML2Profile {
private SimpleProfileMapping profileMapping;
Expand All @@ -28,6 +28,10 @@ public String getFirstName() {
return (String) getAttribute(profileMapping.getFirstName());
}

public List<String> getGroups() {
return (List<String>) getAttribute(profileMapping.getGroups());
}

@Override
public String getUsername() {
Object username = getAttribute(profileMapping.getUsername());
Expand All @@ -38,4 +42,4 @@ public String getUsername() {
}
}

}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package net.unicon.shibui.pac4j;

import com.google.common.collect.Lists;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.pac4j.core.client.Clients;
import org.pac4j.core.config.Config;
Expand All @@ -17,7 +21,6 @@
import org.pac4j.saml.config.SAML2Configuration;
import org.pac4j.saml.credentials.authenticator.SAML2Authenticator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
Expand All @@ -26,12 +29,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

import com.google.common.collect.Lists;

import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import lombok.extern.slf4j.Slf4j;

/**
* 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
Expand Down Expand Up @@ -85,7 +82,6 @@ public Config config(final Pac4jConfigurationProperties pac4jConfigProps,
//saml2Config.setSpLogoutRequestBindingType(pac4jConfigProps.getSpLogoutRequestBindingType());

final SAML2Client saml2Client = new SAML2Client(saml2Config);
saml2Client.setName("Saml2Client");
saml2Client.addAuthorizationGenerator(saml2ModelAuthorizationGenerator);
SAML2Authenticator saml2Authenticator = new SAML2Authenticator(saml2Config.getAttributeAsId(), saml2Config.getMappedAttributes());
saml2Authenticator.setProfileDefinition(new CommonProfileDefinition(p -> new BetterSAML2Profile(pac4jConfigProps.getSimpleProfileMapping())));
Expand All @@ -110,7 +106,7 @@ public void validate(Credentials credentials, WebContext context, SessionStore s
throw new CredentialsException("Invalid Credentials object generated by HeaderClient");
}
final CommonProfile profile = new CommonProfile();
String token = ((TokenCredentials)credentials).getToken();
String token = ((TokenCredentials)credentials).getToken();
profile.setId(token);
profile.addAttribute("username", token);
profile.setRoles(userService.getUserRoles(token));
Expand All @@ -134,4 +130,4 @@ private void registerErrorPages(ErrorPageRegistry registry) {
registry.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/unsecured/error.html"));
registry.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/unsecured/error.html"));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package net.unicon.shibui.pac4j;

import javax.servlet.Filter;

import org.pac4j.springframework.security.web.CallbackFilter;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;

@Component
@ConfigurationProperties(prefix = "shibui.pac4j")
@EnableConfigurationProperties
Expand Down Expand Up @@ -40,8 +36,8 @@ public class Pac4jConfigurationProperties {
public static class SimpleProfileMapping {
private String email;
private String firstName;
private String groups;
private String lastName;
private String username;
}
}

}
}
Loading

0 comments on commit b5aaf82

Please sign in to comment.