From 1e6b97d352983698f4113b706f7e446313a96caf Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 12 Nov 2018 11:42:29 -0500 Subject: [PATCH 01/10] SHIBUI-975: wip --- .../admin/ui/security/model/AdminRole.java | 37 ++++++++++++++++ .../admin/ui/security/model/AdminUser.java | 43 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java new file mode 100644 index 000000000..67eea5513 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java @@ -0,0 +1,37 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.model; + +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ManyToMany; +import java.util.Set; + +/** + * Models a basic administrative role concept in the system. + * + * @author Dmitriy Kopylenko + */ +@Entity +@NoArgsConstructor +@Getter +@Setter +@EqualsAndHashCode(callSuper = true, exclude = "admins") +@ToString(exclude = "admins") +public class AdminRole extends AbstractAuditable { + + @Column(unique = true) + private String name; + + @ManyToMany(cascade = CascadeType.ALL, mappedBy = "roles", fetch = FetchType.EAGER) + private Set admins; + + +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java new file mode 100644 index 000000000..de16be4ca --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java @@ -0,0 +1,43 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import lombok.*; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import java.util.Set; + +/** + * Models a basic administrative user in the system. + * + * @author Dmitriy Kopylenko + */ +@Entity +@NoArgsConstructor +@Getter +@Setter +@EqualsAndHashCode(callSuper = true, exclude = "roles") +@ToString(exclude = "roles") +public class AdminUser extends AbstractAuditable { + + @Column(nullable = false, unique = true) + private String username; + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @Column(nullable = false) + private String password; + + private String firstName; + + private String lastName; + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @ManyToMany(cascade = CascadeType.ALL) + @JoinTable(name = "adminuser_role", joinColumns = @JoinColumn(name = "admin_user_id"), inverseJoinColumns = @JoinColumn(name = "admin_role_id")) + private Set roles; +} From d74edf69402de104b4f1423f39b7301149b6a464 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 12 Nov 2018 12:02:39 -0500 Subject: [PATCH 02/10] WIP1 --- .../repository/AdminRoleRepository.java | 15 +++++++ .../repository/AdminUserRepository.java | 14 +++++++ .../springsecurity/AdminUserService.java | 40 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java new file mode 100644 index 000000000..581f1189a --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java @@ -0,0 +1,15 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.repository; + +import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; + +import java.util.Optional; + +/** + * Spring Data repository to manage entities of type {@link AdminRole}. + * + * @author Dmitriy Kopylenko + */ +public interface AdminRoleRepository { + + Optional findByName(final String name); +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java new file mode 100644 index 000000000..d5fe4c314 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java @@ -0,0 +1,14 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.repository; + +import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Spring Data repository to manage entities of type {@link AdminUser}. + * + * @author Dmitriy Kopylenko + */ +public interface AdminUserRepository extends JpaRepository { + + AdminUser findByUsername(String username); +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java new file mode 100644 index 000000000..00d36d381 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java @@ -0,0 +1,40 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.springsecurity; + +import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; +import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; +import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashSet; +import java.util.Set; + +/** + * Spring Security {@link UserDetailsService} implementation for local administration of admin users ins the system. + * + * @author Dmitriy Kopylenko + */ +@RequiredArgsConstructor +public class AdminUserService implements UserDetailsService { + + private final AdminUserRepository adminUserRepository; + + @Override + @Transactional(readOnly = true) + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + AdminUser user = adminUserRepository.findByUsername(username); + + Set grantedAuthorities = new HashSet<>(); + for (AdminRole role : user.getRoles()) { + grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())); + } + + return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); + } +} + From 3d0ab269c2773a099cd3139b01fb63a3c83d0d76 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 12 Nov 2018 19:08:54 -0500 Subject: [PATCH 03/10] wip --- .../admin/ui/ShibbolethUiApplication.java | 39 ++++++++++++++++++- .../configuration/auto/WebSecurityConfig.java | 21 +++++++++- .../admin/ui/security/model/AdminRole.java | 10 +++-- .../admin/ui/security/model/AdminUser.java | 6 ++- .../repository/AdminRoleRepository.java | 3 +- .../repository/AdminUserRepository.java | 4 +- .../springsecurity/AdminUserService.java | 19 ++++++--- .../src/main/resources/application.properties | 3 +- 8 files changed, 88 insertions(+), 17 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java index a1ccd6c25..d9e356d4a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java @@ -1,6 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; +import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; +import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; +import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminRoleRepository; +import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -18,10 +22,15 @@ import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; @SpringBootApplication @ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "edu.internet2.tier.shibboleth.admin.ui.configuration.auto.*")) -@EntityScan(basePackages = "edu.internet2.tier.shibboleth.admin.ui.domain") +@EntityScan(basePackages = {"edu.internet2.tier.shibboleth.admin.ui.domain", "edu.internet2.tier.shibboleth.admin.ui.security.model"}) @EnableJpaAuditing @EnableScheduling @EnableWebSecurity @@ -50,4 +59,32 @@ void showMetadataResolversResourceIds(ApplicationStartedEvent e) { } } + @Component + @Profile("dev") + public static class SampleAdminUsersCreator { + + @Autowired + AdminUserRepository adminUserRepository; + + @Autowired + AdminRoleRepository adminRoleRepository; + + @EventListener + void createSampleAdminUsers(ApplicationStartedEvent e) { + //TODO: this is wip. Having a hard time with many-to-many saving with Hibernate's detatched entity exceptions, etc. + if(adminUserRepository.count() == 0L) { + AdminRole role = new AdminRole(); + role.setName("ADMIN"); + + Arrays.asList("1", "2").forEach(it -> { + AdminUser user = new AdminUser(); + user.setUsername(String.format("admin%s", it)); + user.setPassword(String.format("{noop}adminpass%s", it)); + //role.getAdmins().add(user); + //user.getRoles().add(role); + adminUserRepository.save(user); + }); + } + } + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java index f824ca8a5..3cd3ca840 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java @@ -1,6 +1,9 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration.auto; import edu.internet2.tier.shibboleth.admin.ui.security.DefaultAuditorAware; +import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository; +import edu.internet2.tier.shibboleth.admin.ui.security.springsecurity.AdminUserService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -13,6 +16,8 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.StrictHttpFirewall; @@ -34,6 +39,9 @@ public class WebSecurityConfig { @Value("${shibui.default-password:}") private String defaultPassword; + @Autowired + private AdminUserRepository adminUserRepository; + @Bean public HttpFirewall allowUrlEncodedSlashHttpFirewall() { StrictHttpFirewall firewall = new StrictHttpFirewall(); @@ -62,12 +70,15 @@ protected void configure(HttpSecurity http) throws Exception { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // TODO: more configurable authentication + PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); if (defaultPassword != null && !"".equals(defaultPassword)) { auth .inMemoryAuthentication() - .withUser("user").password(defaultPassword).roles("USER"); + .withUser("user") + .password(passwordEncoder.encode(defaultPassword)) + .roles("USER"); } else { - super.configure(auth); + auth.userDetailsService(adminUserService(adminUserRepository)).passwordEncoder(passwordEncoder); } } @@ -85,6 +96,12 @@ public AuditorAware defaultAuditorAware() { return new DefaultAuditorAware(); } + @Bean + @Profile("!no-auth") + public AdminUserService adminUserService(AdminUserRepository adminUserRepository) { + return new AdminUserService(adminUserRepository); + } + @Bean @Profile("no-auth") public WebSecurityConfigurerAdapter noAuthUsedForEaseDevelopment() { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java index 67eea5513..b2e202359 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java @@ -1,6 +1,8 @@ package edu.internet2.tier.shibboleth.admin.ui.security.model; +import com.fasterxml.jackson.annotation.JsonProperty; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @@ -11,7 +13,10 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; import javax.persistence.ManyToMany; +import java.util.HashSet; import java.util.Set; /** @@ -30,8 +35,7 @@ public class AdminRole extends AbstractAuditable { @Column(unique = true) private String name; - @ManyToMany(cascade = CascadeType.ALL, mappedBy = "roles", fetch = FetchType.EAGER) - private Set admins; - + @ManyToMany(cascade = CascadeType.PERSIST, mappedBy = "roles", fetch = FetchType.EAGER) + private Set admins = new HashSet<>(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java index de16be4ca..e56052a2c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java @@ -7,9 +7,11 @@ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; +import java.util.HashSet; import java.util.Set; /** @@ -37,7 +39,7 @@ public class AdminUser extends AbstractAuditable { private String lastName; @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) - @ManyToMany(cascade = CascadeType.ALL) + @ManyToMany(cascade = CascadeType.PERSIST) @JoinTable(name = "adminuser_role", joinColumns = @JoinColumn(name = "admin_user_id"), inverseJoinColumns = @JoinColumn(name = "admin_role_id")) - private Set roles; + private Set roles = new HashSet<>(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java index 581f1189a..99672f57d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.security.repository; import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; +import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; @@ -9,7 +10,7 @@ * * @author Dmitriy Kopylenko */ -public interface AdminRoleRepository { +public interface AdminRoleRepository extends JpaRepository { Optional findByName(final String name); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java index d5fe4c314..559156e99 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java @@ -3,6 +3,8 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + /** * Spring Data repository to manage entities of type {@link AdminUser}. * @@ -10,5 +12,5 @@ */ public interface AdminUserRepository extends JpaRepository { - AdminUser findByUsername(String username); + Optional findByUsername(String username); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java index 00d36d381..02c052bee 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java @@ -6,13 +6,14 @@ import lombok.RequiredArgsConstructor; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.transaction.annotation.Transactional; -import java.util.HashSet; import java.util.Set; +import static java.util.stream.Collectors.toSet; /** * Spring Security {@link UserDetailsService} implementation for local administration of admin users ins the system. @@ -27,14 +28,20 @@ public class AdminUserService implements UserDetailsService { @Override @Transactional(readOnly = true) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - AdminUser user = adminUserRepository.findByUsername(username); + AdminUser user = adminUserRepository + .findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException(String.format("User [%s] is not found", username))); - Set grantedAuthorities = new HashSet<>(); - for (AdminRole role : user.getRoles()) { - grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())); + Set grantedAuthorities = user.getRoles().stream() + .map(AdminRole::getName) + .map(SimpleGrantedAuthority::new) + .collect(toSet()); + + if (grantedAuthorities.isEmpty()) { + throw new UsernameNotFoundException(String.format("No roles are defined for user [%s]", username)); } - return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); + return new User(user.getUsername(), user.getPassword(), grantedAuthorities); } } diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 9907c0ff9..b108787bd 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -4,6 +4,7 @@ # Logging Configuration #logging.config=classpath:log4j2.xml +logging.level.org.springframework.security=DEBUG logging.level.org.springframework=INFO logging.level.edu.internet2.tier.shibboleth.admin.ui=INFO @@ -48,7 +49,7 @@ shibui.logout-url=/dashboard # spring.profiles.active=default -#shibui.default-password= +#shibui.default-password=pass shibui.metadata-sources-ui-schema-location=classpath:metadata-sources-ui-schema.json shibui.entity-attributes-filters-ui-schema-location=classpath:entity-attributes-filters-ui-schema.json From c03967baed384c524f8be38aa6ce04d2ef6fda69 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 13 Nov 2018 11:37:37 -0500 Subject: [PATCH 04/10] WIP --- .../admin/ui/ShibbolethUiApplication.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java index d9e356d4a..84941894f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java @@ -69,21 +69,22 @@ public static class SampleAdminUsersCreator { @Autowired AdminRoleRepository adminRoleRepository; + @Transactional @EventListener void createSampleAdminUsers(ApplicationStartedEvent e) { - //TODO: this is wip. Having a hard time with many-to-many saving with Hibernate's detatched entity exceptions, etc. if(adminUserRepository.count() == 0L) { AdminRole role = new AdminRole(); - role.setName("ADMIN"); + role.setName("ROLE_ADMIN"); + AdminUser user = new AdminUser(); + user.setUsername("admin"); + user.setPassword("{noop}adminpass"); - Arrays.asList("1", "2").forEach(it -> { - AdminUser user = new AdminUser(); - user.setUsername(String.format("admin%s", it)); - user.setPassword(String.format("{noop}adminpass%s", it)); - //role.getAdmins().add(user); - //user.getRoles().add(role); - adminUserRepository.save(user); - }); + //The complexity of managing bi-directional many-to-many. TODO: to encapsulate this association + //managing logic into domain model itself + role.getAdmins().add(user); + user.getRoles().add(role); + + adminUserRepository.save(user); } } } From 97314fe641ffbab4f2f4f6a17408a78feca65744 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 13 Nov 2018 14:29:37 -0500 Subject: [PATCH 05/10] SHIBUI-975 --- .../admin/ui/ShibbolethUiApplication.java | 5 +- .../admin/ui/security/model/AdminRole.java | 6 +-- .../admin/ui/security/model/AdminUser.java | 2 +- .../springsecurity/AdminUserService.java | 3 +- .../src/main/resources/application.properties | 2 +- .../AdminUserServiceTests.groovy | 53 +++++++++++++++++++ 6 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java index 84941894f..e9e4d33ae 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java @@ -66,9 +66,6 @@ public static class SampleAdminUsersCreator { @Autowired AdminUserRepository adminUserRepository; - @Autowired - AdminRoleRepository adminRoleRepository; - @Transactional @EventListener void createSampleAdminUsers(ApplicationStartedEvent e) { @@ -79,7 +76,7 @@ void createSampleAdminUsers(ApplicationStartedEvent e) { user.setUsername("admin"); user.setPassword("{noop}adminpass"); - //The complexity of managing bi-directional many-to-many. TODO: to encapsulate this association + //The complexity of managing bi-directional many-to-many. TODO: encapsulate this association //managing logic into domain model itself role.getAdmins().add(user); user.getRoles().add(role); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java index b2e202359..faa7e3167 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java @@ -1,8 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.security.model; -import com.fasterxml.jackson.annotation.JsonProperty; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; -import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @@ -13,8 +11,6 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; -import javax.persistence.JoinColumn; -import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import java.util.HashSet; import java.util.Set; @@ -35,7 +31,7 @@ public class AdminRole extends AbstractAuditable { @Column(unique = true) private String name; - @ManyToMany(cascade = CascadeType.PERSIST, mappedBy = "roles", fetch = FetchType.EAGER) + @ManyToMany(cascade = CascadeType.ALL, mappedBy = "roles", fetch = FetchType.EAGER) private Set admins = new HashSet<>(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java index e56052a2c..0294c14bb 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java @@ -39,7 +39,7 @@ public class AdminUser extends AbstractAuditable { private String lastName; @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) - @ManyToMany(cascade = CascadeType.PERSIST) + @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "adminuser_role", joinColumns = @JoinColumn(name = "admin_user_id"), inverseJoinColumns = @JoinColumn(name = "admin_role_id")) private Set roles = new HashSet<>(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java index 02c052bee..c1f1c06b8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java @@ -16,7 +16,7 @@ import static java.util.stream.Collectors.toSet; /** - * Spring Security {@link UserDetailsService} implementation for local administration of admin users ins the system. + * Spring Security {@link UserDetailsService} implementation for local administration of admin users in the system. * * @author Dmitriy Kopylenko */ @@ -38,6 +38,7 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx .collect(toSet()); if (grantedAuthorities.isEmpty()) { + //As defined by the UserDetailsService API contract throw new UsernameNotFoundException(String.format("No roles are defined for user [%s]", username)); } diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index b108787bd..3e95e06b7 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -4,7 +4,7 @@ # Logging Configuration #logging.config=classpath:log4j2.xml -logging.level.org.springframework.security=DEBUG +#logging.level.org.springframework.security=DEBUG logging.level.org.springframework=INFO logging.level.edu.internet2.tier.shibboleth.admin.ui=INFO diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy new file mode 100644 index 000000000..40b92e561 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy @@ -0,0 +1,53 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.springsecurity + +import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminRoleRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.context.annotation.Profile +import org.springframework.security.core.userdetails.UsernameNotFoundException +import org.springframework.test.annotation.DirtiesContext +import org.springframework.test.context.ActiveProfiles +import spock.lang.Specification + +/** + * Tests for AdminUserService + * + * @author Dmitriy Kopylenko + */ +@SpringBootTest +@ActiveProfiles('dev') +class AdminUserServiceTests extends Specification { + + @Autowired + AdminUserService adminUserService + + @Autowired + AdminRoleRepository adminRoleRepository + + @Autowired + AdminUserRepository adminUserRepository + + + def "Loading existing admin user with admin role"() { + given: 'Valid user with admin role is available (loaded by Spring Boot Listener in dev profile)' + def user = adminUserService.loadUserByUsername('admin') + + expect: + user.username == 'admin' + user.password == '{noop}adminpass' + user.getAuthorities().size() == 1 + user.getAuthorities()[0].authority == 'ROLE_ADMIN' + user.enabled + user.accountNonExpired + user.credentialsNonExpired + } + + def "Loading NON-existing admin user with admin role"() { + when: 'Non-existent admin user is tried to be looked up' + adminUserService.loadUserByUsername('nonexisting') + + then: + thrown UsernameNotFoundException + } +} From 1aba1fc7fae28fd7b56a9de96ea98ce7c4696fcb Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Wed, 14 Nov 2018 07:56:37 -0500 Subject: [PATCH 06/10] SHIBUI-975 address code review feedback --- .../admin/ui/ShibbolethUiApplication.java | 27 ------------- .../configuration/auto/WebSecurityConfig.java | 38 ++++++++++++++++++- .../src/main/resources/application.properties | 4 +- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java index e9e4d33ae..9932a8b48 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java @@ -58,31 +58,4 @@ void showMetadataResolversResourceIds(ApplicationStartedEvent e) { .forEach(it -> System.out.println(String.format("MetadataResolver [%s: %s]", it.getName(), it.getResourceId()))); } } - - @Component - @Profile("dev") - public static class SampleAdminUsersCreator { - - @Autowired - AdminUserRepository adminUserRepository; - - @Transactional - @EventListener - void createSampleAdminUsers(ApplicationStartedEvent e) { - if(adminUserRepository.count() == 0L) { - AdminRole role = new AdminRole(); - role.setName("ROLE_ADMIN"); - AdminUser user = new AdminUser(); - user.setUsername("admin"); - user.setPassword("{noop}adminpass"); - - //The complexity of managing bi-directional many-to-many. TODO: encapsulate this association - //managing logic into domain model itself - role.getAdmins().add(user); - user.getRoles().add(role); - - adminUserRepository.save(user); - } - } - } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java index 3cd3ca840..35704e39b 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java @@ -1,6 +1,8 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration.auto; import edu.internet2.tier.shibboleth.admin.ui.security.DefaultAuditorAware; +import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; +import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository; import edu.internet2.tier.shibboleth.admin.ui.security.springsecurity.AdminUserService; import org.springframework.beans.factory.annotation.Autowired; @@ -22,10 +24,14 @@ import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.StrictHttpFirewall; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; /** * Web security configuration. - * + *

* Workaround for slashes in URL from [https://stackoverflow.com/questions/48453980/spring-5-0-3-requestrejectedexception-the-request-was-rejected-because-the-url] */ @Configuration @@ -75,7 +81,7 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user") - .password(passwordEncoder.encode(defaultPassword)) + .password(defaultPassword) .roles("USER"); } else { auth.userDetailsService(adminUserService(adminUserRepository)).passwordEncoder(passwordEncoder); @@ -119,4 +125,32 @@ public void configure(WebSecurity web) throws Exception { } }; } + + @Component + @Profile("dev") + public static class SampleAdminUsersCreator { + + @Autowired + AdminUserRepository adminUserRepository; + + @Transactional + @PostConstruct + public void createSampleAdminUsers() { + if (adminUserRepository.count() == 0L) { + AdminRole role = new AdminRole(); + role.setName("ROLE_ADMIN"); + AdminUser user = new AdminUser(); + user.setUsername("admin"); + user.setPassword("{noop}adminpass"); + + //The complexity of managing bi-directional many-to-many. TODO: encapsulate this association + //managing logic into domain model itself + role.getAdmins().add(user); + user.getRoles().add(role); + + adminUserRepository.save(user); + } + } + } } + diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 3e95e06b7..da2082af2 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -4,7 +4,7 @@ # Logging Configuration #logging.config=classpath:log4j2.xml -#logging.level.org.springframework.security=DEBUG +#logging.level.org.springframework.security=INFO logging.level.org.springframework=INFO logging.level.edu.internet2.tier.shibboleth.admin.ui=INFO @@ -49,7 +49,7 @@ shibui.logout-url=/dashboard # spring.profiles.active=default -#shibui.default-password=pass +#shibui.default-password={noop}pass shibui.metadata-sources-ui-schema-location=classpath:metadata-sources-ui-schema.json shibui.entity-attributes-filters-ui-schema-location=classpath:entity-attributes-filters-ui-schema.json From 87fee601843c6d129b1defc1b44259675c008ff3 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Wed, 14 Nov 2018 09:15:09 -0500 Subject: [PATCH 07/10] SHIBUI-975: polishing --- backend/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index da2082af2..9ef60951e 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -49,7 +49,7 @@ shibui.logout-url=/dashboard # spring.profiles.active=default -#shibui.default-password={noop}pass +#shibui.default-password= shibui.metadata-sources-ui-schema-location=classpath:metadata-sources-ui-schema.json shibui.entity-attributes-filters-ui-schema-location=classpath:entity-attributes-filters-ui-schema.json From cd03dd337eae125e8026f769a388bc25936bc6ce Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Thu, 15 Nov 2018 12:06:05 -0500 Subject: [PATCH 08/10] Address code review feedback --- .../shibboleth/admin/ui/ShibbolethUiApplication.java | 11 ----------- .../ui/configuration/auto/WebSecurityConfig.java | 3 +-- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java index 9932a8b48..b548b98d4 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/ShibbolethUiApplication.java @@ -1,20 +1,14 @@ package edu.internet2.tier.shibboleth.admin.ui; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; -import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; -import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; -import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminRoleRepository; -import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Profile; import org.springframework.context.event.EventListener; @@ -22,11 +16,6 @@ import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; @SpringBootApplication @ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "edu.internet2.tier.shibboleth.admin.ui.configuration.auto.*")) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java index 35704e39b..e78e8503d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java @@ -83,9 +83,8 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception { .withUser("user") .password(defaultPassword) .roles("USER"); - } else { - auth.userDetailsService(adminUserService(adminUserRepository)).passwordEncoder(passwordEncoder); } + auth.userDetailsService(adminUserService(adminUserRepository)).passwordEncoder(passwordEncoder); } @Override From 022c4f11acaa772c38ec0924bd7d200024575b0e Mon Sep 17 00:00:00 2001 From: Jj! Date: Mon, 19 Nov 2018 12:22:08 -0600 Subject: [PATCH 09/10] [SHIBUI-975] refactor --- .../admin/ui/configuration/DevConfig.groovy | 35 ++++++++++++++ .../configuration/auto/WebSecurityConfig.java | 47 +++---------------- .../model/{AdminRole.java => Role.java} | 8 ++-- .../model/{AdminUser.java => User.java} | 7 ++- .../repository/AdminRoleRepository.java | 16 ------- .../repository/AdminUserRepository.java | 16 ------- .../security/repository/RoleRepository.java | 16 +++++++ .../security/repository/UserRepository.java | 16 +++++++ .../springsecurity/AdminUserService.java | 15 +++--- .../AdminUserServiceTests.groovy | 10 ++-- docs/SECURITY.md | 8 ++++ 11 files changed, 100 insertions(+), 94 deletions(-) create mode 100644 backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy rename backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/{AdminRole.java => Role.java} (80%) rename backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/{AdminUser.java => User.java} (78%) delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java delete mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/RoleRepository.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserRepository.java create mode 100644 docs/SECURITY.md diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy new file mode 100644 index 000000000..2106b52a2 --- /dev/null +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/DevConfig.groovy @@ -0,0 +1,35 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration + +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.UserRepository +import org.springframework.context.annotation.Profile +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +import javax.annotation.PostConstruct + +@Component +@Profile('dev') +class DevConfig { + private final UserRepository adminUserRepository + + DevConfig(UserRepository adminUserRepository) { + this.adminUserRepository = adminUserRepository + } + + @Transactional + @PostConstruct + void createDevAdminUsers() { + if (adminUserRepository.count() == 0) { + def user = new User().with { + username = 'admin' + password = '{noop}adminpass' + roles.add(new Role(name: 'ROLE_ADMIN')) + it + } + + adminUserRepository.save(user) + } + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java index e78e8503d..1dcdc6ce7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/auto/WebSecurityConfig.java @@ -1,9 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration.auto; import edu.internet2.tier.shibboleth.admin.ui.security.DefaultAuditorAware; -import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; -import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; -import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository; +import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import edu.internet2.tier.shibboleth.admin.ui.security.springsecurity.AdminUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -24,10 +22,6 @@ import org.springframework.security.web.firewall.HttpFirewall; import org.springframework.security.web.firewall.StrictHttpFirewall; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.PostConstruct; /** * Web security configuration. @@ -46,7 +40,7 @@ public class WebSecurityConfig { private String defaultPassword; @Autowired - private AdminUserRepository adminUserRepository; + private UserRepository userRepository; @Bean public HttpFirewall allowUrlEncodedSlashHttpFirewall() { @@ -80,11 +74,11 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception { if (defaultPassword != null && !"".equals(defaultPassword)) { auth .inMemoryAuthentication() - .withUser("user") + .withUser("root") .password(defaultPassword) - .roles("USER"); + .roles("ADMIN"); } - auth.userDetailsService(adminUserService(adminUserRepository)).passwordEncoder(passwordEncoder); + auth.userDetailsService(adminUserService(userRepository)).passwordEncoder(passwordEncoder); } @Override @@ -103,8 +97,8 @@ public AuditorAware defaultAuditorAware() { @Bean @Profile("!no-auth") - public AdminUserService adminUserService(AdminUserRepository adminUserRepository) { - return new AdminUserService(adminUserRepository); + public AdminUserService adminUserService(UserRepository userRepository) { + return new AdminUserService(userRepository); } @Bean @@ -124,32 +118,5 @@ public void configure(WebSecurity web) throws Exception { } }; } - - @Component - @Profile("dev") - public static class SampleAdminUsersCreator { - - @Autowired - AdminUserRepository adminUserRepository; - - @Transactional - @PostConstruct - public void createSampleAdminUsers() { - if (adminUserRepository.count() == 0L) { - AdminRole role = new AdminRole(); - role.setName("ROLE_ADMIN"); - AdminUser user = new AdminUser(); - user.setUsername("admin"); - user.setPassword("{noop}adminpass"); - - //The complexity of managing bi-directional many-to-many. TODO: encapsulate this association - //managing logic into domain model itself - role.getAdmins().add(user); - user.getRoles().add(role); - - adminUserRepository.save(user); - } - } - } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Role.java similarity index 80% rename from backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java rename to backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Role.java index faa7e3167..20564093d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminRole.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/Role.java @@ -24,14 +24,14 @@ @NoArgsConstructor @Getter @Setter -@EqualsAndHashCode(callSuper = true, exclude = "admins") -@ToString(exclude = "admins") -public class AdminRole extends AbstractAuditable { +@EqualsAndHashCode(callSuper = true, exclude = "users") +@ToString(exclude = "users") +public class Role extends AbstractAuditable { @Column(unique = true) private String name; @ManyToMany(cascade = CascadeType.ALL, mappedBy = "roles", fetch = FetchType.EAGER) - private Set admins = new HashSet<>(); + private Set users = new HashSet<>(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java similarity index 78% rename from backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java rename to backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java index 0294c14bb..9b24cf946 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/AdminUser.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/User.java @@ -7,7 +7,6 @@ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; @@ -25,7 +24,7 @@ @Setter @EqualsAndHashCode(callSuper = true, exclude = "roles") @ToString(exclude = "roles") -public class AdminUser extends AbstractAuditable { +public class User extends AbstractAuditable { @Column(nullable = false, unique = true) private String username; @@ -40,6 +39,6 @@ public class AdminUser extends AbstractAuditable { @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) @ManyToMany(cascade = CascadeType.ALL) - @JoinTable(name = "adminuser_role", joinColumns = @JoinColumn(name = "admin_user_id"), inverseJoinColumns = @JoinColumn(name = "admin_role_id")) - private Set roles = new HashSet<>(); + @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) + private Set roles = new HashSet<>(); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java deleted file mode 100644 index 99672f57d..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminRoleRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.security.repository; - -import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; - -/** - * Spring Data repository to manage entities of type {@link AdminRole}. - * - * @author Dmitriy Kopylenko - */ -public interface AdminRoleRepository extends JpaRepository { - - Optional findByName(final String name); -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java deleted file mode 100644 index 559156e99..000000000 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/AdminUserRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.security.repository; - -import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; - -/** - * Spring Data repository to manage entities of type {@link AdminUser}. - * - * @author Dmitriy Kopylenko - */ -public interface AdminUserRepository extends JpaRepository { - - Optional findByUsername(String username); -} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/RoleRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/RoleRepository.java new file mode 100644 index 000000000..120f2938e --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/RoleRepository.java @@ -0,0 +1,16 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.repository; + +import edu.internet2.tier.shibboleth.admin.ui.security.model.Role; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +/** + * Spring Data repository to manage entities of type {@link Role}. + * + * @author Dmitriy Kopylenko + */ +public interface RoleRepository extends JpaRepository { + + Optional findByName(final String name); +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserRepository.java new file mode 100644 index 000000000..f19ceb1b7 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserRepository.java @@ -0,0 +1,16 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.repository; + +import edu.internet2.tier.shibboleth.admin.ui.security.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +/** + * Spring Data repository to manage entities of type {@link User}. + * + * @author Dmitriy Kopylenko + */ +public interface UserRepository extends JpaRepository { + + Optional findByUsername(String username); +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java index c1f1c06b8..a2cab06e2 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java @@ -1,12 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.security.springsecurity; -import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminRole; -import edu.internet2.tier.shibboleth.admin.ui.security.model.AdminUser; -import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository; +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.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -23,17 +22,17 @@ @RequiredArgsConstructor public class AdminUserService implements UserDetailsService { - private final AdminUserRepository adminUserRepository; + private final UserRepository userRepository; @Override @Transactional(readOnly = true) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - AdminUser user = adminUserRepository + User user = userRepository .findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException(String.format("User [%s] is not found", username))); Set grantedAuthorities = user.getRoles().stream() - .map(AdminRole::getName) + .map(Role::getName) .map(SimpleGrantedAuthority::new) .collect(toSet()); @@ -42,7 +41,7 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx throw new UsernameNotFoundException(String.format("No roles are defined for user [%s]", username)); } - return new User(user.getUsername(), user.getPassword(), grantedAuthorities); + return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy index 40b92e561..e20b5d8f8 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserServiceTests.groovy @@ -1,12 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.security.springsecurity -import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminRoleRepository -import edu.internet2.tier.shibboleth.admin.ui.security.repository.AdminUserRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest -import org.springframework.context.annotation.Profile import org.springframework.security.core.userdetails.UsernameNotFoundException -import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.ActiveProfiles import spock.lang.Specification @@ -23,10 +21,10 @@ class AdminUserServiceTests extends Specification { AdminUserService adminUserService @Autowired - AdminRoleRepository adminRoleRepository + RoleRepository adminRoleRepository @Autowired - AdminUserRepository adminUserRepository + UserRepository adminUserRepository def "Loading existing admin user with admin role"() { diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 000000000..b8397ecd9 --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,8 @@ +# Security + +Security in the system is controlled by Spring Security. + +Currently, the following roles are recognized: + +1. `ADMIN` +1. `USER` \ No newline at end of file From 94a6e62818e8a21e195cf82cf4c9bd11bce40fa4 Mon Sep 17 00:00:00 2001 From: Jj! Date: Mon, 19 Nov 2018 12:23:24 -0600 Subject: [PATCH 10/10] [SHIBUI-975] update doc --- README.md | 2 +- docs/GETTINGSTARTED.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 07ce59ab0..40788dfe3 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ The easiest way to do this in a servlet container is through the use of system p ## Authentication -Currently, the application is wired with very simple authentication. A password for the user `user` +Currently, the application is wired with very simple authentication. A password for the user `root` can be set with the `shibui.default-password` property. If none is set, a default password will be generated and logged: diff --git a/docs/GETTINGSTARTED.md b/docs/GETTINGSTARTED.md index 65da98a2c..eeed8fae3 100644 --- a/docs/GETTINGSTARTED.md +++ b/docs/GETTINGSTARTED.md @@ -46,7 +46,7 @@ The easiest way to do this in a servlet container is through the use of system p ## Authentication -Currently, the application is wired with very simple authentication. A password for the user `user` +Currently, the application is wired with very simple authentication. A password for the user `root` can be set with the `shibui.default-password` property. If none is set, a default password will be generated and logged: