Move user infos to JWT tokens #19

Merged
william merged 12 commits from feature/12-user-info-in-jwt into develop 2021-12-02 21:58:27 -05:00
10 changed files with 252 additions and 179 deletions
Showing only changes of commit da2a824414 - Show all commits

View File

@ -33,6 +33,8 @@ dependencies {
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.11.3")
implementation("javax.xml.bind:jaxb-api:2.3.0")
implementation("io.jsonwebtoken:jjwt-api:0.11.2")
implementation("io.jsonwebtoken:jjwt-impl:0.11.2")
implementation("io.jsonwebtoken:jjwt-jackson:0.11.2")
implementation("org.apache.poi:poi-ooxml:4.1.0")
implementation("org.apache.pdfbox:pdfbox:2.0.4")
implementation("dev.fyloz.colorrecipesexplorer:database-manager:5.2")
@ -58,9 +60,6 @@ dependencies {
runtimeOnly("mysql:mysql-connector-java:8.0.22")
runtimeOnly("org.postgresql:postgresql:42.2.16")
runtimeOnly("com.microsoft.sqlserver:mssql-jdbc:9.2.1.jre11")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.2")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.2")
}
springBoot {

View File

@ -4,10 +4,8 @@ import dev.fyloz.colorrecipesexplorer.config.properties.CreSecurityProperties
import dev.fyloz.colorrecipesexplorer.emergencyMode
import dev.fyloz.colorrecipesexplorer.model.account.Permission
import dev.fyloz.colorrecipesexplorer.model.account.User
import dev.fyloz.colorrecipesexplorer.model.account.UserDetails
import dev.fyloz.colorrecipesexplorer.model.account.user
import dev.fyloz.colorrecipesexplorer.service.CreUserDetailsService
import dev.fyloz.colorrecipesexplorer.service.UserService
import dev.fyloz.colorrecipesexplorer.service.users.UserDetailsService
import dev.fyloz.colorrecipesexplorer.service.users.UserService
import org.slf4j.Logger
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
@ -23,8 +21,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.AuthenticationEntryPoint
@ -43,7 +39,7 @@ import javax.servlet.http.HttpServletResponse
@EnableConfigurationProperties(CreSecurityProperties::class)
class SecurityConfig(
private val securityProperties: CreSecurityProperties,
@Lazy private val userDetailsService: CreUserDetailsService,
@Lazy private val userDetailsService: UserDetailsService,
@Lazy private val userService: UserService,
private val environment: Environment,
private val logger: Logger
@ -120,6 +116,7 @@ class SecurityConfig(
@EnableConfigurationProperties(CreSecurityProperties::class)
class EmergencySecurityConfig(
private val securityProperties: CreSecurityProperties,
private val userDetailsService: UserDetailsService,
private val environment: Environment
) : WebSecurityConfigurerAdapter() {
init {
@ -134,13 +131,8 @@ class EmergencySecurityConfig(
fun passwordEncoder() =
getPasswordEncoder()
override fun configure(auth: AuthenticationManagerBuilder) {
assertRootUserNotNull(securityProperties)
// Create in-memory root user
auth.inMemoryAuthentication()
.withUser(securityProperties.root!!.id.toString())
.password(passwordEncoder().encode(securityProperties.root!!.password))
.authorities(SimpleGrantedAuthority(Permission.ADMIN.name))
override fun configure(authBuilder: AuthenticationManagerBuilder) {
authBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder())
}
override fun configure(http: HttpSecurity) {
@ -154,7 +146,9 @@ class EmergencySecurityConfig(
JwtAuthenticationFilter(authenticationManager(), securityProperties) { }
)
.addFilter(
JwtAuthorizationFilter(securityProperties, authenticationManager(), this::loadUserById)
JwtAuthorizationFilter(securityProperties, authenticationManager()) {
userDetailsService.loadUserById(it, false)
}
)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
@ -166,18 +160,6 @@ class EmergencySecurityConfig(
http.cors()
}
}
private fun loadUserById(id: Long): UserDetails {
assertRootUserNotNull(securityProperties)
if (id == securityProperties.root!!.id) {
return UserDetails(user(
id = id,
password = securityProperties.root!!.password,
permissions = mutableSetOf(Permission.ADMIN)
))
}
throw UsernameNotFoundException(id.toString())
}
}
@Component

View File

@ -123,11 +123,10 @@ data class UserDetails(val user: User) : SpringUserDetails {
// ==== DSL ====
fun user(
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
id: Long = 0L,
firstName: String = "firstName",
lastName: String = "lastName",
password: String = passwordEncoder.encode("password"),
password: String = "password",
isDefaultGroupUser: Boolean = false,
isSystemUser: Boolean = false,
group: Group? = null,
@ -146,6 +145,30 @@ fun user(
lastLoginTime
).apply(op)
fun user(
id: Long = 0L,
firstName: String = "firstName",
lastName: String = "lastName",
plainPassword: String = "password",
isDefaultGroupUser: Boolean = false,
isSystemUser: Boolean = false,
group: Group? = null,
permissions: MutableSet<Permission> = mutableSetOf(),
lastLoginTime: LocalDateTime? = null,
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
op: User.() -> Unit = {}
) = User(
id,
firstName,
lastName,
passwordEncoder.encode(plainPassword),
isDefaultGroupUser,
isSystemUser,
group,
permissions,
lastLoginTime
).apply(op)
fun userSaveDto(
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
id: Long = 0L,

View File

@ -3,8 +3,8 @@ package dev.fyloz.colorrecipesexplorer.rest
import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeEditUsers
import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewUsers
import dev.fyloz.colorrecipesexplorer.model.account.*
import dev.fyloz.colorrecipesexplorer.service.UserService
import dev.fyloz.colorrecipesexplorer.service.GroupService
import dev.fyloz.colorrecipesexplorer.service.users.GroupService
import dev.fyloz.colorrecipesexplorer.service.users.UserService
import org.springframework.context.annotation.Profile
import org.springframework.http.MediaType
import org.springframework.security.access.prepost.PreAuthorize

View File

@ -5,6 +5,7 @@ import dev.fyloz.colorrecipesexplorer.model.account.Group
import dev.fyloz.colorrecipesexplorer.model.validation.or
import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository
import dev.fyloz.colorrecipesexplorer.service.config.ConfigurationService
import dev.fyloz.colorrecipesexplorer.service.users.GroupService
import dev.fyloz.colorrecipesexplorer.utils.setAll
import org.springframework.context.annotation.Lazy
import org.springframework.context.annotation.Profile

View File

@ -0,0 +1,97 @@
package dev.fyloz.colorrecipesexplorer.service.users
import dev.fyloz.colorrecipesexplorer.config.security.defaultGroupCookieName
import dev.fyloz.colorrecipesexplorer.model.account.*
import dev.fyloz.colorrecipesexplorer.repository.GroupRepository
import dev.fyloz.colorrecipesexplorer.service.AbstractExternalNamedModelService
import dev.fyloz.colorrecipesexplorer.service.ExternalNamedModelService
import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Service
import org.springframework.web.util.WebUtils
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.transaction.Transactional
const val defaultGroupCookieMaxAge = 10 * 365 * 24 * 60 * 60 // 10 ans
interface GroupService :
ExternalNamedModelService<Group, GroupSaveDto, GroupUpdateDto, GroupOutputDto, GroupRepository> {
/** Gets all the users of the group with the given [id]. */
fun getUsersForGroup(id: Long): Collection<User>
/** Gets the default group from a cookie in the given HTTP [request]. */
fun getRequestDefaultGroup(request: HttpServletRequest): Group
/** Sets the default group cookie for the given HTTP [response]. */
fun setResponseDefaultGroup(groupId: Long, response: HttpServletResponse)
}
@Service
@Profile("!emergency")
class GroupServiceImpl(
private val userService: UserService,
groupRepository: GroupRepository
) : AbstractExternalNamedModelService<Group, GroupSaveDto, GroupUpdateDto, GroupOutputDto, GroupRepository>(
groupRepository
),
GroupService {
override fun idNotFoundException(id: Long) = groupIdNotFoundException(id)
override fun idAlreadyExistsException(id: Long) = groupIdAlreadyExistsException(id)
override fun nameNotFoundException(name: String) = groupNameNotFoundException(name)
override fun nameAlreadyExistsException(name: String) = groupNameAlreadyExistsException(name)
override fun Group.toOutput() = GroupOutputDto(
this.id!!,
this.name,
this.permissions,
this.flatPermissions
)
override fun existsByName(name: String): Boolean = repository.existsByName(name)
override fun getUsersForGroup(id: Long): Collection<User> =
userService.getByGroup(getById(id))
@Transactional
override fun save(entity: Group): Group {
return super<AbstractExternalNamedModelService>.save(entity).apply {
userService.saveDefaultGroupUser(this)
}
}
override fun update(entity: GroupUpdateDto): Group {
val persistedGroup by lazy { getById(entity.id) }
return update(with(entity) {
Group(
entity.id,
if (name.isNotBlank()) entity.name else persistedGroup.name,
if (permissions.isNotEmpty()) entity.permissions else persistedGroup.permissions
)
})
}
@Transactional
override fun delete(entity: Group) {
userService.delete(userService.getDefaultGroupUser(entity))
super.delete(entity)
}
override fun getRequestDefaultGroup(request: HttpServletRequest): Group {
val defaultGroupCookie = WebUtils.getCookie(request, defaultGroupCookieName)
?: throw NoDefaultGroupException()
val defaultGroupUser = userService.getById(
defaultGroupCookie.value.toLong(),
ignoreDefaultGroupUsers = false,
ignoreSystemUsers = true
)
return defaultGroupUser.group!!
}
override fun setResponseDefaultGroup(groupId: Long, response: HttpServletResponse) {
val group = getById(groupId)
val defaultGroupUser = userService.getDefaultGroupUser(group)
response.addHeader(
"Set-Cookie",
"$defaultGroupCookieName=${defaultGroupUser.id}; Max-Age=$defaultGroupCookieMaxAge; Path=/api; HttpOnly; Secure; SameSite=strict"
)
}
}

View File

@ -0,0 +1,77 @@
package dev.fyloz.colorrecipesexplorer.service.users
import dev.fyloz.colorrecipesexplorer.SpringUserDetails
import dev.fyloz.colorrecipesexplorer.SpringUserDetailsService
import dev.fyloz.colorrecipesexplorer.config.properties.CreSecurityProperties
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
import dev.fyloz.colorrecipesexplorer.model.account.Permission
import dev.fyloz.colorrecipesexplorer.model.account.User
import dev.fyloz.colorrecipesexplorer.model.account.UserDetails
import dev.fyloz.colorrecipesexplorer.model.account.user
import org.springframework.context.annotation.Profile
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.stereotype.Service
interface UserDetailsService : SpringUserDetailsService {
/** Loads an [User] for the given [id]. */
fun loadUserById(id: Long, ignoreDefaultGroupUsers: Boolean = false): UserDetails
}
@Service
@Profile("!emergency")
class UserDetailsServiceImpl(
private val userService: UserService
) : UserDetailsService {
override fun loadUserByUsername(username: String): UserDetails {
try {
return loadUserById(username.toLong(), true)
} catch (ex: NotFoundException) {
throw UsernameNotFoundException(username)
}
}
override fun loadUserById(id: Long, ignoreDefaultGroupUsers: Boolean): UserDetails {
val user = userService.getById(
id,
ignoreDefaultGroupUsers = ignoreDefaultGroupUsers,
ignoreSystemUsers = false
)
return UserDetails(user)
}
}
@Service
@Profile("emergency")
class EmergencyUserDetailsServiceImpl(
securityProperties: CreSecurityProperties
) : UserDetailsService {
private val users: Set<User>
init {
if (securityProperties.root == null) {
throw NullPointerException("The root user has not been configured")
}
users = setOf(
// Add root user
with(securityProperties.root!!) {
user(
id = this.id,
plainPassword = this.password,
permissions = mutableSetOf(Permission.ADMIN)
)
}
)
}
override fun loadUserByUsername(username: String): SpringUserDetails {
return loadUserById(username.toLong(), true)
}
override fun loadUserById(id: Long, ignoreDefaultGroupUsers: Boolean): UserDetails {
val user = users.firstOrNull { it.id == id }
?: throw UsernameNotFoundException(id.toString())
return UserDetails(user)
}
}

View File

@ -1,23 +1,18 @@
package dev.fyloz.colorrecipesexplorer.service
package dev.fyloz.colorrecipesexplorer.service.users
import dev.fyloz.colorrecipesexplorer.SpringUserDetailsService
import dev.fyloz.colorrecipesexplorer.config.security.blacklistedJwtTokens
import dev.fyloz.colorrecipesexplorer.config.security.defaultGroupCookieName
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
import dev.fyloz.colorrecipesexplorer.model.account.*
import dev.fyloz.colorrecipesexplorer.model.validation.or
import dev.fyloz.colorrecipesexplorer.repository.GroupRepository
import dev.fyloz.colorrecipesexplorer.repository.UserRepository
import dev.fyloz.colorrecipesexplorer.service.AbstractExternalModelService
import dev.fyloz.colorrecipesexplorer.service.ExternalModelService
import org.springframework.context.annotation.Lazy
import org.springframework.context.annotation.Profile
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.web.util.WebUtils
import java.time.LocalDateTime
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.transaction.Transactional
interface UserService :
ExternalModelService<User, UserSaveDto, UserUpdateDto, UserOutputDto, UserRepository> {
@ -55,29 +50,11 @@ interface UserService :
fun logout(request: HttpServletRequest)
}
interface GroupService :
ExternalNamedModelService<Group, GroupSaveDto, GroupUpdateDto, GroupOutputDto, GroupRepository> {
/** Gets all the users of the group with the given [id]. */
fun getUsersForGroup(id: Long): Collection<User>
/** Gets the default group from a cookie in the given HTTP [request]. */
fun getRequestDefaultGroup(request: HttpServletRequest): Group
/** Sets the default group cookie for the given HTTP [response]. */
fun setResponseDefaultGroup(groupId: Long, response: HttpServletResponse)
}
interface CreUserDetailsService : SpringUserDetailsService {
/** Loads an [User] for the given [id]. */
fun loadUserById(id: Long, ignoreDefaultGroupUsers: Boolean = false): UserDetails
}
@Service
@Profile("!emergency")
class UserServiceImpl(
userRepository: UserRepository,
@Lazy val groupService: GroupService,
@Lazy val passwordEncoder: PasswordEncoder,
) : AbstractExternalModelService<User, UserSaveDto, UserUpdateDto, UserOutputDto, UserRepository>(
userRepository
),
@ -120,11 +97,11 @@ class UserServiceImpl(
override fun save(entity: UserSaveDto): User =
save(with(entity) {
User(
id,
firstName,
lastName,
passwordEncoder.encode(password),
user(
id = id,
firstName = firstName,
lastName = lastName,
plainPassword = password,
isDefaultGroupUser = false,
isSystemUser = false,
group = if (groupId != null) groupService.getById(groupId) else null,
@ -146,7 +123,7 @@ class UserServiceImpl(
id = 1000000L + group.id!!,
firstName = group.name,
lastName = "User",
password = passwordEncoder.encode(group.name),
plainPassword = group.name,
group = group,
isDefaultGroupUser = true
)
@ -195,11 +172,11 @@ class UserServiceImpl(
override fun updatePassword(id: Long, password: String): User {
val persistedUser = getById(id, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true)
return super.update(with(persistedUser) {
User(
user(
id,
firstName,
lastName,
passwordEncoder.encode(password),
plainPassword = password,
isDefaultGroupUser,
isSystemUser,
group,
@ -225,100 +202,3 @@ class UserServiceImpl(
}
}
}
const val defaultGroupCookieMaxAge = 10 * 365 * 24 * 60 * 60 // 10 ans
@Service
@Profile("!emergency")
class GroupServiceImpl(
private val userService: UserService,
groupRepository: GroupRepository
) : AbstractExternalNamedModelService<Group, GroupSaveDto, GroupUpdateDto, GroupOutputDto, GroupRepository>(
groupRepository
),
GroupService {
override fun idNotFoundException(id: Long) = groupIdNotFoundException(id)
override fun idAlreadyExistsException(id: Long) = groupIdAlreadyExistsException(id)
override fun nameNotFoundException(name: String) = groupNameNotFoundException(name)
override fun nameAlreadyExistsException(name: String) = groupNameAlreadyExistsException(name)
override fun Group.toOutput() = GroupOutputDto(
this.id!!,
this.name,
this.permissions,
this.flatPermissions
)
override fun existsByName(name: String): Boolean = repository.existsByName(name)
override fun getUsersForGroup(id: Long): Collection<User> =
userService.getByGroup(getById(id))
@Transactional
override fun save(entity: Group): Group {
return super<AbstractExternalNamedModelService>.save(entity).apply {
userService.saveDefaultGroupUser(this)
}
}
override fun update(entity: GroupUpdateDto): Group {
val persistedGroup by lazy { getById(entity.id) }
return update(with(entity) {
Group(
entity.id,
if (name.isNotBlank()) entity.name else persistedGroup.name,
if (permissions.isNotEmpty()) entity.permissions else persistedGroup.permissions
)
})
}
@Transactional
override fun delete(entity: Group) {
userService.delete(userService.getDefaultGroupUser(entity))
super.delete(entity)
}
override fun getRequestDefaultGroup(request: HttpServletRequest): Group {
val defaultGroupCookie = WebUtils.getCookie(request, defaultGroupCookieName)
?: throw NoDefaultGroupException()
val defaultGroupUser = userService.getById(
defaultGroupCookie.value.toLong(),
ignoreDefaultGroupUsers = false,
ignoreSystemUsers = true
)
return defaultGroupUser.group!!
}
override fun setResponseDefaultGroup(groupId: Long, response: HttpServletResponse) {
val group = getById(groupId)
val defaultGroupUser = userService.getDefaultGroupUser(group)
response.addHeader(
"Set-Cookie",
"$defaultGroupCookieName=${defaultGroupUser.id}; Max-Age=${defaultGroupCookieMaxAge}; Path=/api; HttpOnly; Secure; SameSite=strict"
)
}
}
@Service
@Profile("!emergency")
class CreUserDetailsServiceImpl(
private val userService: UserService
) : CreUserDetailsService {
override fun loadUserByUsername(username: String): UserDetails {
try {
return loadUserById(username.toLong(), true)
} catch (ex: NotFoundException) {
throw UsernameNotFoundException(username)
} catch (ex: NotFoundException) {
throw UsernameNotFoundException(username)
}
}
override fun loadUserById(id: Long, ignoreDefaultGroupUsers: Boolean): UserDetails {
val user = userService.getById(
id,
ignoreDefaultGroupUsers = ignoreDefaultGroupUsers,
ignoreSystemUsers = false
)
return UserDetails(user)
}
}

View File

@ -1,6 +1,7 @@
package dev.fyloz.colorrecipesexplorer.utils
import dev.fyloz.colorrecipesexplorer.model.account.User
import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.io.Encoders
@ -13,13 +14,13 @@ data class Jwt(
val secret: String,
val duration: Long? = null,
val signatureAlgorithm: SignatureAlgorithm = SignatureAlgorithm.HS512,
val claims: Map<String, Any?> = mapOf()
val claims: Map<JwtClaim, Any?> = mapOf()
) {
val token: String by lazy {
val builder = Jwts.builder()
.signWith(keyFromSecret(secret))
.setSubject(subject)
.addClaims(claims.filterValues { it != null })
.addClaims(mappedClaims)
duration?.let {
val expirationMs = System.currentTimeMillis() + it
@ -30,6 +31,18 @@ data class Jwt(
builder.compact()
}
private val mappedClaims: Map<String, String> by lazy {
claims
.filterValues { it != null }
.mapKeys { it.key.claim }
.mapValues { it.value.toString() }
}
}
enum class JwtClaim(val claim: String) {
GROUP_ID("groupId"),
GROUP_NAME("groupName")
}
/** Build a [Jwt] for the given [User]. */
@ -39,8 +52,8 @@ fun User.buildJwt(secret: String, duration: Long?) =
secret,
duration,
claims = mapOf(
"groupId" to this.group?.id,
"groupName" to this.group?.name
JwtClaim.GROUP_ID to this.group?.id,
JwtClaim.GROUP_NAME to this.group?.name
)
)

View File

@ -5,8 +5,9 @@ import dev.fyloz.colorrecipesexplorer.config.security.defaultGroupCookieName
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
import dev.fyloz.colorrecipesexplorer.model.account.*
import dev.fyloz.colorrecipesexplorer.repository.UserRepository
import dev.fyloz.colorrecipesexplorer.repository.GroupRepository
import dev.fyloz.colorrecipesexplorer.repository.UserRepository
import dev.fyloz.colorrecipesexplorer.service.users.*
import org.junit.jupiter.api.*
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.core.userdetails.UsernameNotFoundException
@ -25,17 +26,17 @@ class UserServiceTest :
AbstractExternalModelServiceTest<User, UserSaveDto, UserUpdateDto, UserService, UserRepository>() {
private val passwordEncoder = BCryptPasswordEncoder()
override val entity: User = user(passwordEncoder, id = 0L)
override val anotherEntity: User = user(passwordEncoder, id = 1L)
private val entityDefaultGroupUser = user(passwordEncoder, id = 2L, isDefaultGroupUser = true)
private val entitySystemUser = user(passwordEncoder, id = 3L, isSystemUser = true)
override val entity: User = user(id = 0L, passwordEncoder = passwordEncoder)
override val anotherEntity: User = user(id = 1L, passwordEncoder = passwordEncoder)
private val entityDefaultGroupUser = user(id = 2L, isDefaultGroupUser = true, passwordEncoder = passwordEncoder)
private val entitySystemUser = user(id = 3L, isSystemUser = true, passwordEncoder = passwordEncoder)
private val group = group(id = 0L)
override val entitySaveDto: UserSaveDto = spy(userSaveDto(passwordEncoder, id = 0L))
override val entityUpdateDto: UserUpdateDto = spy(userUpdateDto(id = 0L))
override val repository: UserRepository = mock()
private val groupService: GroupService = mock()
override val service: UserService = spy(UserServiceImpl(repository, groupService, passwordEncoder))
override val service: UserService = spy(UserServiceImpl(repository, groupService))
private val entitySaveDtoUser = User(
entitySaveDto.id,
@ -210,7 +211,7 @@ class GroupServiceTest :
override val entityWithEntityName: Group = group(id = 2L, name = entity.name)
private val groupUserId = 1000000L + entity.id!!
private val groupUser = user(BCryptPasswordEncoder(), id = groupUserId, group = entity)
private val groupUser = user(passwordEncoder = BCryptPasswordEncoder(), id = groupUserId, group = entity)
@BeforeEach
override fun afterEach() {
@ -303,7 +304,7 @@ class GroupServiceTest :
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class UserUserDetailsServiceTest {
private val userService: UserService = mock()
private val service = spy(CreUserDetailsServiceImpl(userService))
private val service = spy(UserDetailsServiceImpl(userService))
private val user = user(id = 0L)
@ -317,8 +318,8 @@ class UserUserDetailsServiceTest {
@Test
fun `loadUserByUsername() calls loadUserByUserId() with the given username as an id`() {
whenever(userService.getById(eq(user.id), any(), any())).doReturn(user)
doReturn(SpringUser(user.id.toString(), user.password, listOf())).whenever(service)
.loadUserById(user.id)
doReturn(UserDetails(user(id = user.id, plainPassword = user.password)))
.whenever(service).loadUserById(user.id)
service.loadUserByUsername(user.id.toString())