Use UserService

This commit is contained in:
Hubert Van De Walle 2020-04-20 16:00:34 +02:00
parent d6f2489d50
commit dc7f6b7b3a
3 changed files with 58 additions and 47 deletions

View File

@ -2,9 +2,8 @@ package be.vandewalleh.controllers
import be.vandewalleh.auth.SimpleJWT import be.vandewalleh.auth.SimpleJWT
import be.vandewalleh.auth.UsernamePasswordCredential import be.vandewalleh.auth.UsernamePasswordCredential
import be.vandewalleh.entities.User import be.vandewalleh.services.UserRegistrationDto
import be.vandewalleh.errors.ApiError import be.vandewalleh.services.UserService
import be.vandewalleh.tables.Users
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.locations.Location import io.ktor.locations.Location
@ -12,37 +11,26 @@ import io.ktor.locations.post
import io.ktor.request.receive import io.ktor.request.receive
import io.ktor.response.respond import io.ktor.response.respond
import io.ktor.routing.Routing import io.ktor.routing.Routing
import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.add
import me.liuwj.ktorm.entity.sequenceOf
import org.kodein.di.Kodein import org.kodein.di.Kodein
import org.kodein.di.generic.instance import org.kodein.di.generic.instance
import org.mindrot.jbcrypt.BCrypt import org.mindrot.jbcrypt.BCrypt
import java.time.LocalDateTime
class UserController(kodein: Kodein) : KodeinController(kodein) { class UserController(kodein: Kodein) : KodeinController(kodein) {
private val simpleJwt by instance<SimpleJWT>() private val simpleJwt by instance<SimpleJWT>()
private val db by instance<Database>() private val userService by instance<UserService>()
override fun Routing.registerRoutes() { override fun Routing.registerRoutes() {
post<Routes.Login> { post<Routes.Login> {
data class Response(val token: String) data class Response(val token: String)
val credential = call.receive<UsernamePasswordCredential>() val credential = call.receive<UsernamePasswordCredential>()
val (email, password) = db.from(Users) val (email, password) = userService.getEmailAndPasswordFromUsername(credential.username)
.select(Users.email, Users.password) ?: return@post call.respondStatus(HttpStatusCode.Unauthorized)
.where { Users.username eq credential.username }
.map { row -> row[Users.email]!! to row[Users.password]!! }
.firstOrNull()
?: return@post call.respond(HttpStatusCode.Unauthorized, ApiError.InvalidCredentialError)
if (!BCrypt.checkpw(credential.password, password)) { if (!BCrypt.checkpw(credential.password, password)) {
return@post call.respond(HttpStatusCode.Unauthorized, ApiError.InvalidCredentialError) return@post call.respondStatus(HttpStatusCode.Unauthorized)
} }
return@post call.respond(Response(simpleJwt.sign(email))) return@post call.respond(Response(simpleJwt.sign(email)))
@ -51,29 +39,18 @@ class UserController(kodein: Kodein) : KodeinController(kodein) {
post<Routes.Register> { post<Routes.Register> {
data class Response(val message: String) data class Response(val message: String)
val user = call.receive<RegisterInfo>() val user = call.receive<UserRegistrationDto>()
val exists = db.from(Users) if (userService.userExists(user.username, user.email))
.select() return@post call.respond(HttpStatusCode.Conflict)
.where { (Users.username eq user.username) or (Users.email eq user.email) }
.any()
if (exists) {
return@post call.respond(HttpStatusCode.Conflict, ApiError.ExistingUserError)
}
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt()) val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
val newUser = User { userService.createUser(
this.username = user.username UserRegistrationDto(user.username, user.email, hashedPassword)
this.email = user.email )
this.password = hashedPassword
this.createdAt = LocalDateTime.now()
}
db.sequenceOf(Users).add(newUser) return@post call.respondStatus(HttpStatusCode.Created)
return@post call.respond(HttpStatusCode.Created, Response("User created successfully"))
} }
} }
@ -85,6 +62,4 @@ class UserController(kodein: Kodein) : KodeinController(kodein) {
class Register class Register
} }
} }
data class RegisterInfo(val username: String, val email: String, val password: String)

View File

@ -1,7 +0,0 @@
package be.vandewalleh.errors
sealed class ApiError(val message: String){
object InvalidCredentialError : ApiError("Invalid credentials")
object ExistingUserError : ApiError("User already exists")
object DeletedUserError : ApiError("User has been deleted")
}

View File

@ -1,11 +1,15 @@
package be.vandewalleh.services package be.vandewalleh.services
import be.vandewalleh.entities.User
import be.vandewalleh.tables.Users import be.vandewalleh.tables.Users
import me.liuwj.ktorm.database.Database import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.dsl.* import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.add
import me.liuwj.ktorm.entity.sequenceOf
import org.kodein.di.Kodein import org.kodein.di.Kodein
import org.kodein.di.KodeinAware import org.kodein.di.KodeinAware
import org.kodein.di.generic.instance import org.kodein.di.generic.instance
import java.time.LocalDateTime
/** /**
* service to handle database queries for users. * service to handle database queries for users.
@ -25,4 +29,43 @@ class UserService(override val kodein: Kodein) : KodeinAware {
.firstOrNull() .firstOrNull()
} }
} /**
* returns a user email and password from it's email if found or null
*/
fun getEmailAndPasswordFromUsername(username: String): Pair<String, String>? {
return db.from(Users)
.select(Users.email, Users.password)
.where { Users.username eq username }
.limit(0, 1)
.map { row -> row[Users.email]!! to row[Users.password]!! }
.firstOrNull()
}
fun userExists(username: String, email: String): Boolean {
return db.from(Users)
.select(Users.id)
.where { (Users.username eq username) or (Users.email eq email) }
.limit(0, 1)
.firstOrNull() != null
}
/**
* create a new user
* password should already be hashed
*/
fun createUser(user: UserRegistrationDto) {
db.useTransaction {
val newUser = User {
this.username = user.username
this.email = user.email
this.password = user.password
this.createdAt = LocalDateTime.now()
}
db.sequenceOf(Users).add(newUser)
}
}
}
data class UserRegistrationDto(val username: String, val email: String, val password: String)