From 73841170cc120f17e7e1bbdf029e5cd6fee3d629 Mon Sep 17 00:00:00 2001
From: luevard <99143550+saucepommefrite@users.noreply.github.com>
Date: Thu, 11 Jan 2024 09:55:41 +0100
Subject: [PATCH 1/2] :sparkles: Add database for local usage
---
Sources/pom.xml | 5 +++++
Sources/src/main/kotlin/allin/Application.kt | 2 --
Sources/src/main/kotlin/allin/entities/UserEntity.kt | 5 +++--
Sources/src/main/kotlin/allin/model/User.kt | 4 ++--
4 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/Sources/pom.xml b/Sources/pom.xml
index 0d8b30f..c569b14 100644
--- a/Sources/pom.xml
+++ b/Sources/pom.xml
@@ -32,6 +32,11 @@
ktor-server-core-jvm
${ktor_version}
+
+ org.postgresql
+ postgresql
+ 42.7.1
+
io.ktor
ktor-server-netty-jvm
diff --git a/Sources/src/main/kotlin/allin/Application.kt b/Sources/src/main/kotlin/allin/Application.kt
index 93119eb..5f8fb99 100644
--- a/Sources/src/main/kotlin/allin/Application.kt
+++ b/Sources/src/main/kotlin/allin/Application.kt
@@ -1,6 +1,4 @@
package allin
-
-import allin.model.User
import allin.routing.BasicRouting
import allin.routing.BetRouter
import allin.routing.UserRouter
diff --git a/Sources/src/main/kotlin/allin/entities/UserEntity.kt b/Sources/src/main/kotlin/allin/entities/UserEntity.kt
index d8f0996..0500cf5 100644
--- a/Sources/src/main/kotlin/allin/entities/UserEntity.kt
+++ b/Sources/src/main/kotlin/allin/entities/UserEntity.kt
@@ -1,11 +1,12 @@
package allin.entities
import org.ktorm.schema.Table
+import org.ktorm.schema.double
import org.ktorm.schema.int
import org.ktorm.schema.varchar
-
-object UserEntity : Table("user") {
+object UserEntity : 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
diff --git a/Sources/src/main/kotlin/allin/model/User.kt b/Sources/src/main/kotlin/allin/model/User.kt
index d01dc50..3159bc9 100644
--- a/Sources/src/main/kotlin/allin/model/User.kt
+++ b/Sources/src/main/kotlin/allin/model/User.kt
@@ -1,10 +1,10 @@
package allin.model
import allin.dto.UserDTO
+import allin.routing.users
import kotlinx.serialization.Serializable
@Serializable
data class User(val username: String, val email: String, var password: String, var nbCoins: Int = 1000, var token: String? = null)
-
@Serializable
-data class CheckUser(val login: String,val password: String)
\ No newline at end of file
+data class CheckUser(val login: String,val password: String)
From ff4e36beea0c00af19cb9ab309fa7905a80cf409 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 2/2] :sparkles: Local Database link
---
Sources/src/main/kotlin/allin/dto/UserDTO.kt | 13 +---
.../main/kotlin/allin/entities/UserEntity.kt | 63 ++++++++++++++++++-
Sources/src/main/kotlin/allin/model/Bet.kt | 15 +----
.../src/main/kotlin/allin/model/BetAction.kt | 4 +-
Sources/src/main/kotlin/allin/model/User.kt | 4 +-
.../main/kotlin/allin/routing/BetRouter.kt | 15 +++--
.../main/kotlin/allin/routing/UserRouter.kt | 48 +++++++-------
.../kotlin/allin/serializer/DateSerializer.kt | 26 ++++----
.../main/kotlin/allin/utils/CryptManager.kt | 16 +++--
.../main/kotlin/allin/utils/TokenManager.kt | 26 +++++++-
10 files changed, 151 insertions(+), 79 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/model/Bet.kt b/Sources/src/main/kotlin/allin/model/Bet.kt
index 5d4bc13..c99b923 100644
--- a/Sources/src/main/kotlin/allin/model/Bet.kt
+++ b/Sources/src/main/kotlin/allin/model/Bet.kt
@@ -1,19 +1,10 @@
package allin.model
-import allin.dto.UserDTOWithToken
-import allin.serializer.DateSerializer
+import allin.serializer.ZonedDateTimeSerializer
import kotlinx.serialization.Serializable
import java.util.*
@Serializable
-data class Bet(val id: Int, val theme: String, val sentenceBet: String, @Serializable(DateSerializer::class) val endRegistration: Date, @Serializable(DateSerializer::class) var endBet : Date, var isPrivate : Boolean, var response : MutableList, val createdBy : String)
-
-@Serializable
-data class UpdatedBetData(val id: Int,@Serializable(DateSerializer::class) val endBet: Date, val isPrivate: Boolean, val response: MutableList)
-
+data class Bet(val id: Int, val theme: String, val sentenceBet: String, @Serializable(ZonedDateTimeSerializer::class) val endRegistration: Date, @Serializable(ZonedDateTimeSerializer::class) var endBet : Date, var isPrivate : Boolean, var response : MutableList, var createdBy : String)
@Serializable
-data class BetWithoutId(val theme: String, val sentenceBet: String, @Serializable(DateSerializer::class) val endRegistration: Date, @Serializable(DateSerializer::class) var endBet : Date, var isPrivate : Boolean, var response : MutableList, val createdBy : String)
-
-fun convertBetWithoutIdToBet(betWithoutId: BetWithoutId,id : Int, username : String): Bet {
- return Bet(id,betWithoutId.theme,betWithoutId.sentenceBet,betWithoutId.endRegistration, betWithoutId.endBet, betWithoutId.isPrivate, betWithoutId.response, username)
-}
+data class UpdatedBetData(val id: Int,@Serializable(ZonedDateTimeSerializer::class) val endBet: Date, val isPrivate: Boolean, val response: MutableList)
\ No newline at end of file
diff --git a/Sources/src/main/kotlin/allin/model/BetAction.kt b/Sources/src/main/kotlin/allin/model/BetAction.kt
index f830e2c..2f2298b 100644
--- a/Sources/src/main/kotlin/allin/model/BetAction.kt
+++ b/Sources/src/main/kotlin/allin/model/BetAction.kt
@@ -1,5 +1,5 @@
package allin.model
-import allin.dto.UserDTOWithToken
+import allin.dto.UserDTO
data class BetAction(val id:Int, val coins: Int, val user: String, val bet: Int)
-data class BetActionCompleted(val id:Int, val coins: Int, val user: UserDTOWithToken, val bet: Bet)
+data class BetActionCompleted(val id:Int, val coins: Int, val user: UserDTO, val bet: Bet)
diff --git a/Sources/src/main/kotlin/allin/model/User.kt b/Sources/src/main/kotlin/allin/model/User.kt
index 3159bc9..4f3f1f0 100644
--- a/Sources/src/main/kotlin/allin/model/User.kt
+++ b/Sources/src/main/kotlin/allin/model/User.kt
@@ -1,10 +1,8 @@
package allin.model
-import allin.dto.UserDTO
-import allin.routing.users
import kotlinx.serialization.Serializable
@Serializable
-data class User(val username: String, val email: String, var password: String, var nbCoins: Int = 1000, var token: String? = null)
+data class User(val username: String, val email: String, var password: String, var nbCoins: Double = 1000.0, var token: String? = null)
@Serializable
data class CheckUser(val login: String,val password: String)
diff --git a/Sources/src/main/kotlin/allin/routing/BetRouter.kt b/Sources/src/main/kotlin/allin/routing/BetRouter.kt
index 997cee5..51e398d 100644
--- a/Sources/src/main/kotlin/allin/routing/BetRouter.kt
+++ b/Sources/src/main/kotlin/allin/routing/BetRouter.kt
@@ -5,7 +5,10 @@ import io.ktor.server.routing.*
import allin.model.*
import allin.utils.AppConfig
import io.ktor.http.*
+import io.ktor.server.auth.*
+import io.ktor.server.auth.jwt.*
import io.ktor.server.response.*
+import java.time.ZonedDateTime
val bets = mutableListOf()
val tokenManagerBet= AppConfig.tokenManager
@@ -16,20 +19,22 @@ fun CreateId() : Int{
fun Application.BetRouter(){
routing{
+ authenticate {
route("/bets/add"){
post{
- val bet = call.receive()
+ val bet = call.receive()
+ val token= call.principal()
val id = CreateId()
- val username = tokenManagerBet.getUsernameFromToken(bet.createdBy)
+ bet.createdBy = tokenManagerBet.getUsernameFromToken(token.toString())
val findbet = bets.find { it.id == id }
if(findbet==null){
- val betWithId = convertBetWithoutIdToBet(bet,id,username)
- bets.add(betWithId)
- call.respond(HttpStatusCode.Created, betWithId)
+ bets.add(bet)
+ call.respond(HttpStatusCode.Created, bet)
}
call.respond(HttpStatusCode.Conflict,"Bet already exist")
}
}
+ }
route("/bets/gets"){
get{
// if(bets.size>0)
diff --git a/Sources/src/main/kotlin/allin/routing/UserRouter.kt b/Sources/src/main/kotlin/allin/routing/UserRouter.kt
index 18489be..bfde82d 100644
--- a/Sources/src/main/kotlin/allin/routing/UserRouter.kt
+++ b/Sources/src/main/kotlin/allin/routing/UserRouter.kt
@@ -1,8 +1,10 @@
package allin.routing
-import allin.dto.*
-import allin.model.CheckUser
-import allin.model.User
+import allin.entities.UsersEntity.addUserEntity
+import allin.entities.UsersEntity.deleteUserByUsername
+import allin.entities.UsersEntity.getUserByUsernameAndPassword
+import allin.entities.UsersEntity.getUserToUserDTO
+import allin.model.*
import allin.utils.AppConfig
import io.ktor.http.*
import io.ktor.server.application.*
@@ -11,15 +13,13 @@ import io.ktor.server.auth.jwt.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
+import org.ktorm.database.Database
-val users = mutableListOf()
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"){
post {
@@ -27,12 +27,13 @@ fun Application.UserRouter() {
if (RegexCheckerUser.isEmailInvalid(TempUser.email)){
call.respond(HttpStatusCode.Forbidden,"Input a valid mail !")
}
+ val users=getUserToUserDTO()
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)
+ addUserEntity(TempUser)
+ call.respond(HttpStatusCode.Created,TempUser)
}
call.respond(HttpStatusCode.Conflict,"Mail or/and username already exist")
}
@@ -41,25 +42,27 @@ fun Application.UserRouter() {
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))
+ val user =getUserByUsernameAndPassword(checkUser.login)
+ if (CryptManagerUser.passwordDecrypt(user.second.toString(),checkUser.password)) {
+ val userDtoWithToken=user.first
+ userDtoWithToken?.token=tokenManagerUser.generateOrReplaceJWTToken(userDtoWithToken!!)
+ call.respond(HttpStatusCode.OK, userDtoWithToken)
} else {
call.respond(HttpStatusCode.NotFound,"Login and/or password incorrect.")
}
}
}
-
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))
+ val user =getUserByUsernameAndPassword(checkUser.login)
+ if (CryptManagerUser.passwordDecrypt(user.second.toString(),checkUser.password)) {
+ if (!deleteUserByUsername(checkUser.login)) {
+ call.respond(HttpStatusCode.InternalServerError, "This user can't be delete now !")
+ }
+ call.respond(HttpStatusCode.Accepted,user.first!!)
} else {
- call.respond(HttpStatusCode.NotFound,"Login and/or password incorrect.")
+ call.respond(HttpStatusCode.NotFound, "Login and/or password incorrect.")
}
}
}
@@ -68,14 +71,13 @@ fun Application.UserRouter() {
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))
+ val user=getUserByUsernameAndPassword(username)
+ if (user.first != null) {
+ call.respond(HttpStatusCode.OK, user.first!!)
} else {
call.respond(HttpStatusCode.NotFound, "User not found with the valid token !")
}
}
}
-
}
}
diff --git a/Sources/src/main/kotlin/allin/serializer/DateSerializer.kt b/Sources/src/main/kotlin/allin/serializer/DateSerializer.kt
index 6002c28..dc84760 100644
--- a/Sources/src/main/kotlin/allin/serializer/DateSerializer.kt
+++ b/Sources/src/main/kotlin/allin/serializer/DateSerializer.kt
@@ -6,22 +6,22 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
-import java.text.SimpleDateFormat
-import java.util.*
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
-@Serializer(Date::class)
-class DateSerializer : KSerializer {
- override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING)
+@Serializer(ZonedDateTime::class)
+object ZonedDateTimeSerializer : KSerializer {
+ private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME
- val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.FRANCE)
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("ZonedDateTime", PrimitiveKind.STRING)
- override fun deserialize(decoder: Decoder): Date {
- val dateString = decoder.decodeString()
- return formatter.parse(dateString)
+ override fun serialize(encoder: Encoder, value: ZonedDateTime) {
+ encoder.encodeString(formatter.format(value))
}
- override fun serialize(encoder: Encoder, value: Date) {
- val dateString = formatter.format(value)
- encoder.encodeString(dateString)
+ override fun deserialize(decoder: Decoder): ZonedDateTime {
+ val dateString = decoder.decodeString()
+ return ZonedDateTime.parse(dateString, formatter)
}
-}
\ No newline at end of file
+}
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..0d82193 100644
--- a/Sources/src/main/kotlin/allin/utils/TokenManager.kt
+++ b/Sources/src/main/kotlin/allin/utils/TokenManager.kt
@@ -1,5 +1,6 @@
package allin.utils
+import allin.dto.UserDTO
import allin.model.User
import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
@@ -15,7 +16,6 @@ class TokenManager private constructor(val config: HoconApplicationConfig) {
val issuer=config.property("issuer").getString()
fun generateJWTToken(user : User): String {
val expirationDate = System.currentTimeMillis() + 604800000 // une semaine en miliseconde
-
val token = JWT.create()
.withAudience(audience)
.withIssuer(issuer)
@@ -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
@@ -49,7 +69,9 @@ class TokenManager private constructor(val config: HoconApplicationConfig) {
fun getUserToken(user: User): String? {
return user.token
}
-
+ fun getUserToken(user: UserDTO): String? {
+ return user.token
+ }
fun getUsernameFromToken(token: String) : String{
val decodedJWT: DecodedJWT = JWT.decode(token)
return decodedJWT.getClaim("username").asString()