Skip to content

Commit

Permalink
πŸ”’ : authenticate users with database password
Browse files Browse the repository at this point in the history
  • Loading branch information
juwit committed Dec 28, 2020
1 parent 685db79 commit 2b6d435
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 22 deletions.
33 changes: 22 additions & 11 deletions src/main/java/io/gaia_app/config/security/SecurityConfig.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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<GrantedAuthority> {
return if (this.isAdmin) {
listOf(SimpleGrantedAuthority("ROLE_ADMIN"), SimpleGrantedAuthority("ROLE_USER"))
} else {
listOf(SimpleGrantedAuthority("ROLE_USER"))
}
}
5 changes: 5 additions & 0 deletions src/main/java/io/gaia_app/teams/bo.kt
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
}

/**
Expand Down
19 changes: 16 additions & 3 deletions src/test/java/io/gaia_app/config/security/SecurityConfigIT.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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"))
Expand Down Expand Up @@ -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())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,7 +14,7 @@
class UsersRestControllerTest {

@Mock
private UserRepository userRepository;
private UserService userService;

@InjectMocks
private UsersRestController usersRestController;
Expand All @@ -23,14 +23,14 @@ class UsersRestControllerTest {
void users_shouldReturnAllTeams() {
usersRestController.users();

verify(userRepository).findAll();
verify(userService).findAll();
}

@Test
void saveUser_shouldSaveTheUser() {
var john = new User("john", null);
usersRestController.saveUser(john);

verify(userRepository).save(john);
verify(userService).update(john);
}
}

0 comments on commit 2b6d435

Please sign in to comment.