revert cb9edc7492
continuous-integration/drone/push Build is passing Details

revert Add bet participations and refactoring
pull/4/head
Arthur VALIN 1 year ago
parent cb9edc7492
commit aecce83607

4
.gitignore vendored

@ -33,6 +33,4 @@ out/
/.nb-gradle/
### VS Code ###
.vscode/
**/src/target/**
.vscode/

@ -1,19 +1,19 @@
package allin
import allin.model.User
import allin.routing.BasicRouting
import allin.routing.BetRouter
import allin.routing.ParticipationRouter
import allin.routing.UserRouter
import allin.utils.TokenManager
import com.typesafe.config.ConfigFactory
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.config.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import allin.utils.TokenManager
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
@ -27,9 +27,9 @@ private fun Application.extracted() {
authentication {
jwt {
verifier(tokenManager.verifyJWTToken())
realm = config.property("realm").getString()
realm=config.property("realm").getString()
validate { jwtCredential ->
if (jwtCredential.payload.getClaim("username").asString().isNotEmpty())
if(jwtCredential.payload.getClaim("username").asString().isNotEmpty())
JWTPrincipal(jwtCredential.payload)
else null
}
@ -41,5 +41,4 @@ private fun Application.extracted() {
BasicRouting()
UserRouter()
BetRouter()
ParticipationRouter()
}

@ -1,23 +0,0 @@
package allin.ext
import allin.model.ApiMessage
import allin.model.User
import allin.routing.users
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.response.*
import io.ktor.util.pipeline.*
suspend fun PipelineContext<*, ApplicationCall>.hasToken(content: suspend (principal: JWTPrincipal) -> Unit) =
call.principal<JWTPrincipal>()?.let { content(it) } ?: call.respond(HttpStatusCode.Unauthorized)
suspend fun PipelineContext<*, ApplicationCall>.verifyUserFromToken(
principal: JWTPrincipal,
content: suspend (user: User) -> Unit
) {
val username = principal.payload.getClaim("username").asString()
users.find { it.username == username }?.let { content(it) }
?: call.respond(HttpStatusCode.NotFound, ApiMessage.TokenUserNotFound)
}

@ -1,13 +0,0 @@
package allin.model
object ApiMessage {
const val Welcome = "Welcome on AllIn's API !"
const val TokenUserNotFound = "User not found with the valid token !"
const val BetNotFound = "Bet not found."
const val BetAlreadyExist = "Bet already exists."
const val IncorrectLoginPassword = "Login and/or password incorrect."
const val UserAlreadyExist = "Mail and/or username already exists."
const val InvalidMail = "Invalid mail."
const val ParticipationNotFound = "Participation not found."
const val NotEnoughCoins = "Not enough coins."
}

@ -1,37 +1,19 @@
package allin.model
import allin.serializer.ZonedDateTimeSerializer
import allin.dto.UserDTOWithToken
import allin.serializer.DateSerializer
import kotlinx.serialization.Serializable
import java.time.ZonedDateTime
import java.util.*
@Serializable
data class Bet(
val id: String,
val theme: String,
val sentenceBet: String,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean,
var response: MutableList<String>,
val createdBy: String
)
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<String>, val createdBy : String)
@Serializable
data class UpdatedBetData(
val id: String,
@Serializable(ZonedDateTimeSerializer::class) val endBet: ZonedDateTime,
val isPrivate: Boolean,
val response: MutableList<String>
)
data class UpdatedBetData(val id: Int,@Serializable(DateSerializer::class) val endBet: Date, val isPrivate: Boolean, val response: MutableList<String>)
@Serializable
data class BetWithoutId(
val theme: String,
val sentenceBet: String,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean,
var response: MutableList<String>,
val createdBy: String
)
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<String>, 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)
}

@ -0,0 +1,5 @@
package allin.model
import allin.dto.UserDTOWithToken
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)

@ -1,19 +0,0 @@
package allin.model
import kotlinx.serialization.Serializable
@Serializable
data class Participation(
val id: String,
val betId: String,
val username: String,
val answer: String,
val stake: Int
)
@Serializable
data class ParticipationRequest(
val betId: String,
val answer: String,
val stake: Int
)

@ -1,26 +1,10 @@
package allin.model
import allin.dto.UserDTO
import kotlinx.serialization.Serializable
@Serializable
data class User(
val id: String,
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: Int = 1000, var token: String? = null)
@Serializable
data class UserRequest(
val username: String,
val email: String,
var password: String
)
@Serializable
data class CheckUser(
val login: String,
val password: String
)
data class CheckUser(val login: String,val password: String)

@ -1,14 +1,13 @@
package allin.routing
import allin.model.ApiMessage
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.BasicRouting() {
fun Application.BasicRouting(){
routing {
get("/") {
call.respond(ApiMessage.Welcome)
call.respond("Bienvenue sur l'API de AlLin!")
}
}
}

@ -0,0 +1,13 @@
package allin.routing
import allin.model.BetAction
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.routing.*
fun Application.BetActionRouter(){
routing {
route("/BetAction/add"){
}
}
}

@ -1,85 +1,65 @@
package allin.routing
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.ApiMessage
import allin.model.Bet
import allin.model.BetWithoutId
import allin.model.UpdatedBetData
import allin.utils.AppConfig
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.*
import allin.model.*
import allin.utils.AppConfig
import io.ktor.http.*
import io.ktor.server.response.*
val bets = mutableListOf<Bet>()
val tokenManagerBet = AppConfig.tokenManager
val tokenManagerBet= AppConfig.tokenManager
fun Application.BetRouter() {
routing {
route("/bets/add") {
post {
fun CreateId() : Int{
return bets.size
}
fun Application.BetRouter(){
routing{
route("/bets/add"){
post{
val bet = call.receive<BetWithoutId>()
val id = UUID.randomUUID().toString()
val id = CreateId()
val username = tokenManagerBet.getUsernameFromToken(bet.createdBy)
bets.find { it.id == id }?.let {
call.respond(HttpStatusCode.Conflict, ApiMessage.BetAlreadyExist)
} ?: run {
val betWithId = Bet(
id,
bet.theme,
bet.sentenceBet,
bet.endRegistration,
bet.endBet,
bet.isPrivate,
bet.response,
username
)
val findbet = bets.find { it.id == id }
if(findbet==null){
val betWithId = convertBetWithoutIdToBet(bet,id,username)
bets.add(betWithId)
call.respond(HttpStatusCode.Created, betWithId)
}
call.respond(HttpStatusCode.Conflict,"Bet already exist")
}
}
route("/bets/gets") {
get {
// if(bets.size>0)
call.respond(HttpStatusCode.Accepted, bets.toList())
// else call.respond(HttpStatusCode.NoContent)
route("/bets/gets"){
get{
// if(bets.size>0)
call.respond(HttpStatusCode.Accepted, bets.toList())
// else call.respond(HttpStatusCode.NoContent)
}
}
route("/bets/delete") {
post {
val idbet = call.receive<Map<String, String>>()["id"]
bets.find { it.id == idbet }?.let { findbet ->
bets.remove(findbet)
call.respond(HttpStatusCode.Accepted, findbet)
} ?: call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
route("/bets/delete"){
post{
val idbet = call.receive<Map<String, Int>>()["id"]
val findbet = bets.find { it.id == idbet }
if(findbet==null){
call.respond(HttpStatusCode.NotFound, "Bet doesnt find")
}
bets.remove(findbet)
findbet as Bet
call.respond(HttpStatusCode.Accepted, findbet)
}
}
route("bets/update") {
post {
route("bets/update"){
post{
val updatedBetData = call.receive<UpdatedBetData>()
bets.find { it.id == updatedBetData.id }?.let { findbet ->
val findbet = bets.find { it.id == updatedBetData.id }
if (findbet == null) {
call.respond(HttpStatusCode.NotFound, "Bet not found")
} else {
findbet.endBet = updatedBetData.endBet
findbet.isPrivate = updatedBetData.isPrivate
findbet.response = updatedBetData.response
call.respond(HttpStatusCode.Accepted, findbet)
} ?: call.respond(HttpStatusCode.NotFound, ApiMessage.BetNotFound)
}
}
authenticate {
get("/bets/current") {
hasToken { principal ->
verifyUserFromToken(principal) { user ->
val bets = participations
.filter { it.username == user.username }
.mapNotNull { itParticipation -> bets.find { it.id == itParticipation.betId } }
call.respond(HttpStatusCode.OK, bets)
}
}
}
}

@ -1,56 +0,0 @@
package allin.routing
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.ApiMessage
import allin.model.Participation
import allin.model.ParticipationRequest
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.*
val participations = mutableListOf<Participation>()
fun Application.ParticipationRouter() {
routing {
authenticate {
post("/participations/add") {
hasToken { principal ->
val participation = call.receive<ParticipationRequest>()
verifyUserFromToken(principal) { user ->
if (user.nbCoins >= participation.stake) {
participations.add(
Participation(
id = UUID.randomUUID().toString(),
betId = participation.betId,
username = user.username,
answer = participation.answer,
stake = participation.stake
)
)
call.respond(HttpStatusCode.Created)
} else {
call.respond(HttpStatusCode.Forbidden, ApiMessage.NotEnoughCoins)
}
}
}
}
delete("/participations/delete") {
hasToken { principal ->
val participationId = call.receive<String>()
participations.find { it.id == participationId }?.let { participation ->
verifyUserFromToken(principal) { user ->
user.nbCoins += participation.stake
participations.remove(participation)
call.respond(HttpStatusCode.NoContent)
}
} ?: call.respond(HttpStatusCode.NotFound, ApiMessage.ParticipationNotFound)
}
}
}
}
}

@ -1,92 +1,81 @@
package allin.routing
import allin.dto.convertUserToUserDTO
import allin.dto.convertUserToUserDTOToken
import allin.ext.hasToken
import allin.ext.verifyUserFromToken
import allin.model.ApiMessage
import allin.dto.*
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.*
val users = mutableListOf<User>()
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
fun Application.UserRouter() {
routing {
route("/users/register") {
route("/users/register"){
post {
val tempUser = call.receive<UserRequest>()
if (RegexCheckerUser.isEmailInvalid(tempUser.email)) {
call.respond(HttpStatusCode.Forbidden, ApiMessage.InvalidMail)
val TempUser = call.receive<User>()
if (RegexCheckerUser.isEmailInvalid(TempUser.email)){
call.respond(HttpStatusCode.Forbidden,"Input a valid mail !")
}
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)
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)
}
call.respond(HttpStatusCode.Conflict,"Mail or/and username already exist")
}
}
route("/users/login") {
post {
val checkUser = call.receive<CheckUser>()
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)
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.")
}
}
}
authenticate {
post("/users/delete") {
hasToken { principal ->
verifyUserFromToken(principal) { user ->
val checkUser = call.receive<CheckUser>()
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)
}
}
route("/users/delete") {
post {
val checkUser = call.receive<CheckUser>()
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 {
get("/users/token") {
hasToken { principal ->
verifyUserFromToken(principal) { user ->
call.respond(HttpStatusCode.OK, convertUserToUserDTO(user))
}
val principal = call.principal<JWTPrincipal>()
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 !")
}
}
}
}
}

@ -1,25 +1,27 @@
package allin.serializer
import kotlinx.serialization.KSerializer
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.text.SimpleDateFormat
import java.util.*
object ZonedDateTimeSerializer : KSerializer<ZonedDateTime> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("ZonedDateTime", PrimitiveKind.LONG)
@Serializer(Date::class)
class DateSerializer : KSerializer<Date> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: ZonedDateTime) {
encoder.encodeLong(value.toEpochSecond())
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.FRANCE)
override fun deserialize(decoder: Decoder): Date {
val dateString = decoder.decodeString()
return formatter.parse(dateString)
}
override fun deserialize(decoder: Decoder): ZonedDateTime {
val epoch = decoder.decodeLong()
return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZoneId.systemDefault())
override fun serialize(encoder: Encoder, value: Date) {
val dateString = formatter.format(value)
encoder.encodeString(dateString)
}
}

@ -10,18 +10,19 @@ import java.util.*
class TokenManager private constructor(val config: HoconApplicationConfig) {
val audience = config.property("audience").getString()
val secret = config.property("secret").getString()
val issuer = config.property("issuer").getString()
fun generateJWTToken(user: User): String {
val audience=config.property("audience").getString()
val secret=config.property("secret").getString()
val issuer=config.property("issuer").getString()
fun generateJWTToken(user : User): String {
val expirationDate = System.currentTimeMillis() + 604800000 // une semaine en miliseconde
return JWT.create()
val token = JWT.create()
.withAudience(audience)
.withIssuer(issuer)
.withClaim("username", user.username)
.withExpiresAt(Date(expirationDate))
.sign(Algorithm.HMAC256(secret))
return token
}
fun verifyJWTToken(): JWTVerifier {
@ -33,10 +34,10 @@ class TokenManager private constructor(val config: HoconApplicationConfig) {
fun generateOrReplaceJWTToken(user: User): String {
val userToken = getUserToken(user)
return if (userToken != null && !isTokenExpired(userToken)) {
userToken
if (userToken != null && !isTokenExpired(userToken)) {
return userToken
} else {
generateJWTToken(user)
return generateJWTToken(user)
}
}
@ -49,11 +50,10 @@ class TokenManager private constructor(val config: HoconApplicationConfig) {
return user.token
}
fun getUsernameFromToken(token: String): String {
fun getUsernameFromToken(token: String) : String{
val decodedJWT: DecodedJWT = JWT.decode(token)
return decodedJWT.getClaim("username").asString()
}
companion object {
private var instance: TokenManager? = null
fun getInstance(config: HoconApplicationConfig): TokenManager {

Loading…
Cancel
Save