diff --git a/build.gradle.kts b/build.gradle.kts index 8c57eda..d1502a1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 { diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/security/SecurityConfig.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/security/SecurityConfig.kt index 6835dc2..323706e 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/security/SecurityConfig.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/config/security/SecurityConfig.kt @@ -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 diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/User.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/User.kt index 8f0559a..8fbee34 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/User.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/model/account/User.kt @@ -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 = 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, diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt index 2230093..42ab55e 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/rest/AccountControllers.kt @@ -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 diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt index 4dcda07..f878df3 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/RecipeService.kt @@ -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 diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/GroupService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/GroupService.kt new file mode 100644 index 0000000..62ac1f0 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/GroupService.kt @@ -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 { + /** Gets all the users of the group with the given [id]. */ + fun getUsersForGroup(id: Long): Collection + + /** 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( + 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 = + userService.getByGroup(getById(id)) + + @Transactional + override fun save(entity: Group): Group { + return super.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" + ) + } +} diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/UserDetailsService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/UserDetailsService.kt new file mode 100644 index 0000000..923abed --- /dev/null +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/UserDetailsService.kt @@ -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 + + 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) + } +} diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/UserService.kt similarity index 60% rename from src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt rename to src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/UserService.kt index fcb0122..b1df3ea 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/users/UserService.kt @@ -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 { @@ -55,29 +50,11 @@ interface UserService : fun logout(request: HttpServletRequest) } -interface GroupService : - ExternalNamedModelService { - /** Gets all the users of the group with the given [id]. */ - fun getUsersForGroup(id: Long): Collection - - /** 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( 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( - 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 = - userService.getByGroup(getById(id)) - - @Transactional - override fun save(entity: Group): Group { - return super.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) - } -} diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/utils/Jwt.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/utils/Jwt.kt index b36527e..dbfb2df 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/utils/Jwt.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/utils/Jwt.kt @@ -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 = mapOf() + val claims: Map = 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 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 ) ) diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt index a9979b7..48bce7d 100644 --- a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/AccountsServiceTest.kt @@ -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() { 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())