Skip to content

Commit

Permalink
[auth] password update api
Browse files Browse the repository at this point in the history
  • Loading branch information
Noverish committed May 6, 2024
1 parent 529c3ff commit 35c05dd
Show file tree
Hide file tree
Showing 14 changed files with 131 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package kim.hyunsub.auth.bo.auth

import at.favre.lib.crypto.bcrypt.BCrypt
import jakarta.servlet.http.HttpServletResponse
import kim.hyunsub.auth.model.LoginFailureSession
import kim.hyunsub.auth.model.dto.auth.LoginApiError
Expand All @@ -10,6 +9,7 @@ import kim.hyunsub.auth.repository.UserRepository
import kim.hyunsub.auth.service.CaptchaService
import kim.hyunsub.auth.service.CookieGenerator
import kim.hyunsub.auth.service.LoginFailureSessionService
import kim.hyunsub.auth.service.PasswordService
import kim.hyunsub.auth.service.RsaKeyService
import kim.hyunsub.auth.service.TokenService
import kim.hyunsub.common.web.error.ErrorCode
Expand All @@ -24,6 +24,7 @@ class LoginBo(
private val tokenService: TokenService,
private val cookieGenerator: CookieGenerator,
private val rsaKeyService: RsaKeyService,
private val passwordService: PasswordService,
) {
fun login(params: LoginParams, remoteAddr: String, res: HttpServletResponse): LoginResult {
val session = loginFailureSessionService.getOrNull(remoteAddr)
Expand Down Expand Up @@ -64,9 +65,7 @@ class LoginBo(
val user = userRepository.findByUsername(params.username)
?: throw ErrorCodeException(ErrorCode.NOT_EXIST_USER)

val hashedPw = user.password
val correct = BCrypt.verifyer().verify(params.password.toCharArray(), hashedPw).verified
if (!correct) {
if (passwordService.isWrong(user, params.password)) {
throw ErrorCodeException(ErrorCode.NOT_EXIST_USER)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package kim.hyunsub.auth.bo.auth

import kim.hyunsub.auth.model.AuthStatus
import kim.hyunsub.auth.model.dto.auth.RegisterParams
import kim.hyunsub.auth.model.dto.auth.RegisterResult
import kim.hyunsub.auth.model.dto.auth.UserCreateParams
import kim.hyunsub.auth.repository.UserRepository
import kim.hyunsub.auth.service.CaptchaService
import kim.hyunsub.auth.service.PasswordService
import kim.hyunsub.auth.service.RsaKeyService
import kim.hyunsub.auth.service.UserService
import kim.hyunsub.common.web.error.ErrorCode
import kim.hyunsub.common.web.error.ErrorCodeException
import mu.KotlinLogging
import org.springframework.stereotype.Service

@Service
Expand All @@ -18,9 +19,8 @@ class RegisterBo(
private val captchaService: CaptchaService,
private val rsaKeyService: RsaKeyService,
private val userService: UserService,
private val passwordService: PasswordService,
) {
private val log = KotlinLogging.logger { }

fun register(params: RegisterParams, remoteAddr: String): RegisterResult {
val username = rsaKeyService.decrypt(params.username)
val password = rsaKeyService.decrypt(params.password)
Expand All @@ -39,17 +39,17 @@ class RegisterBo(
throw ErrorCodeException(ErrorCode.SHORT_USERNAME)
}

if (password.length < 8) {
throw ErrorCodeException(ErrorCode.SHORT_PASSWORD)
if (!passwordService.isValidLength(password)) {
return RegisterResult(AuthStatus.INVALID_LENGTH_PASSWORD)
}

val user = userService.create(
userService.create(
UserCreateParams(
username = username,
password = password,
)
)

return RegisterResult(user.user.idNo)
return RegisterResult(AuthStatus.SUCCESS)
}
}
10 changes: 0 additions & 10 deletions hyunsub-auth/src/main/kotlin/kim/hyunsub/auth/bo/user/ProfileBo.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package kim.hyunsub.auth.bo.user

import at.favre.lib.crypto.bcrypt.BCrypt
import jakarta.servlet.http.HttpServletResponse
import kim.hyunsub.auth.config.AuthConstants
import kim.hyunsub.auth.model.api.ApiProfile
import kim.hyunsub.auth.model.dto.user.ProfileUpdateParams
import kim.hyunsub.auth.model.dto.user.ProfileUpdateResult
Expand Down Expand Up @@ -40,13 +38,6 @@ class ProfileBo(
newUser = newUser.copy(username = decrypted)
}

params.password?.let { password ->
val decrypted = rsaKeyService.decrypt(password)
log.debug("updateUserInfo: password={}", decrypted)
val hashed = BCrypt.withDefaults().hashToString(AuthConstants.BCRYPT_COST, decrypted.toCharArray())
newUser = newUser.copy(password = hashed)
}

params.language?.let {
newUser = newUser.copy(lang = it)
val cookie = cookieGenerator.generateLanguageCookie(it)
Expand All @@ -55,7 +46,6 @@ class ProfileBo(

val result = ProfileUpdateResult(
username = params.username?.let { user.username != newUser.username },
password = params.password?.let { user.password != newUser.password },
language = params.language?.let { user.lang != newUser.lang },
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package kim.hyunsub.auth.bo.user

import kim.hyunsub.auth.model.AuthStatus
import kim.hyunsub.auth.model.dto.user.ProfilePasswordParams
import kim.hyunsub.auth.model.dto.user.ProfilePasswordResult
import kim.hyunsub.auth.repository.UserRepository
import kim.hyunsub.auth.service.PasswordService
import kim.hyunsub.auth.service.RsaKeyService
import kim.hyunsub.common.web.error.ErrorCode
import kim.hyunsub.common.web.error.ErrorCodeException
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service

@Service
class ProfilePasswordBo(
private val userRepository: UserRepository,
private val rsaKeyService: RsaKeyService,
private val passwordService: PasswordService,
) {
fun change(userId: String, params: ProfilePasswordParams): ProfilePasswordResult {
val user = userRepository.findByIdOrNull(userId)
?: throw ErrorCodeException(ErrorCode.NO_SUCH_USER)

val oldPw = rsaKeyService.decrypt(params.oldPw)
val newPw = rsaKeyService.decrypt(params.newPw)

if (!passwordService.isValidLength(newPw)) {
return ProfilePasswordResult(AuthStatus.INVALID_LENGTH_PASSWORD)
}

if (passwordService.isWrong(user, oldPw)) {
return ProfilePasswordResult(AuthStatus.WRONG_PASSWORD)
}

if (oldPw == newPw) {
return ProfilePasswordResult(AuthStatus.CURRENT_PASSWORD)
}

val newHash = passwordService.generateHash(newPw)
val newUser = user.copy(password = newHash)
userRepository.save(newUser)

return ProfilePasswordResult(AuthStatus.SUCCESS)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package kim.hyunsub.auth.controller.user

import kim.hyunsub.auth.bo.user.ProfilePasswordBo
import kim.hyunsub.auth.model.dto.user.ProfilePasswordParams
import kim.hyunsub.auth.model.dto.user.ProfilePasswordResult
import kim.hyunsub.common.web.model.UserAuth
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api/v1/profile/password")
class ProfilePasswordController(
private val profilePasswordBo: ProfilePasswordBo,
) {
@PutMapping("")
fun update(
userAuth: UserAuth,
@RequestBody params: ProfilePasswordParams,
): ProfilePasswordResult {
return profilePasswordBo.change(userAuth.idNo, params)
}
}
14 changes: 14 additions & 0 deletions hyunsub-auth/src/main/kotlin/kim/hyunsub/auth/model/AuthStatus.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package kim.hyunsub.auth.model

import com.fasterxml.jackson.annotation.JsonValue

enum class AuthStatus(
@JsonValue val code: Int,
) {
SUCCESS(0),

INVALID_LENGTH_PASSWORD(2000),
WRONG_PASSWORD(2001),
CURRENT_PASSWORD(2002),
;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package kim.hyunsub.auth.model.dto.auth

import kim.hyunsub.auth.model.AuthStatus

data class RegisterResult(
val idNo: String,
val status: AuthStatus,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kim.hyunsub.auth.model.dto.user

data class ProfilePasswordParams(
val oldPw: String,
val newPw: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kim.hyunsub.auth.model.dto.user

import kim.hyunsub.auth.model.AuthStatus

data class ProfilePasswordResult(
val status: AuthStatus,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ import kim.hyunsub.auth.model.UserLanguage

data class ProfileUpdateParams(
val username: String?,
val password: String?,
val language: UserLanguage?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include
@JsonInclude(Include.NON_NULL)
data class ProfileUpdateResult(
val username: Boolean? = null,
val password: Boolean? = null,
val language: Boolean? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kim.hyunsub.auth.service

import at.favre.lib.crypto.bcrypt.BCrypt
import kim.hyunsub.auth.config.AuthConstants
import kim.hyunsub.auth.repository.entity.User
import org.springframework.stereotype.Service

@Service
class PasswordService {
fun isValidLength(password: String): Boolean =
password.length in 8..255

fun generateHash(password: String): String =
BCrypt.withDefaults().hashToString(AuthConstants.BCRYPT_COST, password.toCharArray())

fun isWrong(user: User, password: String): Boolean {
val correct = BCrypt.verifyer().verify(password.toCharArray(), user.password).verified
return !correct
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package kim.hyunsub.auth.service

import at.favre.lib.crypto.bcrypt.BCrypt
import kim.hyunsub.auth.config.AuthConstants
import kim.hyunsub.auth.model.dto.auth.UserCreateParams
import kim.hyunsub.auth.model.dto.auth.UserCreateResult
import kim.hyunsub.auth.repository.AuthorityRepository
Expand All @@ -22,14 +20,13 @@ class UserService(
private val authorityRepository: AuthorityRepository,
private val profileService: ProfileService,
private val otherServiceUserNotifier: OtherServiceUserNotifier,
private val passwordService: PasswordService,
) {
fun create(params: UserCreateParams): UserCreateResult {
val hashed = BCrypt.withDefaults().hashToString(AuthConstants.BCRYPT_COST, params.password.toCharArray())

val user = User(
idNo = userRepository.generateId(),
username = params.username,
password = hashed,
password = passwordService.generateHash(params.password),
)

val authorities = authorityRepository.findDefaults()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ enum class ErrorCode(
ALREADY_EXIST_USERNAME(1001, "Already exist username"),
NOT_EXIST_USER(1002, "Invalid ID or password"),
SHORT_USERNAME(1003, "ID should be longer than 3 characters"),
SHORT_PASSWORD(1004, "Password must be longer than 7 characters"),

NOT_LOGIN(2001, "Login Required", HttpStatus.UNAUTHORIZED),
INVALID_JWT(2002, "Invalid Login Information", HttpStatus.UNAUTHORIZED),
Expand Down

0 comments on commit 35c05dd

Please sign in to comment.