Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ val logback_version: String by project
val kmongo_version: String by project
val koin_version: String by project
val firebase_version: String by project
val prometheus_version: String by project

plugins {
application
Expand All @@ -30,12 +31,15 @@ dependencies {
implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktor_version")
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
implementation("io.ktor:ktor-serialization-gson-jvm:$ktor_version")
implementation("io.ktor:ktor-server-metrics-jvm:$ktor_version")
implementation("io.ktor:ktor-server-call-logging-jvm:$ktor_version")
implementation("io.ktor:ktor-server-cors-jvm:$ktor_version")
implementation("io.ktor:ktor-server-auth-jvm:$ktor_version")
implementation("io.ktor:ktor-server-auth-jwt-jvm:$ktor_version")
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
implementation("io.ktor:ktor-server-rate-limit:$ktor_version")
implementation("io.ktor:ktor-server-metrics-jvm:$ktor_version")
implementation("io.ktor:ktor-server-metrics-micrometer:$ktor_version")
implementation("io.micrometer:micrometer-registry-prometheus:$prometheus_version")
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")

Expand All @@ -61,6 +65,9 @@ dependencies {
// JavaMail
implementation("javax.mail:javax.mail-api:1.6.2")
implementation("com.sun.mail:javax.mail:1.6.2")

// TOTP
implementation("dev.turingcomplete:kotlin-onetimepassword:2.4.0")
}

ktor {
Expand All @@ -71,7 +78,7 @@ ktor {
docker {
jreVersion.set(io.ktor.plugin.features.JreVersion.JRE_17)
localImageName.set(rootProject.name)
imageTag.set("0.0.1-preview")
imageTag.set("1.0-preview")

externalRegistry.set(
io.ktor.plugin.features.DockerImageRegistry.dockerHub(
Expand Down
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
ktor_version=2.2.1
ktor_version=2.2.4
kotlin_version=1.7.21
logback_version=1.4.4
kmongo_version=4.7.2
koin_version=3.2.2
firebase_version=9.1.1
prometheus_version=1.10.3
kotlin.code.style=official
1 change: 1 addition & 0 deletions src/main/kotlin/es/wokis/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ fun Application.module() {
configureMonitoring()
configureHTTP()
configureSecurity()
configureRateLimit()
configureRouting()
}
10 changes: 10 additions & 0 deletions src/main/kotlin/es/wokis/data/bo/recover/RecoverBO.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package es.wokis.data.bo.recover

import java.util.*

data class RecoverBO(
val id: String? = null,
val email: String,
val verificationToken: String,
val timeStamp: Date = Date()
)
7 changes: 7 additions & 0 deletions src/main/kotlin/es/wokis/data/bo/user/TOTPResponseBO.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package es.wokis.data.bo.user

data class TOTPResponseBO(
val encodedSecret: String,
val totpUrl: String,
val words: List<String>
)
6 changes: 6 additions & 0 deletions src/main/kotlin/es/wokis/data/bo/user/UpdateUserBO.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package es.wokis.data.bo.user

data class UpdateUserBO(
val username: String?,
val email: String?,
)
49 changes: 47 additions & 2 deletions src/main/kotlin/es/wokis/data/bo/user/UserBO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,56 @@ data class UserBO(
val image: String = EMPTY_TEXT,
val lang: String = DEFAULT_LANG,
val createdOn: Long = Date().time,
val totpEncodedSecret: ByteArray? = null,
val currentSession: String? = null,
val emailVerified: Boolean = false,
val sessions: List<String> = emptyList(),
val badges: List<BadgeBO> = emptyList(),
val devices: List<String> = emptyList()
) : Principal
val devices: List<String> = emptyList(),
val recoverWords: List<String> = emptyList()
) : Principal {

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as UserBO

if (id != other.id) return false
if (username != other.username) return false
if (email != other.email) return false
if (password != other.password) return false
if (image != other.image) return false
if (lang != other.lang) return false
if (createdOn != other.createdOn) return false
if (totpEncodedSecret != null) {
if (other.totpEncodedSecret == null) return false
if (!totpEncodedSecret.contentEquals(other.totpEncodedSecret)) return false
} else if (other.totpEncodedSecret != null) return false
if (emailVerified != other.emailVerified) return false
if (sessions != other.sessions) return false
if (badges != other.badges) return false
if (devices != other.devices) return false

return true
}

override fun hashCode(): Int {
var result = id?.hashCode() ?: 0
result = 31 * result + username.hashCode()
result = 31 * result + email.hashCode()
result = 31 * result + password.hashCode()
result = 31 * result + image.hashCode()
result = 31 * result + lang.hashCode()
result = 31 * result + createdOn.hashCode()
result = 31 * result + (totpEncodedSecret?.contentHashCode() ?: 0)
result = 31 * result + emailVerified.hashCode()
result = 31 * result + sessions.hashCode()
result = 31 * result + badges.hashCode()
result = 31 * result + devices.hashCode()
return result
}
}

data class BadgeBO(
val id: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package es.wokis.data.bo.verification
import java.util.*

data class VerificationBO(
val id: Long? = null,
val id: String? = null,
val email: String,
val verificationToken: String,
val timeStamp: Date = Date()
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/es/wokis/data/constants/ServerConstants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ package es.wokis.data.constants
object ServerConstants {
const val EMPTY_TEXT = ""
const val DEFAULT_LANG = "en"
const val LANG_ES = "es"
const val MAX_OG_DATE = "01/06/2023"
}
4 changes: 4 additions & 0 deletions src/main/kotlin/es/wokis/data/database/AppDataBase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import com.mongodb.MongoCredential
import es.wokis.data.dbo.invoice.InvoiceDBO
import es.wokis.data.dbo.recover.RecoverDBO
import es.wokis.data.dbo.user.UserDBO
import es.wokis.data.dbo.verification.VerificationDBO
import es.wokis.plugins.config
import org.litote.kmongo.KMongo
import org.litote.kmongo.getCollection
Expand All @@ -23,6 +25,8 @@ class AppDataBase {
val database by lazy { client.getDatabase(databaseName) }
val usersCollection by lazy { database.getCollection<UserDBO>("users") }
val invoicesCollection by lazy { database.getCollection<InvoiceDBO>("invoices") }
val verificationCollection by lazy { database.getCollection<VerificationDBO>("verification") }
val recoverCollection by lazy { database.getCollection<RecoverDBO>("recover") }

companion object {
private const val MONGODB_PREFIX = "mongodb://"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package es.wokis.data.datasource.recover

import com.mongodb.client.MongoCollection
import es.wokis.data.bo.recover.RecoverBO
import es.wokis.data.dbo.recover.RecoverDBO
import es.wokis.data.mapper.recover.toBO
import es.wokis.data.mapper.recover.toDBO
import org.bson.types.ObjectId
import org.litote.kmongo.Id
import org.litote.kmongo.eq
import org.litote.kmongo.findOne
import org.litote.kmongo.id.toId
import org.litote.kmongo.regex

interface RecoverLocalDataSource {
suspend fun getRecoverByToken(token: String): RecoverBO?
suspend fun saveRecoverRequest(recover: RecoverBO): Boolean
suspend fun removeRecover(id: String): Boolean
}

class RecoverLocalDataSourceImpl(private val recoverCollection: MongoCollection<RecoverDBO>) :
RecoverLocalDataSource {
override suspend fun getRecoverByToken(token: String): RecoverBO? =
recoverCollection.findOne(RecoverDBO::recoverToken.regex(token))?.toBO()

override suspend fun saveRecoverRequest(recover: RecoverBO): Boolean {
return try {
recoverCollection.insertOne(recover.toDBO()).wasAcknowledged()

} catch (e: Throwable) {
println(e.stackTraceToString())
false
}
}

override suspend fun removeRecover(id: String): Boolean = try {
val bsonId: Id<RecoverDBO> = ObjectId(id).toId()
recoverCollection.deleteOne(RecoverDBO::id eq bsonId).wasAcknowledged()

} catch (e: Throwable) {
println(e.stackTraceToString())
false
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package es.wokis.data.datasource.verify

import com.mongodb.client.MongoCollection
import es.wokis.data.bo.verification.VerificationBO
import es.wokis.data.dbo.invoice.InvoiceDBO
import es.wokis.data.dbo.user.UserDBO
import es.wokis.data.dbo.verification.VerificationDBO
import es.wokis.data.mapper.user.toBO
import es.wokis.data.mapper.verify.toBO
import es.wokis.data.mapper.verify.toDBO
import org.bson.types.ObjectId
import org.litote.kmongo.Id
import org.litote.kmongo.eq
import org.litote.kmongo.findOne
import org.litote.kmongo.id.toId
import org.litote.kmongo.regex

interface VerifyLocalDataSource {
suspend fun getVerificationByToken(token: String): VerificationBO?
suspend fun addVerification(verification: VerificationBO): Boolean
suspend fun removeVerification(id: String): Boolean
}

class VerifyLocalDataSourceImpl(private val verificationCollection: MongoCollection<VerificationDBO>) :
VerifyLocalDataSource {
override suspend fun getVerificationByToken(token: String): VerificationBO? = verificationCollection.findOne(VerificationDBO::verificationToken.regex(token))?.toBO()

override suspend fun addVerification(verification: VerificationBO): Boolean {
return try {
verificationCollection.insertOne(verification.toDBO()).wasAcknowledged()

} catch (e: Throwable) {
println(e.stackTraceToString())
false
}
}

override suspend fun removeVerification(id: String): Boolean = try {
val bsonId: Id<VerificationDBO> = ObjectId(id).toId()
verificationCollection.deleteOne(VerificationDBO::id eq bsonId).wasAcknowledged()

} catch (e: Throwable) {
println(e.stackTraceToString())
false
}

}
12 changes: 12 additions & 0 deletions src/main/kotlin/es/wokis/data/dbo/recover/RecoverDBO.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package es.wokis.data.dbo.recover

import org.bson.codecs.pojo.annotations.BsonId
import org.litote.kmongo.Id

data class RecoverDBO(
@BsonId
val id: Id<RecoverDBO>? = null,
val email: String,
val recoverToken: String,
val timeStamp: Long,
)
4 changes: 3 additions & 1 deletion src/main/kotlin/es/wokis/data/dbo/user/UserDBO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ data class UserDBO(
val image: String = ServerConstants.EMPTY_TEXT,
val createdOn: Long = Date().time,
val emailVerified: Boolean = false,
val totpEncodedSecret: ByteArray? = null,
val sessions: List<String> = emptyList(),
val badges: List<BadgeDBO> = emptyList(),
val devices: List<String> = emptyList()
val devices: List<String> = emptyList(),
val recoverWords: List<String> = emptyList()
)

data class BadgeDBO(
Expand Down
13 changes: 13 additions & 0 deletions src/main/kotlin/es/wokis/data/dbo/verification/VerificationDBO.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package es.wokis.data.dbo.verification

import es.wokis.data.dbo.user.UserDBO
import org.bson.codecs.pojo.annotations.BsonId
import org.litote.kmongo.Id

data class VerificationDBO(
@BsonId
val id: Id<VerificationDBO>? = null,
val email: String,
val verificationToken: String,
val timeStamp: Long,
)
8 changes: 8 additions & 0 deletions src/main/kotlin/es/wokis/data/dto/recover/RecoverPass.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package es.wokis.data.dto.recover

import com.google.gson.annotations.SerializedName

data class RecoverPassRequestDTO(
@SerializedName("email")
val email: String
)
12 changes: 12 additions & 0 deletions src/main/kotlin/es/wokis/data/dto/totp/TOTPResponseDTO.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package es.wokis.data.dto.totp

import com.google.gson.annotations.SerializedName

data class TOTPResponseDTO(
@SerializedName("encodedSecret")
val encodedSecret: String,
@SerializedName("totpUrl")
val totpUrl: String,
@SerializedName("words")
val words: List<String>
)
2 changes: 2 additions & 0 deletions src/main/kotlin/es/wokis/data/dto/user/UserDTO.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ data class UserDTO(
val lang: String,
@SerializedName("createdOn")
val createdOn: Long,
@SerializedName("totpEnabled")
val totpEnabled: Boolean,
@SerializedName("emailVerified")
val emailVerified: Boolean = false,
@SerializedName("devices")
Expand Down
19 changes: 18 additions & 1 deletion src/main/kotlin/es/wokis/data/dto/user/auth/Auth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package es.wokis.data.dto.user.auth

import com.google.gson.annotations.SerializedName
import es.wokis.data.constants.ServerConstants.DEFAULT_LANG
import es.wokis.services.GOOGLE_AUTHENTICATOR

data class LoginDTO(
@SerializedName("username")
Expand All @@ -28,7 +29,23 @@ data class AuthResponseDTO(
val authToken: String
)

data class GoogleAuthDTO (
data class GoogleAuthDTO(
@SerializedName("authToken")
val authToken: String
)

data class ChangePassRequestDTO(
@SerializedName("oldPass")
val oldPass: String?,
@SerializedName("recoverCode")
val recoverCode: String?,
@SerializedName("newPass")
val newPass: String
)

data class TOTPRequestDTO(
@SerializedName("authType")
val authType: String = GOOGLE_AUTHENTICATOR,
@SerializedName("timestamp")
val timestamp: Long,
)
Loading