diff --git a/src/main/java/io/gaia_app/config/security/SecurityConfig.kt b/src/main/java/io/gaia_app/config/security/SecurityConfig.kt index 7fbfc6b99..2a823649a 100644 --- a/src/main/java/io/gaia_app/config/security/SecurityConfig.kt +++ b/src/main/java/io/gaia_app/config/security/SecurityConfig.kt @@ -1,13 +1,17 @@ package io.gaia_app.config.security +import io.gaia_app.teams.repository.UserRepository import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity 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.core.GrantedAuthority +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.core.userdetails.User +import org.springframework.security.core.userdetails.UserDetailsService import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.web.csrf.CookieCsrfTokenRepository @@ -19,7 +23,8 @@ class SecurityConfig( val failureHandler: FailureHandler, val logoutSuccessHandler: LogoutSuccessHandler, val accessDeniedHandler: AccessDeniedHandler, - val authenticationEntryPoint: AuthenticationEntryPoint) : WebSecurityConfigurerAdapter() { + val authenticationEntryPoint: AuthenticationEntryPoint, + val userRepository: UserRepository) : WebSecurityConfigurerAdapter() { @Value("\${gaia.admin-password:admin123}") private val adminPassword: String? = null @@ -74,15 +79,21 @@ class SecurityConfig( // @formatter:on } - public override fun configure(auth: AuthenticationManagerBuilder) { - // @formatter:off - auth - .inMemoryAuthentication() - // configure default admin user - .withUser("admin").password(bcrypt().encode(adminPassword)).authorities("ROLE_ADMIN", "ROLE_USER") - .and() - .withUser("user").password(bcrypt().encode("user123")).authorities("ROLE_USER") - // @formatter:on + @Bean + override fun userDetailsService(): UserDetailsService { + return UserDetailsService { + username:String -> userRepository.findById(username) + .map { User.builder().username(it.username).password(it.password).authorities(it.toAuthorities()).build() } + .orElseThrow() + } } } + +fun io.gaia_app.teams.User.toAuthorities(): List { + return if (this.isAdmin) { + listOf(SimpleGrantedAuthority("ROLE_ADMIN"), SimpleGrantedAuthority("ROLE_USER")) + } else { + listOf(SimpleGrantedAuthority("ROLE_USER")) + } +} diff --git a/src/main/java/io/gaia_app/teams/bo.kt b/src/main/java/io/gaia_app/teams/bo.kt index 03c795dd0..b23b68a8f 100644 --- a/src/main/java/io/gaia_app/teams/bo.kt +++ b/src/main/java/io/gaia_app/teams/bo.kt @@ -1,6 +1,8 @@ package io.gaia_app.teams +import com.fasterxml.jackson.annotation.JsonBackReference import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonProperty import org.springframework.data.annotation.Id import org.springframework.data.mongodb.core.mapping.DBRef @@ -19,6 +21,9 @@ data class User( var isAdmin: Boolean = false var oAuth2User: OAuth2User? = null + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + var password: String? = null } /** diff --git a/src/test/java/io/gaia_app/config/security/SecurityConfigIT.kt b/src/test/java/io/gaia_app/config/security/SecurityConfigIT.kt index 2b170afb0..b35870936 100644 --- a/src/test/java/io/gaia_app/config/security/SecurityConfigIT.kt +++ b/src/test/java/io/gaia_app/config/security/SecurityConfigIT.kt @@ -1,9 +1,11 @@ package io.gaia_app.config.security +import io.gaia_app.teams.User +import io.gaia_app.teams.repository.UserRepository import io.gaia_app.test.any +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyNoInteractions +import org.mockito.Mockito.* import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.boot.test.context.SpringBootTest @@ -21,6 +23,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController +import java.util.* @SpringBootTest(classes = [SecurityConfig::class, SecurityConfigIT.FakeRestController::class]) @AutoConfigureMockMvc @@ -45,6 +48,16 @@ class SecurityConfigIT { @MockBean lateinit var authenticationEntryPoint: AuthenticationEntryPoint + @MockBean + lateinit var userRepository: UserRepository + + @BeforeEach + internal fun setUp() { + val adminUser = User("admin", null) + adminUser.password = "\$2a\$10\$hr8QjaJ0ync5OQoCtoown.XKplCdhAnyfkWaCf9fto9Cd4470hO/e" + `when`(userRepository.findById("admin")).thenReturn(Optional.of(adminUser)); + } + @Test fun `security should not authenticate assets access`() { mockMvc.perform(get("/assets/img/gaia.png")) @@ -84,7 +97,7 @@ class SecurityConfigIT { @Test fun `security should handle login success`() { - mockMvc.perform(formLogin("/auth/classic").user("admin").password("admin456")) + mockMvc.perform(formLogin("/auth/classic").user("admin").password("admin123")) .andExpect(authenticated().withUsername("admin")) verify(successHandler).onAuthenticationSuccess(any(), any(), any()) } diff --git a/src/test/java/io/gaia_app/teams/controller/UsersRestControllerIT.java b/src/test/java/io/gaia_app/teams/controller/UsersRestControllerIT.java index bce63e18c..7a5714960 100644 --- a/src/test/java/io/gaia_app/teams/controller/UsersRestControllerIT.java +++ b/src/test/java/io/gaia_app/teams/controller/UsersRestControllerIT.java @@ -73,16 +73,16 @@ void users_shouldBeExposed_atSpecificUrl() throws Exception { @Test void saveUser_shouldBeExposed_atSpecificUrl() throws Exception { - mockMvc.perform(put("/api/users/test") + mockMvc.perform(put("/api/users/Luke Skywalker") .with(csrf()) .contentType(MediaType.APPLICATION_JSON) - .content("{\"username\":\"Bob\"}")) + .content("{\"username\":\"Luke Skywalker\"}")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.username", is("Bob"))) + .andExpect(jsonPath("$.username", is("Luke Skywalker"))) .andExpect(jsonPath("$.admin", is(false))) .andExpect(jsonPath("$.team", isEmptyOrNullString())); - assertThat(userRepository.existsById("Bob")).isTrue(); + assertThat(userRepository.existsById("Luke Skywalker")).isTrue(); } @Test diff --git a/src/test/java/io/gaia_app/teams/controller/UsersRestControllerTest.java b/src/test/java/io/gaia_app/teams/controller/UsersRestControllerTest.java index 02b678f3f..be67faf14 100644 --- a/src/test/java/io/gaia_app/teams/controller/UsersRestControllerTest.java +++ b/src/test/java/io/gaia_app/teams/controller/UsersRestControllerTest.java @@ -1,7 +1,7 @@ package io.gaia_app.teams.controller; import io.gaia_app.teams.User; -import io.gaia_app.teams.repository.UserRepository; +import io.gaia_app.teams.UserService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -14,7 +14,7 @@ class UsersRestControllerTest { @Mock - private UserRepository userRepository; + private UserService userService; @InjectMocks private UsersRestController usersRestController; @@ -23,7 +23,7 @@ class UsersRestControllerTest { void users_shouldReturnAllTeams() { usersRestController.users(); - verify(userRepository).findAll(); + verify(userService).findAll(); } @Test @@ -31,6 +31,6 @@ void saveUser_shouldSaveTheUser() { var john = new User("john", null); usersRestController.saveUser(john); - verify(userRepository).save(john); + verify(userService).update(john); } }