full reborn

This commit is contained in:
2021-11-24 13:52:27 +06:00
parent 0ac6b0a4df
commit 6a6a197041
246 changed files with 4327 additions and 6952 deletions

View File

@@ -0,0 +1,18 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply from: "$mppJavaProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":postssystem.features.roles.common")
api project(":postssystem.features.common.server")
api project(":postssystem.features.auth.server")
}
}
}
}

View File

@@ -0,0 +1,29 @@
package dev.inmo.postssystem.features.roles.server
import dev.inmo.postssystem.features.roles.common.*
import dev.inmo.postssystem.features.users.common.User
import io.ktor.application.ApplicationCall
interface RolesChecker<T : UserRole> {
val key: String
suspend operator fun ApplicationCall.invoke(
usersRolesStorage: ReadUsersRolesStorage<T>,
user: User
): Boolean
companion object {
fun <T : UserRole> default(
key: String,
role: T
): RolesChecker<T> = object : RolesChecker<T> {
override val key: String
get() = key
override suspend fun ApplicationCall.invoke(
usersRolesStorage: ReadUsersRolesStorage<T>,
user: User
): Boolean = usersRolesStorage.contains(user.id, role)
}
}
}

View File

@@ -0,0 +1,39 @@
package dev.inmo.postssystem.features.roles.server
import dev.inmo.postssystem.features.roles.common.UserRole
import dev.inmo.postssystem.features.roles.common.UsersRolesStorage
import dev.inmo.postssystem.features.users.common.UserId
class UsersRolesAggregator(
private val otherStorages: List<UsersRolesStorageHolder<*>>
) : UsersRolesStorage<UserRole> {
private val otherStoragesByClass = otherStorages.associateBy { it.kclass }
override suspend fun getUsers(userRole: UserRole): List<UserId> {
return otherStoragesByClass[userRole::class] ?.getUsers(userRole) ?: emptyList()
}
override suspend fun getRoles(userId: UserId): List<UserRole> = otherStorages.flatMap { it.getRoles(userId) }
override suspend fun contains(userId: UserId, userRole: UserRole): Boolean {
return otherStoragesByClass[userRole::class] ?.contains(userId, userRole) ?: false
}
override suspend fun containsAny(userId: UserId, userRoles: List<UserRole>): Boolean {
return userRoles.any {
contains(userId, it)
}
}
override suspend fun include(
userId: UserId,
userRole: UserRole
): Boolean = otherStoragesByClass[userRole::class] ?.include(userId, userRole) ?: false
override suspend fun exclude(
userId: UserId,
userRole: UserRole
): Boolean {
return otherStoragesByClass[userRole::class] ?.exclude(userId, userRole) ?: false
}
}

View File

@@ -0,0 +1,40 @@
package dev.inmo.postssystem.features.roles.server
import dev.inmo.postssystem.features.auth.common.AuthToken
import dev.inmo.postssystem.features.auth.server.principal
import dev.inmo.postssystem.features.auth.server.tokens.AuthTokensService
import dev.inmo.postssystem.features.common.server.sessions.ApplicationAuthenticationConfigurator
import dev.inmo.postssystem.features.roles.common.UserRole
import dev.inmo.postssystem.features.roles.common.UsersRolesStorage
import io.ktor.application.call
import io.ktor.auth.Authentication
import io.ktor.auth.session
import io.ktor.http.HttpStatusCode
import io.ktor.response.respond
class UsersRolesAuthenticationConfigurator<T : UserRole>(
private val usersRolesStorage: UsersRolesStorage<T>,
private val authTokensService: AuthTokensService,
private val rolesCheckers: List<RolesChecker<T>>
) : ApplicationAuthenticationConfigurator.Element {
override fun Authentication.Configuration.invoke() {
rolesCheckers.forEach { checker ->
session<AuthToken>(checker.key) {
validate {
val result = authTokensService.getUserPrincipal(it)
if (result.isSuccess) {
val user = result.getOrThrow().principal()
if (checker.run { invoke(usersRolesStorage, user.user) }) {
user
} else {
null
}
} else {
null
}
}
challenge { call.respond(HttpStatusCode.Unauthorized) }
}
}
}
}

View File

