226 lines
7.1 KiB
Kotlin
226 lines
7.1 KiB
Kotlin
package com.andr3h3nriqu3s.applications
|
|
|
|
import java.util.UUID
|
|
import kotlin.io.encoding.Base64
|
|
import kotlin.random.Random
|
|
import org.springframework.http.HttpStatus
|
|
import org.springframework.http.MediaType
|
|
import org.springframework.jdbc.core.JdbcTemplate
|
|
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder
|
|
import org.springframework.stereotype.Service
|
|
import org.springframework.web.bind.annotation.ControllerAdvice
|
|
import org.springframework.web.bind.annotation.ExceptionHandler
|
|
import org.springframework.web.bind.annotation.GetMapping
|
|
import org.springframework.web.bind.annotation.PostMapping
|
|
import org.springframework.web.bind.annotation.RequestBody
|
|
import org.springframework.web.bind.annotation.RequestHeader
|
|
import org.springframework.web.bind.annotation.RequestMapping
|
|
import org.springframework.web.bind.annotation.ResponseStatus
|
|
import org.springframework.web.bind.annotation.RestController
|
|
|
|
enum class UserLevel(val level: Int) {
|
|
NORMAL(1),
|
|
ADMIN(255)
|
|
}
|
|
|
|
data class UserDb(
|
|
var id: String,
|
|
var username: String,
|
|
var email: String,
|
|
var password: String,
|
|
var level: Int
|
|
) {
|
|
fun toSafe(): User {
|
|
return User(this.id, this.username, this.email, this.level)
|
|
}
|
|
|
|
fun checkLevel(level: Int): Boolean {
|
|
return (level and this.level) == level
|
|
}
|
|
|
|
fun checkLevelThrow(level: Int) {
|
|
if (!this.checkLevel(level)) {
|
|
throw NotAuth()
|
|
}
|
|
}
|
|
}
|
|
|
|
data class UserCreateRequest(val email: String, val password: String, val username: String)
|
|
|
|
data class LoginUserRequest(val email: String, val password: String)
|
|
|
|
data class LoggedInUser(val token: String, val user: User)
|
|
|
|
data class User(val id: String, var username: String, var email: String, var level: Int)
|
|
|
|
@RestController
|
|
@ControllerAdvice
|
|
@RequestMapping("/api/user")
|
|
@kotlin.io.encoding.ExperimentalEncodingApi
|
|
class UserController(
|
|
val userService: UserService,
|
|
val sessionService: SessionService,
|
|
val sessionServiceCreator: SessionServiceCreator,
|
|
) {
|
|
|
|
@PostMapping(path = ["/login"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
|
public fun login(@RequestBody user: LoginUserRequest): LoggedInUser {
|
|
val userdb = userService.login(user.email, user.password)
|
|
val session = sessionServiceCreator.createSession(userdb)
|
|
|
|
return LoggedInUser(session.token, userdb.toSafe())
|
|
}
|
|
|
|
@GetMapping(path = ["/list"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
|
public fun list(@RequestHeader("token") token: String): List<User> {
|
|
sessionService.verifyTokenThrow(token).checkLevelThrow(UserLevel.ADMIN.ordinal)
|
|
return userService.findUsers().map { elm -> elm.toSafe() }
|
|
}
|
|
|
|
@PostMapping(path = ["/register"], produces = [MediaType.APPLICATION_JSON_VALUE])
|
|
public fun register(@RequestBody user: UserCreateRequest): LoggedInUser {
|
|
var new_user = userService.createUser(user)
|
|
val session = sessionServiceCreator.createSession(new_user)
|
|
|
|
return LoggedInUser(session.token, new_user.toSafe())
|
|
}
|
|
}
|
|
|
|
@Service
|
|
class UserService(val db: JdbcTemplate) {
|
|
|
|
fun login(email: String, passwd: String): UserDb {
|
|
var user = this.findUserByEmail(email)
|
|
if (user == null) {
|
|
throw UserNotFound()
|
|
}
|
|
|
|
var arg2SpringSecurity = Argon2PasswordEncoder(16, 32, 1, 60000, 10)
|
|
|
|
// TODO make this safe
|
|
if (!arg2SpringSecurity.matches(passwd, user.password)) {
|
|
throw UserNotFound()
|
|
}
|
|
return user
|
|
}
|
|
|
|
fun findUserByEmail(email: String): UserDb? {
|
|
var users =
|
|
db
|
|
.query("select * from users where email=?", arrayOf(email)) { response, _ ->
|
|
UserDb(
|
|
response.getString("id"),
|
|
response.getString("username"),
|
|
response.getString("email"),
|
|
response.getString("passwd"),
|
|
response.getInt("level")
|
|
)
|
|
}
|
|
.toList()
|
|
|
|
if (users.size == 0) {
|
|
return null
|
|
}
|
|
|
|
return users[0]
|
|
}
|
|
|
|
fun createUser(user: UserCreateRequest): UserDb {
|
|
var user_check = findUserByEmail(user.email)
|
|
if (user_check != null) {
|
|
throw Exception("User already exists")
|
|
}
|
|
|
|
val id = UUID.randomUUID().toString()
|
|
|
|
var arg2SpringSecurity = Argon2PasswordEncoder(16, 32, 1, 60000, 10)
|
|
val hashPassword = arg2SpringSecurity.encode(user.password)
|
|
|
|
var new_user = UserDb(id, user.username, user.email, hashPassword, UserLevel.NORMAL.ordinal)
|
|
|
|
db.update(
|
|
"insert into users (id, username, email, passwd, level) values (?, ?, ?, ?, ?)",
|
|
new_user.id,
|
|
user.username,
|
|
user.email,
|
|
new_user.password,
|
|
new_user.level,
|
|
)
|
|
|
|
return new_user
|
|
}
|
|
|
|
fun findUsers(): List<UserDb> =
|
|
db.query("select * from users") { response, _ ->
|
|
UserDb(
|
|
response.getString("id"),
|
|
response.getString("username"),
|
|
response.getString("email"),
|
|
response.getString("passwd"),
|
|
response.getInt("level"),
|
|
)
|
|
}
|
|
}
|
|
|
|
data class Session(val token: String, val user_id: String)
|
|
|
|
@Service
|
|
class SessionService(val db: JdbcTemplate) {
|
|
|
|
fun verifyToken(token: String): UserDb? {
|
|
var users =
|
|
db
|
|
.query(
|
|
"select * from tokens as t inner join users as u on id = user_id where token = ?",
|
|
arrayOf(token)
|
|
) { response, _ ->
|
|
UserDb(
|
|
response.getString("id"),
|
|
response.getString("username"),
|
|
response.getString("email"),
|
|
response.getString("passwd"),
|
|
response.getInt("level"),
|
|
)
|
|
}
|
|
.toList()
|
|
|
|
if (users.size == 0) {
|
|
return null
|
|
}
|
|
|
|
return users[0]
|
|
}
|
|
|
|
fun verifyTokenThrow(token: String): UserDb {
|
|
val new_user = this.verifyToken(token)
|
|
if (new_user == null) {
|
|
throw NoToken()
|
|
}
|
|
return new_user
|
|
}
|
|
}
|
|
|
|
@Service
|
|
@kotlin.io.encoding.ExperimentalEncodingApi
|
|
class SessionServiceCreator(val db: JdbcTemplate) {
|
|
|
|
fun createSession(user: UserDb): Session {
|
|
val session = Session(this.generateToken(), user.id)
|
|
|
|
db.update(
|
|
"insert into tokens (token, user_id) values (?,?);",
|
|
session.token,
|
|
session.user_id
|
|
)
|
|
|
|
return session
|
|
}
|
|
|
|
fun generateToken(): String {
|
|
var b = ByteArray(60)
|
|
Random.nextBytes(b)
|
|
|
|
return Base64.encode(b)
|
|
}
|
|
}
|