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()