From 95351233b9c9ec96892d8f7766160aeff96db4e6 Mon Sep 17 00:00:00 2001 From: luevard <99143550+saucepommefrite@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:34:26 +0100 Subject: [PATCH] :sparkles: Local Database link --- Sources/src/main/kotlin/allin/dto/UserDTO.kt | 13 +-- .../main/kotlin/allin/entities/UserEntity.kt | 63 +++++++++++- .../main/kotlin/allin/routing/UserRouter.kt | 97 +++++++++++-------- .../main/kotlin/allin/utils/CryptManager.kt | 16 ++- .../main/kotlin/allin/utils/TokenManager.kt | 20 ++++ 5 files changed, 150 insertions(+), 59 deletions(-) diff --git a/Sources/src/main/kotlin/allin/dto/UserDTO.kt b/Sources/src/main/kotlin/allin/dto/UserDTO.kt index d59f39c..a440450 100644 --- a/Sources/src/main/kotlin/allin/dto/UserDTO.kt +++ b/Sources/src/main/kotlin/allin/dto/UserDTO.kt @@ -1,15 +1,4 @@ package allin.dto - -import allin.model.User import kotlinx.serialization.Serializable - @Serializable -data class UserDTO(val username: String,val email: String, val nbCoins: Int) -@Serializable -data class UserDTOWithToken(val username: String,val email: String, val nbCoins: Int, val token:String?) -fun convertUserToUserDTO(user: User): UserDTO { - return UserDTO(user.username, user.email, user.nbCoins) -} -fun convertUserToUserDTOToken(user: User): UserDTOWithToken { - return UserDTOWithToken(user.username, user.email, user.nbCoins,user.token) -} +data class UserDTO(val username: String, val email: String, val nbCoins: Double, var token:String?) diff --git a/Sources/src/main/kotlin/allin/entities/UserEntity.kt b/Sources/src/main/kotlin/allin/entities/UserEntity.kt index 0500cf5..e1ba032 100644 --- a/Sources/src/main/kotlin/allin/entities/UserEntity.kt +++ b/Sources/src/main/kotlin/allin/entities/UserEntity.kt @@ -1,12 +1,71 @@ package allin.entities +import allin.dto.UserDTO +import allin.model.User +import allin.routing.database +import org.ktorm.dsl.* +import org.ktorm.entity.* import org.ktorm.schema.Table import org.ktorm.schema.double import org.ktorm.schema.int import org.ktorm.schema.varchar -object UserEntity : Table("utilisateur") { + +interface UserEntity : Entity { + val username: String + var email: String + var password: String + var nbCoins: Double +} +object UsersEntity : Table("utilisateur") { val id = int("id").primaryKey() val username = varchar("username") val password = varchar("password") val nbCoins = double("nbCoins") -} \ No newline at end of file + val email = varchar("email") + + fun getUserToUserDTO(): MutableList { + return database.from(UsersEntity).select().map { + row -> UserDTO( + row[username].toString(), + row[email].toString(), + row[nbCoins]?:0.0, + null + ) + }.toMutableList() + } + + fun getUserByUsernameAndPassword(login: String): Pair { + return database.from(UsersEntity) + .select() + .where { (username eq login) /*and (password eq passwordParam)*/ } + .map { row -> + Pair( + UserDTO( + row[username].toString(), + row[email].toString(), + row[nbCoins] ?: 0.0, + null + ), + row[password].toString() + ) + } + .firstOrNull() ?: Pair(null, null) + } + + fun addUserEntity(user : User){ + database.insert(UsersEntity){ + set(it.nbCoins,user.nbCoins) + set(it.username,user.username) + set(it.password,user.password) + set(it.email,user.email) + } + } + fun deleteUserByUsername(username: String): Boolean { + val deletedCount = database.delete(UsersEntity) { + it.username eq username + } + return deletedCount > 0 + } +} + + diff --git a/Sources/src/main/kotlin/allin/routing/UserRouter.kt b/Sources/src/main/kotlin/allin/routing/UserRouter.kt index 18489be..e0e52b1 100644 --- a/Sources/src/main/kotlin/allin/routing/UserRouter.kt +++ b/Sources/src/main/kotlin/allin/routing/UserRouter.kt @@ -1,81 +1,98 @@ package allin.routing -import allin.dto.* +import allin.dto.convertUserToUserDTO +import allin.dto.convertUserToUserDTOToken +import allin.ext.hasToken +import allin.ext.verifyUserFromToken +import allin.model.ApiMessage import allin.model.CheckUser import allin.model.User +import allin.model.UserRequest import allin.utils.AppConfig import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.auth.* -import io.ktor.server.auth.jwt.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* +import java.util.* +import org.ktorm.database.Database val users = mutableListOf() +val RegexCheckerUser = AppConfig.regexChecker +val CryptManagerUser = AppConfig.cryptManager +val tokenManagerUser = AppConfig.tokenManager +const val DEFAULT_COINS = 500 + val RegexCheckerUser= AppConfig.regexChecker val CryptManagerUser= AppConfig.cryptManager val tokenManagerUser=AppConfig.tokenManager - +val database = Database.connect("jdbc:postgresql://localhost:5432/Allin", user = "postgres", password = "lulu") fun Application.UserRouter() { routing { - route("/users/register"){ + route("/users/register") { post { - val TempUser = call.receive() - if (RegexCheckerUser.isEmailInvalid(TempUser.email)){ - call.respond(HttpStatusCode.Forbidden,"Input a valid mail !") + val tempUser = call.receive() + if (RegexCheckerUser.isEmailInvalid(tempUser.email)) { + call.respond(HttpStatusCode.Forbidden, ApiMessage.InvalidMail) } - val user = users.find { it.username == TempUser.username || it.email == TempUser.email } - if(user == null) { - CryptManagerUser.passwordCrypt(TempUser) - TempUser.token=tokenManagerUser.generateOrReplaceJWTToken(TempUser) - users.add(TempUser) - call.respond(HttpStatusCode.Created, TempUser) + users.find { it.username == tempUser.username || it.email == tempUser.email }?.let { user -> + call.respond(HttpStatusCode.Conflict, ApiMessage.UserAlreadyExist) + } ?: run { + val user = User( + id = UUID.randomUUID().toString(), + username = tempUser.username, + email = tempUser.email, + password = tempUser.password, + nbCoins = DEFAULT_COINS, + token = null + ) + CryptManagerUser.passwordCrypt(user) + user.token = tokenManagerUser.generateOrReplaceJWTToken(user) + users.add(user) + call.respond(HttpStatusCode.Created, user) } - call.respond(HttpStatusCode.Conflict,"Mail or/and username already exist") } } route("/users/login") { post { val checkUser = call.receive() - val user = users.find { it.username == checkUser.login || it.email == checkUser.login } - if (user != null && CryptManagerUser.passwordDecrypt(user,checkUser.password)) { - user.token=tokenManagerUser.generateOrReplaceJWTToken(user) - call.respond(HttpStatusCode.OK, convertUserToUserDTOToken(user)) - } else { - call.respond(HttpStatusCode.NotFound,"Login and/or password incorrect.") - } + users.find { it.username == checkUser.login || it.email == checkUser.login }?.let { user -> + if (CryptManagerUser.passwordDecrypt(user, checkUser.password)) { + user.token = tokenManagerUser.generateOrReplaceJWTToken(user) + call.respond(HttpStatusCode.OK, convertUserToUserDTOToken(user)) + } else { + call.respond(HttpStatusCode.NotFound, ApiMessage.IncorrectLoginPassword) + } + } ?: call.respond(HttpStatusCode.NotFound, ApiMessage.IncorrectLoginPassword) } } - route("/users/delete") { - post { - val checkUser = call.receive() - val user = users.find { it.username == checkUser.login || it.email == checkUser.login } - if (user != null && user.password == checkUser.password) { - users.remove(user) - call.respond(HttpStatusCode.Accepted,convertUserToUserDTO(user)) - } else { - call.respond(HttpStatusCode.NotFound,"Login and/or password incorrect.") + authenticate { + post("/users/delete") { + hasToken { principal -> + verifyUserFromToken(principal) { user -> + val checkUser = call.receive() + if (user.username == checkUser.login && user.password == checkUser.password) { + users.remove(user) + call.respond(HttpStatusCode.Accepted, convertUserToUserDTO(user)) + } else { + call.respond(HttpStatusCode.NotFound, ApiMessage.IncorrectLoginPassword) + } + } } } - } - authenticate { get("/users/token") { - val principal = call.principal() - val username = principal!!.payload.getClaim("username").asString() - val user = users.find { it.username == username } - if (user != null) { - call.respond(HttpStatusCode.OK,convertUserToUserDTO(user)) - } else { - call.respond(HttpStatusCode.NotFound, "User not found with the valid token !") + hasToken { principal -> + verifyUserFromToken(principal) { user -> + call.respond(HttpStatusCode.OK, convertUserToUserDTO(user)) + } } } } - } } diff --git a/Sources/src/main/kotlin/allin/utils/CryptManager.kt b/Sources/src/main/kotlin/allin/utils/CryptManager.kt index 216e733..df9f8be 100644 --- a/Sources/src/main/kotlin/allin/utils/CryptManager.kt +++ b/Sources/src/main/kotlin/allin/utils/CryptManager.kt @@ -4,12 +4,18 @@ import allin.model.User import org.mindrot.jbcrypt.BCrypt class CryptManager { - val salt=BCrypt.gensalt() + //val salt=BCrypt.gensalt() + fun passwordCrypt(password : String): String { + return BCrypt.hashpw(password,"\$2a\$10\$42wsdBeoLKaF6SM9oADONe") + } fun passwordCrypt(user: User){ - user.password=BCrypt.hashpw(user.password,salt) - + user.password=BCrypt.hashpw(user.password,"\$2a\$10\$42wsdBeoLKaF6SM9oADONe") } - fun passwordDecrypt(user: User, password: String): Boolean{ - return BCrypt.hashpw(password,salt)==user.password + fun passwordDecrypt(password: String, passwordClear: String): Boolean{ + return BCrypt.hashpw(passwordClear,"\$2a\$10\$42wsdBeoLKaF6SM9oADONe")==password + } + + fun CheckPassword(hashed: String, clear: String): Boolean{ + return BCrypt.checkpw(hashed,clear) } } \ No newline at end of file diff --git a/Sources/src/main/kotlin/allin/utils/TokenManager.kt b/Sources/src/main/kotlin/allin/utils/TokenManager.kt index d35ede5..84da76c 100644 --- a/Sources/src/main/kotlin/allin/utils/TokenManager.kt +++ b/Sources/src/main/kotlin/allin/utils/TokenManager.kt @@ -41,6 +41,26 @@ class TokenManager private constructor(val config: HoconApplicationConfig) { } } + fun generateOrReplaceJWTToken(user: UserDTO): String { + val userToken = getUserToken(user) + if (userToken != null && !isTokenExpired(userToken)) { + return userToken + } else { + return generateJWTToken(user) + } + } + + fun generateJWTToken(user : UserDTO): String { + val expirationDate = System.currentTimeMillis() + 604800000 // une semaine en miliseconde + val token = JWT.create() + .withAudience(audience) + .withIssuer(issuer) + .withClaim("username", user.username) + .withExpiresAt(Date(expirationDate)) + .sign(Algorithm.HMAC256(secret)) + return token + } + fun isTokenExpired(token: String): Boolean { val expirationTime = JWT.decode(token).expiresAt.time return System.currentTimeMillis() > expirationTime