refactor
This commit is contained in:
@@ -29,9 +29,8 @@ dependencies {
|
|||||||
implementation(libs.exposed)
|
implementation(libs.exposed)
|
||||||
implementation(libs.exposed.dao)
|
implementation(libs.exposed.dao)
|
||||||
implementation(libs.exposed.jdbc)
|
implementation(libs.exposed.jdbc)
|
||||||
|
implementation(libs.migration)
|
||||||
implementation(libs.postgres)
|
implementation(libs.postgres)
|
||||||
implementation(libs.koin)
|
|
||||||
implementation(libs.koin.logger)
|
|
||||||
implementation(libs.random)
|
implementation(libs.random)
|
||||||
implementation(libs.otp)
|
implementation(libs.otp)
|
||||||
implementation(libs.kdf)
|
implementation(libs.kdf)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
kotlin = "2.2.10"
|
kotlin = "2.2.10"
|
||||||
ktor = "3.2.3"
|
ktor = "3.2.3"
|
||||||
exposed = "0.60.0"
|
exposed = "0.60.0"
|
||||||
|
migration = "1.0.0"
|
||||||
postgres = "42.7.5"
|
postgres = "42.7.5"
|
||||||
koin = "4.1.0"
|
|
||||||
random = "1.0.0"
|
random = "1.0.0"
|
||||||
otp = "1.1.1"
|
otp = "1.1.1"
|
||||||
kdf = "1.0.3"
|
kdf = "1.0.3"
|
||||||
@@ -30,11 +30,9 @@ ktor-server-validation = { module = "io.ktor:ktor-server-request-validation", ve
|
|||||||
exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
|
exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
|
||||||
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
|
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
|
||||||
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
|
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
|
||||||
|
migration = { module = "com.infendro:ktor-migration", version.ref = "migration" }
|
||||||
postgres = { module = "org.postgresql:postgresql", version.ref = "postgres" }
|
postgres = { module = "org.postgresql:postgresql", version.ref = "postgres" }
|
||||||
|
|
||||||
koin = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
|
|
||||||
koin-logger = { module = "io.insert-koin:koin-logger-slf4j", version.ref = "koin" }
|
|
||||||
|
|
||||||
random = { module = "com.infendro:random", version.ref = "random" }
|
random = { module = "com.infendro:random", version.ref = "random" }
|
||||||
otp = { module = "com.infendro:otp", version.ref = "otp" }
|
otp = { module = "com.infendro:otp", version.ref = "otp" }
|
||||||
kdf = { module = "com.infendro:kdf", version.ref = "kdf" }
|
kdf = { module = "com.infendro:kdf", version.ref = "kdf" }
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ fun main(
|
|||||||
) = EngineMain.main(args)
|
) = EngineMain.main(args)
|
||||||
|
|
||||||
fun Application.module() {
|
fun Application.module() {
|
||||||
configureDependencyInjection()
|
|
||||||
|
|
||||||
configureDatabase()
|
configureDatabase()
|
||||||
|
|
||||||
configureSerialization()
|
configureSerialization()
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
package com.infendro.account.config
|
package com.infendro.account.config
|
||||||
|
|
||||||
import com.infendro.account.model.Role
|
import com.infendro.account.model.Role
|
||||||
import com.infendro.account.model.entity.MigrationTable
|
|
||||||
import com.infendro.account.model.migration
|
|
||||||
import com.infendro.account.model.repository.AccountRepository
|
import com.infendro.account.model.repository.AccountRepository
|
||||||
import com.infendro.account.util.SecureHasher
|
import com.infendro.account.util.SecureHasher
|
||||||
|
import com.infendro.migration.Migration
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
import org.jetbrains.exposed.sql.SchemaUtils
|
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
|
||||||
import org.koin.ktor.ext.inject
|
|
||||||
|
|
||||||
fun Application.configureDatabase() {
|
fun Application.configureDatabase() {
|
||||||
val config = environment.config.database
|
val config = environment.config.database
|
||||||
@@ -19,16 +15,8 @@ fun Application.configureDatabase() {
|
|||||||
user = config.username,
|
user = config.username,
|
||||||
password = config.password,
|
password = config.password,
|
||||||
)
|
)
|
||||||
migrate()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Application.migrate() {
|
|
||||||
transaction {
|
|
||||||
SchemaUtils.create(MigrationTable)
|
|
||||||
}
|
|
||||||
|
|
||||||
val accounts by inject<AccountRepository>()
|
|
||||||
|
|
||||||
|
install(Migration) {
|
||||||
migration("initial") {
|
migration("initial") {
|
||||||
exec(
|
exec(
|
||||||
"""
|
"""
|
||||||
@@ -70,7 +58,7 @@ fun Application.migrate() {
|
|||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
accounts.insert {
|
AccountRepository.insert {
|
||||||
val salt = SecureHasher.generateSalt()
|
val salt = SecureHasher.generateSalt()
|
||||||
|
|
||||||
this.username = "infendro"
|
this.username = "infendro"
|
||||||
@@ -80,4 +68,5 @@ fun Application.migrate() {
|
|||||||
this.role = Role.OWNER
|
this.role = Role.OWNER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.infendro.account.config
|
|
||||||
|
|
||||||
import com.infendro.account.model.repository.AccessRepository
|
|
||||||
import com.infendro.account.model.repository.AccountRepository
|
|
||||||
import com.infendro.account.model.repository.MigrationRepository
|
|
||||||
import com.infendro.account.model.repository.SessionRepository
|
|
||||||
import com.infendro.account.service.AccessService
|
|
||||||
import com.infendro.account.service.AccountService
|
|
||||||
import com.infendro.account.service.RoleService
|
|
||||||
import com.infendro.account.service.SessionService
|
|
||||||
import io.ktor.server.application.*
|
|
||||||
import org.koin.core.module.dsl.singleOf
|
|
||||||
import org.koin.dsl.module
|
|
||||||
import org.koin.ktor.plugin.Koin
|
|
||||||
import org.koin.logger.slf4jLogger
|
|
||||||
|
|
||||||
fun Application.configureDependencyInjection() {
|
|
||||||
install(Koin) {
|
|
||||||
slf4jLogger()
|
|
||||||
modules(module)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val module = module {
|
|
||||||
singleOf(::MigrationRepository)
|
|
||||||
singleOf(::AccessRepository)
|
|
||||||
singleOf(::AccountRepository)
|
|
||||||
singleOf(::SessionRepository)
|
|
||||||
|
|
||||||
singleOf(::AccessService)
|
|
||||||
singleOf(::AccountService)
|
|
||||||
singleOf(::RoleService)
|
|
||||||
singleOf(::SessionService)
|
|
||||||
}
|
|
||||||
@@ -7,15 +7,12 @@ import com.infendro.account.model.entity.SessionEntity
|
|||||||
import com.infendro.account.model.entity.SessionTable
|
import com.infendro.account.model.entity.SessionTable
|
||||||
import com.infendro.account.model.repository.SessionRepository
|
import com.infendro.account.model.repository.SessionRepository
|
||||||
import com.infendro.account.util.Hasher
|
import com.infendro.account.util.Hasher
|
||||||
import io.ktor.server.application.Application
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.application.install
|
import io.ktor.server.auth.*
|
||||||
import io.ktor.server.auth.Authentication
|
|
||||||
import io.ktor.server.auth.session
|
|
||||||
import io.ktor.server.sessions.*
|
import io.ktor.server.sessions.*
|
||||||
import kotlin.time.Duration.Companion.days
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.koin.ktor.ext.inject
|
import kotlin.time.Duration.Companion.days
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AuthenticationSession(
|
data class AuthenticationSession(
|
||||||
@@ -46,9 +43,7 @@ fun Application.configureSecurity() {
|
|||||||
install(Authentication) {
|
install(Authentication) {
|
||||||
session<AuthenticationSession> {
|
session<AuthenticationSession> {
|
||||||
validate { authenticationSession ->
|
validate { authenticationSession ->
|
||||||
val sessionRepository by inject<SessionRepository>()
|
val session = SessionRepository
|
||||||
|
|
||||||
val session = sessionRepository
|
|
||||||
.singleOrNull { SessionTable.tokenHash eq Hasher.hash(authenticationSession.token) }
|
.singleOrNull { SessionTable.tokenHash eq Hasher.hash(authenticationSession.token) }
|
||||||
?: return@validate null
|
?: return@validate null
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.infendro.account.model
|
|
||||||
|
|
||||||
import com.infendro.account.model.entity.MigrationTable
|
|
||||||
import com.infendro.account.model.repository.MigrationRepository
|
|
||||||
import io.ktor.server.application.*
|
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
|
||||||
import org.jetbrains.exposed.sql.Transaction
|
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
|
||||||
import org.koin.ktor.ext.inject
|
|
||||||
|
|
||||||
fun Application.migration(
|
|
||||||
name: String,
|
|
||||||
block: Transaction.() -> Unit,
|
|
||||||
) {
|
|
||||||
val migrations by inject<MigrationRepository>()
|
|
||||||
|
|
||||||
migrations
|
|
||||||
.singleOrNull { MigrationTable.name eq name }
|
|
||||||
?.run { return }
|
|
||||||
|
|
||||||
transaction {
|
|
||||||
try {
|
|
||||||
block()
|
|
||||||
log.info("""Migration "$name" successful""")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
log.error("""Migration "$name" failed""")
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
migrations.insert {
|
|
||||||
this.name = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package com.infendro.account.model.entity
|
|
||||||
|
|
||||||
import org.jetbrains.exposed.dao.LongEntity
|
|
||||||
import org.jetbrains.exposed.dao.LongEntityClass
|
|
||||||
import org.jetbrains.exposed.dao.id.EntityID
|
|
||||||
import org.jetbrains.exposed.dao.id.LongIdTable
|
|
||||||
|
|
||||||
object MigrationTable : LongIdTable("migration", "id") {
|
|
||||||
val name = text("name")
|
|
||||||
}
|
|
||||||
|
|
||||||
class MigrationEntity(id: EntityID<Long>) : LongEntity(id) {
|
|
||||||
companion object : LongEntityClass<MigrationEntity>(MigrationTable)
|
|
||||||
|
|
||||||
var name by MigrationTable.name
|
|
||||||
}
|
|
||||||
@@ -2,4 +2,4 @@ package com.infendro.account.model.repository
|
|||||||
|
|
||||||
import com.infendro.account.model.entity.AccessEntity
|
import com.infendro.account.model.entity.AccessEntity
|
||||||
|
|
||||||
class AccessRepository : Repository<AccessEntity>(AccessEntity)
|
object AccessRepository : Repository<AccessEntity>(AccessEntity)
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ package com.infendro.account.model.repository
|
|||||||
|
|
||||||
import com.infendro.account.model.entity.AccountEntity
|
import com.infendro.account.model.entity.AccountEntity
|
||||||
|
|
||||||
class AccountRepository : Repository<AccountEntity>(AccountEntity)
|
object AccountRepository : Repository<AccountEntity>(AccountEntity)
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.infendro.account.model.repository
|
|
||||||
|
|
||||||
import com.infendro.account.model.entity.MigrationEntity
|
|
||||||
|
|
||||||
class MigrationRepository : Repository<MigrationEntity>(MigrationEntity)
|
|
||||||
@@ -50,8 +50,8 @@ abstract class Repository<ENTITY : LongEntity>(
|
|||||||
|
|
||||||
fun exists(
|
fun exists(
|
||||||
where: Op<Boolean>,
|
where: Op<Boolean>,
|
||||||
): Boolean {
|
): Boolean = transaction {
|
||||||
return all(where).isNotEmpty()
|
entityClass.find(where).any()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exists(
|
fun exists(
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ package com.infendro.account.model.repository
|
|||||||
|
|
||||||
import com.infendro.account.model.entity.SessionEntity
|
import com.infendro.account.model.entity.SessionEntity
|
||||||
|
|
||||||
class SessionRepository : Repository<SessionEntity>(SessionEntity)
|
object SessionRepository : Repository<SessionEntity>(SessionEntity)
|
||||||
|
|||||||
@@ -3,15 +3,12 @@ package com.infendro.account.routing
|
|||||||
import com.infendro.account.config.AuthenticationPrincipal
|
import com.infendro.account.config.AuthenticationPrincipal
|
||||||
import com.infendro.account.service.AccessService
|
import com.infendro.account.service.AccessService
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
import io.ktor.resources.Resource
|
import io.ktor.resources.*
|
||||||
import io.ktor.server.auth.authenticate
|
import io.ktor.server.auth.*
|
||||||
import io.ktor.server.auth.principal
|
import io.ktor.server.resources.*
|
||||||
import io.ktor.server.resources.delete
|
|
||||||
import io.ktor.server.resources.get
|
|
||||||
import io.ktor.server.resources.post
|
import io.ktor.server.resources.post
|
||||||
import io.ktor.server.response.respond
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.Routing
|
import io.ktor.server.routing.Routing
|
||||||
import org.koin.ktor.ext.inject
|
|
||||||
|
|
||||||
@Resource("/access")
|
@Resource("/access")
|
||||||
private class Access {
|
private class Access {
|
||||||
@@ -28,20 +25,18 @@ private class Access {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Routing.access() {
|
fun Routing.access() {
|
||||||
val accessService by inject<AccessService>()
|
|
||||||
|
|
||||||
authenticate {
|
authenticate {
|
||||||
post<Access> {
|
post<Access> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
accessService.post(principal)
|
AccessService.post(principal)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
get<Access.All> {
|
get<Access.All> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
accessService.getAll(principal)
|
AccessService.getAll(principal)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +44,7 @@ fun Routing.access() {
|
|||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
val id = resource.id
|
val id = resource.id
|
||||||
|
|
||||||
accessService.deleteId(principal, id)
|
AccessService.deleteId(principal, id)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import io.ktor.server.resources.put
|
|||||||
import io.ktor.server.response.*
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.Routing
|
import io.ktor.server.routing.Routing
|
||||||
import io.ktor.server.sessions.*
|
import io.ktor.server.sessions.*
|
||||||
import org.koin.ktor.ext.inject
|
|
||||||
|
|
||||||
@Resource("/account")
|
@Resource("/account")
|
||||||
private class Account {
|
private class Account {
|
||||||
@@ -71,12 +70,10 @@ private class Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Routing.account() {
|
fun Routing.account() {
|
||||||
val accountService by inject<AccountService>()
|
|
||||||
|
|
||||||
post<Account> {
|
post<Account> {
|
||||||
val request = call.receive<PostAccountRequest>()
|
val request = call.receive<PostAccountRequest>()
|
||||||
|
|
||||||
accountService.post(request)
|
AccountService.post(request)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,21 +81,21 @@ fun Routing.account() {
|
|||||||
get<Account.All> {
|
get<Account.All> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
accountService.getAll(principal)
|
AccountService.getAll(principal)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
get<Account.Current> {
|
get<Account.Current> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
accountService.getCurrent(principal)
|
AccountService.getCurrent(principal)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
delete<Account.Current> {
|
delete<Account.Current> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
accountService.deleteCurrent(principal)
|
AccountService.deleteCurrent(principal)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +103,7 @@ fun Routing.account() {
|
|||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
val request = call.receive<PutAccountCurrentUsernameRequest>()
|
val request = call.receive<PutAccountCurrentUsernameRequest>()
|
||||||
|
|
||||||
accountService.putCurrentUsername(principal, call.sessions, request)
|
AccountService.putCurrentUsername(principal, call.sessions, request)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +111,7 @@ fun Routing.account() {
|
|||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
val request = call.receive<PutAccountCurrentPasswordRequest>()
|
val request = call.receive<PutAccountCurrentPasswordRequest>()
|
||||||
|
|
||||||
accountService.putCurrentPassword(principal, call.sessions, request)
|
AccountService.putCurrentPassword(principal, call.sessions, request)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,14 +119,14 @@ fun Routing.account() {
|
|||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
val request = call.receive<PostAccountCurrentSecretRequest>()
|
val request = call.receive<PostAccountCurrentSecretRequest>()
|
||||||
|
|
||||||
accountService.postCurrentSecret(principal, call.sessions, request)
|
AccountService.postCurrentSecret(principal, call.sessions, request)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
get<Account.Current.Session.All> {
|
get<Account.Current.Session.All> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
accountService.getCurrentSessionAll(principal)
|
AccountService.getCurrentSessionAll(principal)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +134,7 @@ fun Routing.account() {
|
|||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
val id = resource.id
|
val id = resource.id
|
||||||
|
|
||||||
accountService.deleteCurrentSessionId(principal, call.sessions, id)
|
AccountService.deleteCurrentSessionId(principal, call.sessions, id)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +142,7 @@ fun Routing.account() {
|
|||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
val id = resource.id
|
val id = resource.id
|
||||||
|
|
||||||
accountService.deleteId(principal, id)
|
AccountService.deleteId(principal, id)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +151,7 @@ fun Routing.account() {
|
|||||||
val id = resource.parent.id
|
val id = resource.parent.id
|
||||||
val request = call.receive<PutAccountIdRoleRequest>()
|
val request = call.receive<PutAccountIdRoleRequest>()
|
||||||
|
|
||||||
accountService.putIdRole(principal, id, request)
|
AccountService.putIdRole(principal, id, request)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package com.infendro.account.routing
|
package com.infendro.account.routing
|
||||||
|
|
||||||
import com.infendro.account.service.RoleService
|
import com.infendro.account.service.RoleService
|
||||||
import io.ktor.resources.Resource
|
import io.ktor.resources.*
|
||||||
import io.ktor.server.resources.get
|
import io.ktor.server.resources.*
|
||||||
import io.ktor.server.response.respond
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.Routing
|
import io.ktor.server.routing.*
|
||||||
import org.koin.ktor.ext.inject
|
|
||||||
|
|
||||||
@Resource("/role")
|
@Resource("/role")
|
||||||
private class Role {
|
private class Role {
|
||||||
@@ -16,10 +15,8 @@ private class Role {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Routing.role() {
|
fun Routing.role() {
|
||||||
val roleService by inject<RoleService>()
|
|
||||||
|
|
||||||
get<Role.All> {
|
get<Role.All> {
|
||||||
roleService.getAll()
|
RoleService.getAll()
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,14 @@ import com.infendro.account.config.AuthenticationPrincipal
|
|||||||
import com.infendro.account.dto.request.PostSessionRequest
|
import com.infendro.account.dto.request.PostSessionRequest
|
||||||
import com.infendro.account.service.SessionService
|
import com.infendro.account.service.SessionService
|
||||||
import io.ktor.http.HttpStatusCode.Companion.OK
|
import io.ktor.http.HttpStatusCode.Companion.OK
|
||||||
import io.ktor.resources.Resource
|
import io.ktor.resources.*
|
||||||
import io.ktor.server.auth.authenticate
|
import io.ktor.server.auth.*
|
||||||
import io.ktor.server.auth.principal
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.request.receive
|
import io.ktor.server.resources.*
|
||||||
import io.ktor.server.resources.delete
|
|
||||||
import io.ktor.server.resources.get
|
|
||||||
import io.ktor.server.resources.post
|
import io.ktor.server.resources.post
|
||||||
import io.ktor.server.response.respond
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.Routing
|
import io.ktor.server.routing.Routing
|
||||||
import io.ktor.server.sessions.sessions
|
import io.ktor.server.sessions.*
|
||||||
import org.koin.ktor.ext.inject
|
|
||||||
|
|
||||||
@Resource("/session")
|
@Resource("/session")
|
||||||
private class Session {
|
private class Session {
|
||||||
@@ -36,12 +33,10 @@ private class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Routing.session() {
|
fun Routing.session() {
|
||||||
val sessionService by inject<SessionService>()
|
|
||||||
|
|
||||||
post<Session> {
|
post<Session> {
|
||||||
val request = call.receive<PostSessionRequest>()
|
val request = call.receive<PostSessionRequest>()
|
||||||
|
|
||||||
sessionService.post(call.sessions, request)
|
SessionService.post(call.sessions, request)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +44,7 @@ fun Routing.session() {
|
|||||||
get<Session.All> {
|
get<Session.All> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
sessionService.getAll(principal)
|
SessionService.getAll(principal)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,21 +52,21 @@ fun Routing.session() {
|
|||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
val id = resource.id
|
val id = resource.id
|
||||||
|
|
||||||
sessionService.deleteId(principal, id)
|
SessionService.deleteId(principal, id)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
get<Session.Current> {
|
get<Session.Current> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
sessionService.getCurrent(principal)
|
SessionService.getCurrent(principal)
|
||||||
.also { call.respond(it) }
|
.also { call.respond(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
delete<Session.Current> {
|
delete<Session.Current> {
|
||||||
val principal = call.principal<AuthenticationPrincipal>()!!
|
val principal = call.principal<AuthenticationPrincipal>()!!
|
||||||
|
|
||||||
sessionService.deleteCurrent(principal, call.sessions)
|
SessionService.deleteCurrent(principal, call.sessions)
|
||||||
call.respond(OK)
|
call.respond(OK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ import com.infendro.account.util.Hasher
|
|||||||
import com.infendro.account.util.TokenGenerator
|
import com.infendro.account.util.TokenGenerator
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
|
||||||
class AccessService(
|
object AccessService {
|
||||||
private val accessRepository: AccessRepository,
|
|
||||||
) {
|
|
||||||
fun post(
|
fun post(
|
||||||
principal: AuthenticationPrincipal,
|
principal: AuthenticationPrincipal,
|
||||||
): PostAccessResponse {
|
): PostAccessResponse {
|
||||||
@@ -23,7 +21,7 @@ class AccessService(
|
|||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
val token = TokenGenerator.generate()
|
val token = TokenGenerator.generate()
|
||||||
accessRepository.insert {
|
AccessRepository.insert {
|
||||||
this.tokenHash = Hasher.hash(token)
|
this.tokenHash = Hasher.hash(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +36,7 @@ class AccessService(
|
|||||||
if (principal.role != OWNER)
|
if (principal.role != OWNER)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
return accessRepository
|
return AccessRepository
|
||||||
.all()
|
.all()
|
||||||
.map { it.toResponse() }
|
.map { it.toResponse() }
|
||||||
}
|
}
|
||||||
@@ -50,10 +48,10 @@ class AccessService(
|
|||||||
if (principal.role != OWNER)
|
if (principal.role != OWNER)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
val access = accessRepository
|
val access = AccessRepository
|
||||||
.singleOrNull { AccessTable.id eq id }
|
.singleOrNull { AccessTable.id eq id }
|
||||||
?: throw NotFoundException()
|
?: throw NotFoundException()
|
||||||
|
|
||||||
accessRepository.delete(access)
|
AccessRepository.delete(access)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,26 +20,22 @@ import com.infendro.account.util.SecureHasher
|
|||||||
import io.ktor.server.sessions.*
|
import io.ktor.server.sessions.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
|
||||||
class AccountService(
|
object AccountService {
|
||||||
private val accessRepository: AccessRepository,
|
|
||||||
private val accountRepository: AccountRepository,
|
|
||||||
private val sessionRepository: SessionRepository,
|
|
||||||
) {
|
|
||||||
fun post(
|
fun post(
|
||||||
request: PostAccountRequest,
|
request: PostAccountRequest,
|
||||||
): PostAccountResponse {
|
): PostAccountResponse {
|
||||||
val access = accessRepository
|
val access = AccessRepository
|
||||||
.singleOrNull { AccessTable.tokenHash eq Hasher.hash(request.access.token) }
|
.singleOrNull { AccessTable.tokenHash eq Hasher.hash(request.access.token) }
|
||||||
?: throw UnauthorizedException()
|
?: throw UnauthorizedException()
|
||||||
|
|
||||||
accountRepository
|
AccountRepository
|
||||||
.singleOrNull { AccountTable.username eq request.username }
|
.singleOrNull { AccountTable.username eq request.username }
|
||||||
?.run { throw ConflictException() }
|
?.run { throw ConflictException() }
|
||||||
|
|
||||||
accessRepository.delete(access)
|
AccessRepository.delete(access)
|
||||||
|
|
||||||
val secret = OTP.generateSecret()
|
val secret = OTP.generateSecret()
|
||||||
accountRepository.insert {
|
AccountRepository.insert {
|
||||||
val salt = SecureHasher.generateSalt()
|
val salt = SecureHasher.generateSalt()
|
||||||
|
|
||||||
this.username = request.username
|
this.username = request.username
|
||||||
@@ -60,7 +56,7 @@ class AccountService(
|
|||||||
if (principal.role != OWNER)
|
if (principal.role != OWNER)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
return accountRepository
|
return AccountRepository
|
||||||
.all()
|
.all()
|
||||||
.map(AccountEntity::toResponse)
|
.map(AccountEntity::toResponse)
|
||||||
}
|
}
|
||||||
@@ -77,7 +73,7 @@ class AccountService(
|
|||||||
if (principal.role == OWNER)
|
if (principal.role == OWNER)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
accountRepository.delete(principal.account)
|
AccountRepository.delete(principal.account)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putCurrentUsername(
|
fun putCurrentUsername(
|
||||||
@@ -88,10 +84,10 @@ class AccountService(
|
|||||||
if (!OTP.verify(principal.account.secret, request.otp))
|
if (!OTP.verify(principal.account.secret, request.otp))
|
||||||
throw UnauthorizedException()
|
throw UnauthorizedException()
|
||||||
|
|
||||||
accountRepository.update(principal.account) {
|
AccountRepository.update(principal.account) {
|
||||||
this.username = request.username
|
this.username = request.username
|
||||||
}
|
}
|
||||||
sessionRepository.delete(principal.account.sessions())
|
SessionRepository.delete(principal.account.sessions())
|
||||||
|
|
||||||
sessions.clear<AuthenticationSession>()
|
sessions.clear<AuthenticationSession>()
|
||||||
}
|
}
|
||||||
@@ -104,13 +100,13 @@ class AccountService(
|
|||||||
if (!OTP.verify(principal.account.secret, request.otp))
|
if (!OTP.verify(principal.account.secret, request.otp))
|
||||||
throw UnauthorizedException()
|
throw UnauthorizedException()
|
||||||
|
|
||||||
accountRepository.update(principal.account) {
|
AccountRepository.update(principal.account) {
|
||||||
val salt = SecureHasher.generateSalt()
|
val salt = SecureHasher.generateSalt()
|
||||||
|
|
||||||
this.passwordHash = SecureHasher.hash(request.password, salt)
|
this.passwordHash = SecureHasher.hash(request.password, salt)
|
||||||
this.passwordSalt = salt
|
this.passwordSalt = salt
|
||||||
}
|
}
|
||||||
sessionRepository.delete(principal.account.sessions())
|
SessionRepository.delete(principal.account.sessions())
|
||||||
|
|
||||||
sessions.clear<AuthenticationSession>()
|
sessions.clear<AuthenticationSession>()
|
||||||
}
|
}
|
||||||
@@ -124,10 +120,10 @@ class AccountService(
|
|||||||
throw UnauthorizedException()
|
throw UnauthorizedException()
|
||||||
|
|
||||||
val secret = OTP.generateSecret()
|
val secret = OTP.generateSecret()
|
||||||
accountRepository.update(principal.account) {
|
AccountRepository.update(principal.account) {
|
||||||
this.secret = secret
|
this.secret = secret
|
||||||
}
|
}
|
||||||
sessionRepository.delete(principal.account.sessions())
|
SessionRepository.delete(principal.account.sessions())
|
||||||
|
|
||||||
sessions.clear<AuthenticationSession>()
|
sessions.clear<AuthenticationSession>()
|
||||||
|
|
||||||
@@ -148,14 +144,14 @@ class AccountService(
|
|||||||
sessions: CurrentSession,
|
sessions: CurrentSession,
|
||||||
id: Long,
|
id: Long,
|
||||||
) {
|
) {
|
||||||
val session = sessionRepository
|
val session = SessionRepository
|
||||||
.singleOrNull { SessionTable.id eq id }
|
.singleOrNull { SessionTable.id eq id }
|
||||||
?: throw NotFoundException()
|
?: throw NotFoundException()
|
||||||
|
|
||||||
if (session.account().id != principal.account.id)
|
if (session.account().id != principal.account.id)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
sessionRepository.delete(session)
|
SessionRepository.delete(session)
|
||||||
|
|
||||||
if (session.id == principal.session.id)
|
if (session.id == principal.session.id)
|
||||||
sessions.clear<AuthenticationSession>()
|
sessions.clear<AuthenticationSession>()
|
||||||
@@ -165,14 +161,14 @@ class AccountService(
|
|||||||
principal: AuthenticationPrincipal,
|
principal: AuthenticationPrincipal,
|
||||||
id: Long,
|
id: Long,
|
||||||
) {
|
) {
|
||||||
val account = accountRepository
|
val account = AccountRepository
|
||||||
.singleOrNull { AccountTable.id eq id }
|
.singleOrNull { AccountTable.id eq id }
|
||||||
?: throw NotFoundException()
|
?: throw NotFoundException()
|
||||||
|
|
||||||
if (account.role !in principal.role.children)
|
if (account.role !in principal.role.children)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
accountRepository.delete(account)
|
AccountRepository.delete(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putIdRole(
|
fun putIdRole(
|
||||||
@@ -180,7 +176,7 @@ class AccountService(
|
|||||||
id: Long,
|
id: Long,
|
||||||
request: PutAccountIdRoleRequest,
|
request: PutAccountIdRoleRequest,
|
||||||
) {
|
) {
|
||||||
val account = accountRepository
|
val account = AccountRepository
|
||||||
.singleOrNull { AccountTable.id eq id }
|
.singleOrNull { AccountTable.id eq id }
|
||||||
?: throw NotFoundException()
|
?: throw NotFoundException()
|
||||||
|
|
||||||
@@ -190,7 +186,7 @@ class AccountService(
|
|||||||
if (request.role !in principal.role.children)
|
if (request.role !in principal.role.children)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
accountRepository.update(account) {
|
AccountRepository.update(account) {
|
||||||
role = request.role
|
role = request.role
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import com.infendro.account.dto.response.RoleResponse
|
|||||||
import com.infendro.account.dto.response.toResponse
|
import com.infendro.account.dto.response.toResponse
|
||||||
import com.infendro.account.model.Role
|
import com.infendro.account.model.Role
|
||||||
|
|
||||||
class RoleService {
|
object RoleService {
|
||||||
fun getAll(): List<RoleResponse> {
|
fun getAll(): List<RoleResponse> {
|
||||||
return Role.ALL
|
return Role.ALL
|
||||||
.map(Role::toResponse)
|
.map(Role::toResponse)
|
||||||
|
|||||||
@@ -22,15 +22,12 @@ import io.ktor.server.sessions.*
|
|||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
|
||||||
|
|
||||||
class SessionService(
|
object SessionService {
|
||||||
private val accountRepository: AccountRepository,
|
|
||||||
private val sessionRepository: SessionRepository,
|
|
||||||
) {
|
|
||||||
fun post(
|
fun post(
|
||||||
sessions: CurrentSession,
|
sessions: CurrentSession,
|
||||||
request: PostSessionRequest,
|
request: PostSessionRequest,
|
||||||
) {
|
) {
|
||||||
val account = accountRepository
|
val account = AccountRepository
|
||||||
.singleOrNull { AccountTable.username eq request.username }
|
.singleOrNull { AccountTable.username eq request.username }
|
||||||
?: throw NotFoundException()
|
?: throw NotFoundException()
|
||||||
|
|
||||||
@@ -41,7 +38,7 @@ class SessionService(
|
|||||||
throw UnauthorizedException()
|
throw UnauthorizedException()
|
||||||
|
|
||||||
val token = TokenGenerator.generate()
|
val token = TokenGenerator.generate()
|
||||||
sessionRepository.insert {
|
SessionRepository.insert {
|
||||||
this.tokenHash = Hasher.hash(token)
|
this.tokenHash = Hasher.hash(token)
|
||||||
this.account = account
|
this.account = account
|
||||||
}
|
}
|
||||||
@@ -57,7 +54,7 @@ class SessionService(
|
|||||||
if (principal.role == USER)
|
if (principal.role == USER)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
return accountRepository
|
return AccountRepository
|
||||||
.all { AccountTable.role inList principal.role.children }
|
.all { AccountTable.role inList principal.role.children }
|
||||||
.flatMap { it.sessions() }
|
.flatMap { it.sessions() }
|
||||||
.map(SessionEntity::toResponse)
|
.map(SessionEntity::toResponse)
|
||||||
@@ -70,14 +67,14 @@ class SessionService(
|
|||||||
if (principal.role == USER)
|
if (principal.role == USER)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
val session = sessionRepository
|
val session = SessionRepository
|
||||||
.singleOrNull { SessionTable.id eq id }
|
.singleOrNull { SessionTable.id eq id }
|
||||||
?: throw NotFoundException()
|
?: throw NotFoundException()
|
||||||
|
|
||||||
if (session.account().role !in principal.role.children)
|
if (session.account().role !in principal.role.children)
|
||||||
throw ForbiddenException()
|
throw ForbiddenException()
|
||||||
|
|
||||||
sessionRepository.delete(session)
|
SessionRepository.delete(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrent(
|
fun getCurrent(
|
||||||
@@ -90,7 +87,7 @@ class SessionService(
|
|||||||
principal: AuthenticationPrincipal,
|
principal: AuthenticationPrincipal,
|
||||||
sessions: CurrentSession,
|
sessions: CurrentSession,
|
||||||
) {
|
) {
|
||||||
sessionRepository.delete(principal.session)
|
SessionRepository.delete(principal.session)
|
||||||
|
|
||||||
sessions.clear<AuthenticationSession>()
|
sessions.clear<AuthenticationSession>()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import com.infendro.kdf.PBKDF2
|
|||||||
import com.infendro.random.PRNG
|
import com.infendro.random.PRNG
|
||||||
|
|
||||||
object SecureHasher {
|
object SecureHasher {
|
||||||
private val kdf = PBKDF2(SHA256)
|
|
||||||
private val random = PRNG.HMAC
|
private val random = PRNG.HMAC
|
||||||
|
private val kdf = PBKDF2(SHA256)
|
||||||
|
|
||||||
fun hash(
|
fun hash(
|
||||||
value: String,
|
value: String,
|
||||||
|
|||||||
Reference in New Issue
Block a user