From 3d0ab269c2773a099cd3139b01fb63a3c83d0d76 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 12 Nov 2018 19:08:54 -0500 Subject: [PATCH] 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