@@ -0,0 +1,42 @@
package dev.inmo.postssystem.features.roles.server
import dev.inmo.postssystem.features.roles.common.UserRole
import dev.inmo.postssystem.features.roles.common.UsersRolesStorage
import dev.inmo.postssystem.features.users.common.UserId
import dev.inmo.micro_utils.common.*
import kotlin.reflect.KClass
data class UsersRolesStorageHolder<T : UserRole>(
val kclass: KClass<T>,
val storage: UsersRolesStorage<T>
) {
private suspend fun <R> doIfRelevant(
userRole: UserRole,
block: suspend (T) -> R
): Optional<R> = if (kclass.isInstance(userRole)) {
block(userRole as T).optional
} else {
Optional.absent()
}
suspend fun getUsers(userRole: UserRole): List<UserId>? = doIfRelevant(userRole) {
storage.getUsers(it)
}.dataOrNull()
suspend fun getRoles(userId: UserId): List<UserRole> = storage.getRoles(userId)
suspend fun contains(userId: UserId, userRole: UserRole): Boolean? = doIfRelevant(userRole) {
storage.contains(userId, it)
}.dataOrNull()
suspend fun include(
userId: UserId,
userRole: UserRole
): Boolean? = doIfRelevant(userRole) {
storage.include(userId, it)
}.dataOrNull()
suspend fun exclude(userId: UserId, userRole: UserRole): Boolean? = doIfRelevant(userRole) {
storage.exclude(userId, it)
}.dataOrNull()
}

View File

@@ -0,0 +1,73 @@
package dev.inmo.postssystem.features.roles.server
import dev.inmo.postssystem.features.roles.common.*
import dev.inmo.postssystem.features.users.common.UserId
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
import io.ktor.application.call
import io.ktor.auth.authenticate
import io.ktor.routing.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
class UsersRolesStorageReadServerRoutesConfigurator<T : UserRole>(
private val storage: ReadUsersRolesStorage<T>,
private val serializer: KSerializer<T>
) : ApplicationRoutingConfigurator.Element {
private val userRolesSerializer = ListSerializer(serializer)
override fun Route.invoke() {
authenticate {
route(usersRolesRootPathPart) {
get(usersRolesGetUsersPathPart) {
val userRole = call.decodeUrlQueryValueOrSendError(usersRolesUserRoleQueryParameterName, serializer)
?: return@get
call.unianswer(
UsersIdsSerializer,
storage.getUsers(userRole)
)
}
get(usersRolesGetRolesPathPart) {
val userId =
call.decodeUrlQueryValueOrSendError(usersRolesUserIdQueryParameterName, UserId.serializer())
?: return@get
call.unianswer(
userRolesSerializer,
storage.getRoles(userId)
)
}
get(usersRolesContainsPathPart) {
val userId = call.decodeUrlQueryValueOrSendError(
usersRolesUserIdQueryParameterName,
UserId.serializer()
) ?: return@get
val userRole = call.decodeUrlQueryValueOrSendError(
usersRolesUserRoleQueryParameterName,
serializer
) ?: return@get
call.unianswer(
Boolean.serializer(),
storage.contains(userId, userRole)
)
}
get(usersRolesContainsAnyPathPart) {
val userId = call.decodeUrlQueryValueOrSendError(
usersRolesUserIdQueryParameterName,
UserId.serializer()
) ?: return@get
val userRoles = call.decodeUrlQueryValueOrSendError(
usersRolesUserRoleQueryParameterName,
userRolesSerializer
) ?: return@get
call.unianswer(
Boolean.serializer(),
storage.containsAny(userId, userRoles)
)
}
}
}
}
}

View File

@@ -0,0 +1,51 @@
package dev.inmo.postssystem.features.roles.server
import dev.inmo.postssystem.features.roles.common.*
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
import io.ktor.application.call
import io.ktor.auth.authenticate
import io.ktor.routing.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
class UsersRolesStorageWriteServerRoutesConfigurator<T : UserRole>(
private val storage: WriteUsersRolesStorage<T>,
private val serializer: KSerializer<T>,
private val includeAuthKey: String,
private val excludeAuthKey: String = includeAuthKey
) : ApplicationRoutingConfigurator.Element {
override fun Route.invoke() {
route(usersRolesRootPathPart) {
val wrapperSerializer = UserRolesStorageIncludeExcludeWrapper.serializer(
serializer
)
authenticate(includeAuthKey) {
post(usersRolesIncludePathPart) {
val wrapper = call.uniload(wrapperSerializer)
call.unianswer(
Boolean.serializer(),
storage.include(
wrapper.userId,
wrapper.userRole
)
)
}
}
authenticate(excludeAuthKey) {
post(usersRolesExcludePathPart) {
val wrapper = call.uniload(wrapperSerializer)
call.unianswer(
Boolean.serializer(),
storage.exclude(
wrapper.userId,
wrapper.userRole
)
)
}
}
}
}
}