Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth authorize #9

Merged
merged 2 commits into from
Dec 13, 2023
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
6 changes: 3 additions & 3 deletions extopy-backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ kotlin {
val koinVersion = "3.5.0"
val exposedVersion = "0.40.1"
val logbackVersion = "0.9.30"
val ktorxVersion = "1.7.0"
val ktorxVersion = "1.7.4"

sourceSets {
val commonMain by getting {
Expand Down Expand Up @@ -60,13 +60,13 @@ kotlin {
implementation("ch.qos.logback:logback-core:$logbackVersion")
implementation("ch.qos.logback:logback-classic:$logbackVersion")

implementation("me.nathanfallet.i18n:i18n:1.0.5")
implementation("me.nathanfallet.i18n:i18n:1.0.7")
implementation("me.nathanfallet.ktorx:ktor-i18n:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-i18n-freemarker:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-routers:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-routers-locale:$ktorxVersion")
implementation("me.nathanfallet.ktorx:ktor-sentry:$ktorxVersion")
implementation("me.nathanfallet.cloudflare:cloudflare-api-client:4.0.8")
implementation("me.nathanfallet.cloudflare:cloudflare-api-client:4.0.10")

implementation("com.mysql:mysql-connector-j:8.0.33")
implementation("at.favre.lib:bcrypt:0.9.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,34 @@ import me.nathanfallet.extopy.models.auth.RegisterCodePayload
import me.nathanfallet.extopy.models.auth.RegisterPayload
import me.nathanfallet.ktorx.controllers.auth.IAuthWithCodeController
import me.nathanfallet.ktorx.models.auth.AuthMapping
import me.nathanfallet.ktorx.routers.auth.AuthAPIRouter
import me.nathanfallet.ktorx.routers.auth.LocalizedAuthWithCodeTemplateRouter
import me.nathanfallet.ktorx.routers.concat.ConcatUnitRouter
import me.nathanfallet.ktorx.usecases.localization.IGetLocaleForCallUseCase

class AuthRouter(
controller: IAuthWithCodeController<LoginPayload, RegisterPayload, RegisterCodePayload>,
getLocaleForCallUseCase: IGetLocaleForCallUseCase,
) : LocalizedAuthWithCodeTemplateRouter<LoginPayload, RegisterPayload, RegisterCodePayload>(
LoginPayload::class,
RegisterPayload::class,
RegisterCodePayload::class,
AuthMapping(loginTemplate = "auth/login.ftl", registerTemplate = "auth/register.ftl"),
{ template, model -> respondTemplate(template, model) },
controller,
getLocaleForCallUseCase
) : ConcatUnitRouter(
listOf(
LocalizedAuthWithCodeTemplateRouter(
LoginPayload::class,
RegisterPayload::class,
RegisterCodePayload::class,
AuthMapping(
loginTemplate = "auth/login.ftl",
registerTemplate = "auth/register.ftl",
authorizeTemplate = "auth/authorize.ftl",
redirectTemplate = "auth/redirect.ftl",
redirectUnauthorizedToUrl = "/auth/login?redirect={path}",
),
{ template, model -> respondTemplate(template, model) },
controller,
getLocaleForCallUseCase
),
AuthAPIRouter(
controller,
prefix = "/api/v1"
)
)
)
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package me.nathanfallet.extopy.database

import kotlinx.coroutines.Dispatchers
import me.nathanfallet.extopy.database.application.Clients
import me.nathanfallet.extopy.database.application.CodesInEmails
import me.nathanfallet.extopy.database.notifications.Notifications
import me.nathanfallet.extopy.database.notifications.TokensInNotifications
import me.nathanfallet.extopy.database.posts.LikesInPosts
import me.nathanfallet.extopy.database.posts.Posts
import me.nathanfallet.extopy.database.users.ClientsInUsers
import me.nathanfallet.extopy.database.users.FollowersInUsers
import me.nathanfallet.extopy.database.users.Users
import org.jetbrains.exposed.sql.SchemaUtils
Expand All @@ -20,34 +22,31 @@ class Database(
password: String = "",
) {

private val database: org.jetbrains.exposed.sql.Database
// Connect to database
private val database: org.jetbrains.exposed.sql.Database = when (protocol) {
"mysql" -> org.jetbrains.exposed.sql.Database.connect(
"jdbc:mysql://$host:3306/$name", "com.mysql.cj.jdbc.Driver",
user, password
)

init {
// Connect to database
database = when (protocol) {
"mysql" -> org.jetbrains.exposed.sql.Database.connect(
"jdbc:mysql://$host:3306/$name", "com.mysql.cj.jdbc.Driver",
user, password
)

"h2" -> org.jetbrains.exposed.sql.Database.connect(
"jdbc:h2:mem:$name;DB_CLOSE_DELAY=-1;", "org.h2.Driver"
)
"h2" -> org.jetbrains.exposed.sql.Database.connect(
"jdbc:h2:mem:$name;DB_CLOSE_DELAY=-1;", "org.h2.Driver"
)

else -> throw Exception("Unsupported database protocol: $protocol")
}
else -> throw Exception("Unsupported database protocol: $protocol")
}

// Create tables (if needed)
init {
transaction(database) {
//SchemaUtils.create(Authorizes)
//SchemaUtils.create(Clients)
SchemaUtils.create(Clients)
SchemaUtils.create(CodesInEmails)
SchemaUtils.create(Users)
SchemaUtils.create(ClientsInUsers)
SchemaUtils.create(FollowersInUsers)
SchemaUtils.create(Notifications)
SchemaUtils.create(TokensInNotifications)
SchemaUtils.create(Posts)
SchemaUtils.create(LikesInPosts)
SchemaUtils.create(Users)
SchemaUtils.create(FollowersInUsers)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package me.nathanfallet.extopy.database.application

import me.nathanfallet.extopy.extensions.generateId
import me.nathanfallet.extopy.models.application.Client
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.select

object Clients : Table() {

val id = varchar("id", 32)
val ownerId = varchar("owner_id", 32)
val name = varchar("name", 255)
val description = text("description")
val secret = varchar("secret", 255)
val redirectUri = text("redirect_uri")

override val primaryKey = PrimaryKey(id)

fun generateId(): String {
val candidate = String.generateId()
return if (select { id eq candidate }.count() > 0) generateId() else candidate
}

fun toClient(
row: ResultRow,
) = Client(
row[id],
row[ownerId],
row[name],
row[description],
row[secret],
row[redirectUri]
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package me.nathanfallet.extopy.database.application

import me.nathanfallet.extopy.database.Database
import me.nathanfallet.extopy.models.application.Client
import me.nathanfallet.usecases.context.IContext
import me.nathanfallet.usecases.models.repositories.IModelSuspendRepository
import org.jetbrains.exposed.sql.select

class DatabaseClientsRepository(
private val database: Database,
) : IModelSuspendRepository<Client, String, Unit, Unit> {

override suspend fun get(id: String, context: IContext?): Client? {
return database.dbQuery {
Clients
.select { Clients.id eq id }
.map(Clients::toClient)
.singleOrNull()
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package me.nathanfallet.extopy.database.users

import kotlinx.datetime.toInstant
import me.nathanfallet.extopy.extensions.generateId
import me.nathanfallet.extopy.models.users.ClientInUser
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.select

object ClientsInUsers : Table() {

val code = varchar("code", 32)
val userId = varchar("user_id", 32)
val clientId = varchar("client_id", 32)
val expiration = varchar("expiration", 255)

override val primaryKey = PrimaryKey(code)

fun generateCode(): String {
val candidate = String.generateId()
return if (select { code eq candidate }.count() > 0) generateCode() else candidate
}

fun toClientInUser(
row: ResultRow,
) = ClientInUser(
row[code],
row[userId],
row[clientId],
row[expiration].toInstant()
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package me.nathanfallet.extopy.database.users

import kotlinx.datetime.Instant
import me.nathanfallet.extopy.database.Database
import me.nathanfallet.extopy.models.users.ClientInUser
import me.nathanfallet.extopy.repositories.users.IClientsInUsersRepository
import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select

class DatabaseClientsInUsersRepository(
private val database: Database,
) : IClientsInUsersRepository {

override suspend fun create(userId: String, clientId: String, expiration: Instant): ClientInUser? {
return database.dbQuery {
ClientsInUsers.insert {
it[code] = generateCode()
it[ClientsInUsers.userId] = userId
it[ClientsInUsers.clientId] = clientId
it[ClientsInUsers.expiration] = expiration.toString()
}.resultedValues?.map(ClientsInUsers::toClientInUser)?.singleOrNull()
}
}

override suspend fun get(code: String): ClientInUser? {
return database.dbQuery {
ClientsInUsers
.select { ClientsInUsers.code eq code }
.map(ClientsInUsers::toClientInUser)
.singleOrNull()
}
}

override suspend fun delete(code: String): Boolean {
return database.dbQuery {
ClientsInUsers.deleteWhere {
Op.build { ClientsInUsers.code eq code }
}
} == 1
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package me.nathanfallet.extopy.database.users

import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDate
import kotlinx.serialization.Serializable
import me.nathanfallet.extopy.database.posts.Posts
import me.nathanfallet.extopy.extensions.generateId
import me.nathanfallet.extopy.models.users.User
Expand Down Expand Up @@ -65,9 +64,3 @@ object Users : Table() {
)

}

@Serializable
data class UserAuthorize(val client_id: String, val client_secret: String, val code: String)

//@Serializable
//data class UserAuthorizeRequest(val client: Client, val user: User)
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import me.nathanfallet.extopy.controllers.posts.PostsRouter
import me.nathanfallet.extopy.controllers.users.UsersController
import me.nathanfallet.extopy.controllers.users.UsersRouter
import me.nathanfallet.extopy.database.Database
import me.nathanfallet.extopy.database.application.DatabaseClientsRepository
import me.nathanfallet.extopy.database.application.DatabaseCodesInEmailsRepository
import me.nathanfallet.extopy.database.posts.DatabasePostsRepository
import me.nathanfallet.extopy.database.users.DatabaseClientsInUsersRepository
import me.nathanfallet.extopy.database.users.DatabaseUsersRepository
import me.nathanfallet.extopy.models.application.Client
import me.nathanfallet.extopy.models.auth.LoginPayload
import me.nathanfallet.extopy.models.auth.RegisterCodePayload
import me.nathanfallet.extopy.models.auth.RegisterPayload
Expand All @@ -21,9 +24,12 @@ import me.nathanfallet.extopy.models.users.UpdateUserPayload
import me.nathanfallet.extopy.models.users.User
import me.nathanfallet.extopy.repositories.application.ICodesInEmailsRepository
import me.nathanfallet.extopy.repositories.posts.IPostsRepository
import me.nathanfallet.extopy.repositories.users.IClientsInUsersRepository
import me.nathanfallet.extopy.repositories.users.IUsersRepository
import me.nathanfallet.extopy.services.emails.EmailsService
import me.nathanfallet.extopy.services.emails.IEmailsService
import me.nathanfallet.extopy.services.jwt.IJWTService
import me.nathanfallet.extopy.services.jwt.JWTService
import me.nathanfallet.extopy.usecases.application.SendEmailUseCase
import me.nathanfallet.extopy.usecases.auth.*
import me.nathanfallet.extopy.usecases.posts.CreatePostUseCase
Expand All @@ -47,8 +53,11 @@ import me.nathanfallet.usecases.localization.ITranslateUseCase
import me.nathanfallet.usecases.models.create.ICreateModelSuspendUseCase
import me.nathanfallet.usecases.models.create.context.ICreateModelWithContextSuspendUseCase
import me.nathanfallet.usecases.models.delete.IDeleteModelSuspendUseCase
import me.nathanfallet.usecases.models.get.GetModelFromRepositorySuspendUseCase
import me.nathanfallet.usecases.models.get.IGetModelSuspendUseCase
import me.nathanfallet.usecases.models.get.context.GetModelWithContextFromRepositorySuspendUseCase
import me.nathanfallet.usecases.models.get.context.IGetModelWithContextSuspendUseCase
import me.nathanfallet.usecases.models.repositories.IModelSuspendRepository
import me.nathanfallet.usecases.models.update.IUpdateModelSuspendUseCase
import org.koin.core.qualifier.named
import org.koin.dsl.module
Expand All @@ -75,17 +84,35 @@ fun Application.configureKoin() {
environment.config.property("email.password").getString()
)
}
single<IJWTService> {
JWTService(
environment.config.property("jwt.secret").getString(),
environment.config.property("jwt.issuer").getString()
)
}
}
val repositoryModule = module {
// Application
single<ICodesInEmailsRepository> { DatabaseCodesInEmailsRepository(get()) }
single<IModelSuspendRepository<Client, String, Unit, Unit>>(named<Client>()) {
DatabaseClientsRepository(get())
}

// Users
single<IUsersRepository> { DatabaseUsersRepository(get()) }
single<IClientsInUsersRepository> { DatabaseClientsInUsersRepository(get()) }

// Posts
single<IPostsRepository> { DatabasePostsRepository(get()) }
}
val useCaseModule = module {
// Application
single<ISendEmailUseCase> { SendEmailUseCase(get()) }
single<ITranslateUseCase> { TranslateUseCase() }
single<IGetLocaleForCallUseCase> { GetLocaleForCallUseCase() }
single<IGetModelSuspendUseCase<Client, String>>(named<Client>()) {
GetModelFromRepositorySuspendUseCase(get(named<Client>()))
}

// Auth
single<IHashPasswordUseCase> { HashPasswordUseCase() }
Expand All @@ -107,6 +134,13 @@ fun Application.configureKoin() {
)
}
single<IDeleteCodeRegisterUseCase> { DeleteCodeRegisterUseCase(get()) }
single<IGetClientUseCase> { GetClientFromModelUseCase<Client>(get(named<Client>())) }
single<ICreateAuthCodeUseCase> { CreateAuthCodeUseCase(get()) }
single<IGetAuthCodeUseCase> { GetAuthCodeUseCase(get(), get(), get(named<User>())) }
single<IDeleteAuthCodeUseCase> { DeleteAuthCodeUseCase(get()) }
single<IGenerateAuthTokenUseCase> {
GenerateAuthTokenUseCase(get())
}

// Users
single<IRequireUserForCallUseCase> { RequireUserForCallUseCase(get()) }
Expand Down Expand Up @@ -139,6 +173,12 @@ fun Application.configureKoin() {
// Auth
single<IAuthWithCodeController<LoginPayload, RegisterPayload, RegisterCodePayload>> {
AuthWithCodeController(
get(),
get(),
get(),
get(),
get(),
get(),
get(),
get(),
get(),
Expand Down
Loading