Skip to content

Commit

Permalink
feat: simple implementation of authController
Browse files Browse the repository at this point in the history
  • Loading branch information
ShiinaKin committed Sep 12, 2024
1 parent 7ce5018 commit fc105fb
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 0 deletions.
61 changes: 61 additions & 0 deletions app/src/main/kotlin/io/sakurasou/controller/AuthController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.sakurasou.controller

import io.github.smiley4.ktorswaggerui.dsl.routing.post
import io.github.smiley4.ktorswaggerui.dsl.routing.route
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.sakurasou.controller.request.UserInsertRequest
import io.sakurasou.controller.request.UserLoginRequest
import io.sakurasou.model.dao.relation.RelationDao
import io.sakurasou.model.dao.user.UserDao
import io.sakurasou.service.auth.AuthService
import io.sakurasou.service.auth.AuthServiceImpl

/**
* @author Shiina Kin
* 2024/9/12 10:14
*/
fun Route.authRoute(userDao: UserDao, relationDao: RelationDao) {
val authService = AuthServiceImpl(userDao, relationDao)
val authController = AuthController(authService)
route("user", {
protected = false
}) {
post("login", {
request {
body<UserLoginRequest> {
required = true
}
}
}) {
val loginRequest = call.receive<UserLoginRequest>()
val token = authController.handleLogin(loginRequest)
call.respond(mapOf("token" to token))
}
post("signup", {
protected = false
request {
body<UserInsertRequest> {
required = true
}
}
}) {
val userInsertRequest = call.receive<UserInsertRequest>()
authController.handleSignup(userInsertRequest)
}
}
}

class AuthController(
private val authService: AuthService
) {
suspend fun handleLogin(loginRequest: UserLoginRequest): String {
return authService.login(loginRequest)
}

suspend fun handleSignup(userInsertRequest: UserInsertRequest) {
authService.saveUser(userInsertRequest)
}
}
13 changes: 13 additions & 0 deletions app/src/main/kotlin/io/sakurasou/service/auth/AuthService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.sakurasou.service.auth

import io.sakurasou.controller.request.UserInsertRequest
import io.sakurasou.controller.request.UserLoginRequest

/**
* @author Shiina Kin
* 2024/9/12 12:54
*/
interface AuthService {
suspend fun login(loginRequest: UserLoginRequest): String
suspend fun saveUser(userInsertRequest: UserInsertRequest)
}
77 changes: 77 additions & 0 deletions app/src/main/kotlin/io/sakurasou/service/auth/AuthServiceImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package io.sakurasou.service.auth

import at.favre.lib.crypto.bcrypt.BCrypt
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import io.sakurasou.config.JwtConfig.audience
import io.sakurasou.config.JwtConfig.issuer
import io.sakurasou.config.JwtConfig.jwkProvider
import io.sakurasou.controller.request.UserInsertRequest
import io.sakurasou.controller.request.UserLoginRequest
import io.sakurasou.exception.UnauthorizedAccessException
import io.sakurasou.exception.UserNotFoundException
import io.sakurasou.model.DatabaseSingleton.dbQuery
import io.sakurasou.model.dao.relation.RelationDao
import io.sakurasou.model.dao.user.UserDao
import io.sakurasou.model.dto.UserInsertDTO
import kotlinx.datetime.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toJavaInstant
import kotlinx.datetime.toLocalDateTime
import java.security.KeyFactory
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.PKCS8EncodedKeySpec
import kotlin.time.Duration

/**
* @author Shiina Kin
* 2024/9/12 12:55
*/
class AuthServiceImpl(
private val userDao: UserDao,
private val relationDao: RelationDao
) : AuthService {
override suspend fun login(loginRequest: UserLoginRequest): String {
val user = dbQuery {
userDao.findUserByName(loginRequest.username) ?: run { throw UserNotFoundException() }
}

val isCorrectPassword = BCrypt.verifyer().verify(loginRequest.password.toCharArray(), user.password)
if (!isCorrectPassword.verified) throw UnauthorizedAccessException()

val role: List<String> = relationDao.listRoleByGroupId(user.groupId)

val publicKey = jwkProvider.get("6f8856ed-9189-488f-9011-0ff4b6c08edc").publicKey
val keySpecPKCS8 = PKCS8EncodedKeySpec(publicKey.encoded)
val privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpecPKCS8)
val token = JWT.create()
.withAudience(audience)
.withIssuer(issuer)
.withClaim("id", user.id)
.withClaim("username", user.name)
.withClaim("groupId", user.groupId)
.withClaim("role", role)
.withExpiresAt(Clock.System.now().plus(Duration.parse("3d")).toJavaInstant())
.sign(Algorithm.RSA256(publicKey as RSAPublicKey, privateKey as RSAPrivateKey))
return token
}

override suspend fun saveUser(userInsertRequest: UserInsertRequest) {
val rowPassword = userInsertRequest.password
val encodePassword = BCrypt.withDefaults().hashToString(12, rowPassword.toCharArray())

val now = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())
val userInsertDTO = UserInsertDTO(
userInsertRequest.groupId,
userInsertRequest.username,
encodePassword,
userInsertRequest.email,
now,
now
)
dbQuery {
userDao.saveUser(userInsertDTO)
}
}
}

0 comments on commit fc105fb

Please sign in to comment.