Skip to content

Commit

Permalink
SHIBUI-2063
Browse files Browse the repository at this point in the history
intermediate commit
  • Loading branch information
chasegawa committed Sep 13, 2021
1 parent f4f6df0 commit 30e7b0a
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,15 @@ public String getOwnerId() {
public OwnerType getOwnerType() {
return OwnerType.USER;
}


/**
* @return the FIRST role found for the user or an exception if the user has no roles
*/
public String getRole() {
if (StringUtils.isBlank(this.role)) {
Set<Role> roles = this.getRoles();
if (roles.size() != 1) {
throw new RuntimeException(String.format("User with username [%s] has no role or does not have exactly one role!", this.getUsername()));
if (roles.isEmpty()) {
throw new RuntimeException(String.format("User with username [%s] has no roles", this.getUsername()));
}
this.role = roles.iterator().next().getName();
}
Expand All @@ -145,4 +148,4 @@ public void setGroups(Set<Group> groups) {
public void registerLoader(ILazyLoaderHelper lazyLoaderHelper) {
this.lazyLoaderHelper = lazyLoaderHelper;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package edu.internet2.tier.shibboleth.admin.ui.security.service;

import java.util.List;
import java.util.Optional;
import java.util.Set;

import edu.internet2.tier.shibboleth.admin.ui.exception.EntityNotFoundException;
import edu.internet2.tier.shibboleth.admin.ui.security.exception.RoleDeleteException;
Expand All @@ -11,12 +13,17 @@ public interface IRolesService {

Role createRole(Role role) throws RoleExistsConflictException;

Role updateRole(Role role) throws EntityNotFoundException;

List<Role> findAll();

Optional<Role> findByName(String roleNone);

Role findByResourceId(String resourceId) throws EntityNotFoundException;

Set<Role> getAndCreateAllRoles(Set<String> roles);

void deleteDefinition(String resourceId) throws EntityNotFoundException, RoleDeleteException;

}
Role updateRole(Role role) throws EntityNotFoundException;

void save(Role newUserRole);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package edu.internet2.tier.shibboleth.admin.ui.security.service;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -42,6 +44,11 @@ public List<Role> findAll() {
return roleRepository.findAll();
}

@Override
public Optional<Role> findByName(String roleName) {
return roleRepository.findByName(roleName);
}

@Override
public Role findByResourceId(String resourceId) throws EntityNotFoundException {
Optional<Role> found = roleRepository.findByResourceId(resourceId);
Expand All @@ -51,6 +58,30 @@ public Role findByResourceId(String resourceId) throws EntityNotFoundException {
return found.get();
}

@Override
public Set<Role> getAndCreateAllRoles(Set<String> roleNames) {
HashSet<Role> result = new HashSet<>();
if (roleNames.isEmpty()) {
Role r = getRoleNone();
result.add(r);
return result;
}
roleNames.forEach(roleName -> {
Optional<Role> role = roleRepository.findByName(roleName);
result.add(role.orElseGet(() -> roleRepository.save(new Role(roleName))));
});
return result;
}

private Role getRoleNone() {
Optional<Role> noRole = roleRepository.findByName("ROLE_NONE");
if (noRole.isEmpty()) {
Role newUserRole = new Role("ROLE_NONE");
return roleRepository.save(newUserRole);
}
return noRole.get();
}

@Override
public Role updateRole(Role role) throws EntityNotFoundException {
Optional<Role> found = roleRepository.findByName(role.getName());
Expand All @@ -59,4 +90,9 @@ public Role updateRole(Role role) throws EntityNotFoundException {
}
return roleRepository.save(role);
}
}

@Override
public void save(Role newUserRole) {
roleRepository.save(newUserRole);
}
}
2 changes: 2 additions & 0 deletions backend/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
# forceServiceProviderMetadataGeneration: false
# callbackUrl: "https://localhost:8443/callback"
# maximumAuthenticationLifetime: 3600000
# requireAssertedRoleForNewUsers: false
# saml2ProfileMapping:
# username: urn:oid:0.9.2342.19200300.100.1.1
# firstname: urn:oid:2.5.4.42
# lastname: urn:oid:2.5.4.4
# email: urn:oid:0.9.2342.19200300.100.1.3
# groups: urn:oid:1.3.6.1.4.1.5923.1.5.1.1 # attributeId - isMemberOf

custom:
attributes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
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.service.IGroupService;
import edu.internet2.tier.shibboleth.admin.ui.security.service.IRolesService;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.ui.service.EmailService;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -17,6 +17,7 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.transaction.annotation.Transactional;

import javax.mail.MessagingException;
import javax.servlet.Filter;
Expand All @@ -27,7 +28,6 @@
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
Expand All @@ -43,13 +43,13 @@ public class AddNewUserFilter implements Filter {
private IGroupService groupService;
private Matcher matcher;
private Pac4jConfigurationProperties pac4jConfigurationProperties;
private RoleRepository roleRepository;
private IRolesService rolesService;
private Pac4jConfigurationProperties.SimpleProfileMapping simpleProfileMapping;
private UserService userService;

public AddNewUserFilter(Pac4jConfigurationProperties pac4jConfigurationProperties, UserService userService, RoleRepository roleRepository, Matcher matcher, IGroupService groupService, Optional<EmailService> emailService) {
public AddNewUserFilter(Pac4jConfigurationProperties pac4jConfigurationProperties, UserService userService, IRolesService rolesService, Matcher matcher, IGroupService groupService, Optional<EmailService> emailService) {
this.userService = userService;
this.roleRepository = roleRepository;
this.rolesService = rolesService;
this.emailService = emailService;
this.pac4jConfigurationProperties = pac4jConfigurationProperties;
this.matcher = matcher;
Expand All @@ -59,16 +59,10 @@ public AddNewUserFilter(Pac4jConfigurationProperties pac4jConfigurationPropertie

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

User user = new User();
user.getRoles().add(newUserRole);
user.setRoles(userRoles);
user.setUsername(profile.getUsername());
user.setPassword(BCrypt.hashpw(RandomStringUtils.randomAlphanumeric(20), BCrypt.gensalt()));
user.setFirstName(profile.getFirstName());
Expand All @@ -90,11 +84,15 @@ User buildAndPersistNewUserFromProfile(CommonProfile profile) {
}
}

User persistedUser = userService.save(user);
// Don't save the user if the role required flag is on and the user has the default none role
if (pac4jConfigurationProperties.isRequireAssertedRoleForNewUsers() && user.getRole().equals(ROLE_NONE)) {
return user;
}
user = userService.save(user);
if (log.isDebugEnabled()) {
log.debug("Persisted new user:\n" + user);
}
return persistedUser;
return user;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

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

public class BetterSAML2Profile extends SAML2Profile {
private SimpleProfileMapping profileMapping;
Expand Down Expand Up @@ -39,6 +40,10 @@ public List<String> getGroups() {
return (List<String>) getAttribute(profileMapping.getGroups());
}

public Set<String> getRoles() {
return (Set<String>) getAttribute(profileMapping.getRoles());
}

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

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ public void validate(Credentials credentials, WebContext context) {
saml2Config.setForceServiceProviderMetadataGeneration(pac4jConfigProps.isForceServiceProviderMetadataGeneration());
saml2Config.setWantsAssertionsSigned(pac4jConfigProps.isWantAssertionsSigned());
saml2Config.setAttributeAsId(pac4jConfigProps.getSimpleProfileMapping().getUsername());
//saml2Config.setPostLogoutURL(pac4jConfigProps.getPostLogoutURL()); // consideration needed?
//saml2Config.setSpLogoutRequestBindingType(pac4jConfigProps.getSpLogoutRequestBindingType());

final SAML2Client saml2Client = new SAML2Client(saml2Config);
saml2Client.addAuthorizationGenerator(saml2ModelAuthorizationGenerator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class Pac4jConfigurationProperties {
private String keystorePath = "/tmp/samlKeystore.jks";
private int maximumAuthenticationLifetime = 3600;
private String privateKeyPassword = "changeit";
private boolean requireAssertedRoleForNewUsers = false;
private SimpleProfileMapping simpleProfileMapping;
private String serviceProviderEntityId = "https://unicon.net/shibui";
private String serviceProviderMetadataPath = "/tmp/sp-metadata.xml";
Expand All @@ -37,6 +38,7 @@ public static class SimpleProfileMapping {
private String email;
private String firstName;
private String groups;
private String roles;
private String lastName;
private String username;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package net.unicon.shibui.pac4j;

import edu.internet2.tier.shibboleth.admin.ui.configuration.auto.EmailConfiguration;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService;
import edu.internet2.tier.shibboleth.admin.ui.security.service.IRolesService;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.ui.service.EmailService;
import static net.unicon.shibui.pac4j.Pac4jConfiguration.PAC4J_CLIENT_NAME;
import org.pac4j.core.config.Config;

import org.pac4j.core.matching.Matcher;
import org.pac4j.springframework.security.web.CallbackFilter;
import org.pac4j.springframework.security.web.SecurityFilter;
Expand All @@ -34,9 +33,9 @@
public class WebSecurity {
@Bean("webSecurityConfig")
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter(final Config config, UserService userService,
RoleRepository roleRepository, Optional<EmailService> emailService,
IRolesService rolesService, Optional<EmailService> emailService,
Pac4jConfigurationProperties pac4jConfigurationProperties, IGroupService groupService) {
return new Pac4jWebSecurityConfigurerAdapter(config, userService, roleRepository, emailService, groupService,
return new Pac4jWebSecurityConfigurerAdapter(config, userService, rolesService, emailService, groupService,
pac4jConfigurationProperties);
}

Expand All @@ -46,14 +45,14 @@ public static class Pac4jWebSecurityConfigurerAdapter extends WebSecurityConfigu
private Optional<EmailService> emailService;
private IGroupService groupService;
private Pac4jConfigurationProperties pac4jConfigurationProperties;
private RoleRepository roleRepository;
private IRolesService rolesService;
private UserService userService;

public Pac4jWebSecurityConfigurerAdapter(final Config config, UserService userService, RoleRepository roleRepository,
public Pac4jWebSecurityConfigurerAdapter(final Config config, UserService userService, IRolesService rolesService,
Optional<EmailService> emailService, IGroupService groupService, Pac4jConfigurationProperties pac4jConfigurationProperties) {
this.config = config;
this.userService = userService;
this.roleRepository = roleRepository;
this.rolesService = rolesService;
this.emailService = emailService;
this.groupService = groupService;
this.pac4jConfigurationProperties = pac4jConfigurationProperties;
Expand All @@ -69,7 +68,7 @@ protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").addFilterBefore(getFilter(config, pac4jConfigurationProperties.getTypeOfAuth()), BasicAuthenticationFilter.class);
http.antMatcher("/**").addFilterBefore(securityFilter, BasicAuthenticationFilter.class);
// add the new user filter
http.addFilterAfter(new AddNewUserFilter(pac4jConfigurationProperties, userService, roleRepository, getPathMatcher("exclude-paths-matcher"), groupService, emailService), SecurityFilter.class);
http.addFilterAfter(new AddNewUserFilter(pac4jConfigurationProperties, userService, rolesService, getPathMatcher("exclude-paths-matcher"), groupService, emailService), SecurityFilter.class);

http.authorizeRequests().anyRequest().fullyAuthenticated();

Expand Down
27 changes: 26 additions & 1 deletion pac4j-module/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,29 @@ shibui:
firstName: givenName
lastName: sn
email: mail
groups: urn:oid:1.3.6.1.4.1.5923.1.5.1.1 # attributeId - isMemberOf
groups: urn:oid:1.3.6.1.4.1.5923.1.5.1.1 # attributeId - isMemberOf
roles:

#shibui:
## Default password must be set for the default user to be configured and setup
# default-rootuser:root
## need to include the encoding for the password - be sure to quote the entire value as shown
# default-password: "{noop}foopassword"
# pac4j-enabled: true
# pac4j:
# keystorePath: "/etc/shibui/samlKeystore.jks"
# keystorePassword: "changeit"
# privateKeyPassword: "changeit"
# serviceProviderEntityId: "https://idp.example.com/shibui"
# serviceProviderMetadataPath: "/etc/shibui/sp-metadata.xml"
# identityProviderMetadataPath: "/etc/shibui/idp-metadata.xml"
# forceServiceProviderMetadataGeneration: false
# callbackUrl: "https://localhost:8443/callback"
# maximumAuthenticationLifetime: 3600000
# requireAssertedRoleForNewUsers: false
# saml2ProfileMapping:
# username: urn:oid:0.9.2342.19200300.100.1.1
# firstname: urn:oid:2.5.4.42
# lastname: urn:oid:2.5.4.4
# email: urn:oid:0.9.2342.19200300.100.1.3
# groups: urn:oid:1.3.6.1.4.1.5923.1.5.1.1 # attributeId - isMemberOf

0 comments on commit 30e7b0a

Please sign in to comment.