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"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":postssystem.features.roles.common")
api project(":postssystem.features.common.client")
}
}
}
}

View File

@@ -0,0 +1,17 @@
package dev.inmo.postssystem.features.roles.client
import dev.inmo.postssystem.features.roles.common.*
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import kotlinx.serialization.KSerializer
class ClientUsersRolesStorage<T : UserRole>(
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester,
private val serializer: KSerializer<T>
) : UsersRolesStorage<T>,
ReadUsersRolesStorage<T> by ReadClientUsersRolesStorage(
baseUrl, unifiedRequester, serializer
),
WriteUsersRolesStorage<T> by WriteClientUsersRolesStorage(
baseUrl, unifiedRequester, serializer
)

View File

@@ -0,0 +1,70 @@
package dev.inmo.postssystem.features.roles.client
import dev.inmo.postssystem.features.roles.common.*
import dev.inmo.postssystem.features.users.common.UserId
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
class ReadClientUsersRolesStorage<T : UserRole>(
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester,
private val serializer: KSerializer<T>
) : ReadUsersRolesStorage<T> {
private val userRolesSerializer = ListSerializer(serializer)
private val userRolesFullUrl = buildStandardUrl(
baseUrl,
usersRolesRootPathPart
)
override suspend fun getUsers(
userRole: T
): List<UserId> = unifiedRequester.uniget(
buildStandardUrl(
userRolesFullUrl,
usersRolesGetUsersPathPart,
usersRolesUserRoleQueryParameterName to unifiedRequester.encodeUrlQueryValue(serializer, userRole)
),
UsersIdsSerializer
)
override suspend fun getRoles(
userId: UserId
): List<T> = unifiedRequester.uniget(
buildStandardUrl(
userRolesFullUrl,
usersRolesGetRolesPathPart,
usersRolesUserIdQueryParameterName to unifiedRequester.encodeUrlQueryValue(UserId.serializer(), userId)
),
userRolesSerializer
)
override suspend fun contains(
userId: UserId,
userRole: T
): Boolean = unifiedRequester.uniget(
buildStandardUrl(
userRolesFullUrl,
usersRolesContainsPathPart,
usersRolesUserIdQueryParameterName to unifiedRequester.encodeUrlQueryValue(UserId.serializer(), userId),
usersRolesUserRoleQueryParameterName to unifiedRequester.encodeUrlQueryValue(serializer, userRole)
),
Boolean.serializer()
)
override suspend fun containsAny(
userId: UserId,
userRoles: List<T>
): Boolean = unifiedRequester.uniget(
buildStandardUrl(
userRolesFullUrl,
usersRolesContainsAnyPathPart,
usersRolesUserIdQueryParameterName to unifiedRequester.encodeUrlQueryValue(UserId.serializer(), userId),
usersRolesUserRoleQueryParameterName to unifiedRequester.encodeUrlQueryValue(userRolesSerializer, userRoles)
),
Boolean.serializer()
)
}

View File

@@ -0,0 +1,52 @@
package dev.inmo.postssystem.features.roles.client
import dev.inmo.postssystem.features.roles.common.*
import dev.inmo.postssystem.features.users.common.UserId
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
class WriteClientUsersRolesStorage<T : UserRole>(
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester,
private val serializer: KSerializer<T>
) : WriteUsersRolesStorage<T> {
private val wrapperSerializer = UserRolesStorageIncludeExcludeWrapper.serializer(
serializer
)
private val userRolesFullUrl = buildStandardUrl(
baseUrl,
usersRolesRootPathPart
)
private val includeFullUrl = buildStandardUrl(
userRolesFullUrl,
usersRolesIncludePathPart
)
private val excludeFullUrl = buildStandardUrl(
userRolesFullUrl,
usersRolesExcludePathPart
)
override suspend fun include(
userId: UserId,
userRole: T
): Boolean = unifiedRequester.unipost(
includeFullUrl,
wrapperSerializer to UserRolesStorageIncludeExcludeWrapper(
userId, userRole
),
Boolean.serializer()
)
override suspend fun exclude(
userId: UserId,
userRole: T
): Boolean = unifiedRequester.unipost(
excludeFullUrl,
wrapperSerializer to UserRolesStorageIncludeExcludeWrapper(
userId, userRole
),
Boolean.serializer()
)
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.postssystem.features.roles.client"/>

View File

@@ -0,0 +1,18 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":postssystem.features.common.common")
api project(":postssystem.features.users.common")
}
}
}
}

View File

@@ -0,0 +1,26 @@
package dev.inmo.postssystem.features.roles.common
import dev.inmo.postssystem.features.users.common.UserId
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
const val usersRolesRootPathPart = "roles"
val UsersIdsSerializer = ListSerializer(UserId.serializer())
const val usersRolesUserRoleQueryParameterName = "userRole"
const val usersRolesUserIdQueryParameterName = "userId"
const val usersRolesGetUsersPathPart = "getUsersByRole"
const val usersRolesGetRolesPathPart = "getUserRoles"
const val usersRolesContainsPathPart = "contains"
const val usersRolesContainsAnyPathPart = "containsAny"
const val usersRolesIncludePathPart = "include"
const val usersRolesExcludePathPart = "exclude"
@Serializable
data class UserRolesStorageIncludeExcludeWrapper<T : UserRole>(
val userId: UserId,
val userRole: T
)

View File

@@ -0,0 +1,81 @@
package dev.inmo.postssystem.features.roles.common
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
@Serializable(UserRoleSerializer::class)
interface UserRole { // temporarily made as class while interfaces are bugged
companion object {
fun serializer() = UserRoleSerializer
}
}
@Serializable
data class UnknownUserRole(val originalJson: JsonElement) : UserRole
@Serializer(UserRole::class)
object UserRoleSerializer : KSerializer<UserRole> {
private val userRoleFormat = Json { ignoreUnknownKeys = true }
private const val keyField = "key"
private const val valueField = "value"
private val serializers = mutableMapOf<String, KSerializer<out UserRole>>()
override val descriptor: SerialDescriptor = String.serializer().descriptor
@InternalSerializationApi
override fun deserialize(decoder: Decoder): UserRole {
return if (decoder is JsonDecoder) {
val originalJson = decoder.decodeJsonElement().jsonObject
val type = originalJson[keyField]?.jsonPrimitive ?.content
return if (type == null || !serializers.containsKey(type)) {
UnknownUserRole(originalJson)
} else {
userRoleFormat.decodeFromJsonElement(
serializers.getValue(type),
originalJson[valueField] ?: buildJsonObject { }
)
}
} else {
val encoded = decoder.decodeString()
userRoleFormat.decodeFromString(this, encoded)
}
}
@InternalSerializationApi
private fun <T : UserRole> T.toJson(): JsonElement {
return userRoleFormat.encodeToJsonElement(this::class.serializer() as KSerializer<T>, this)
}
@InternalSerializationApi
override fun serialize(encoder: Encoder, value: UserRole) {
if (encoder is JsonEncoder) {
if (value is UnknownUserRole) {
encoder.encodeJsonElement(value.originalJson)
} else {
val valueSerializer = value::class.serializer()
val type = serializers.keys.first { serializers[it] == valueSerializer }
encoder.encodeJsonElement(
buildJsonObject {
put(keyField, type)
put(valueField, value.toJson())
}
)
}
} else {
encoder.encodeString(
userRoleFormat.encodeToString(this, value)
)
}
}
fun <T : UserRole> includeSerializer(
type: String,
kSerializer: KSerializer<T>
) { serializers[type] = kSerializer }
fun excludeSerializer(type: String) {
serializers.remove(type)
}
}

View File

@@ -0,0 +1,5 @@
package dev.inmo.postssystem.features.roles.common
import kotlinx.serialization.builtins.ListSerializer
val UserRolesSerializer = ListSerializer(UserRole.serializer())

View File

@@ -0,0 +1,16 @@
package dev.inmo.postssystem.features.roles.common
import dev.inmo.postssystem.features.users.common.UserId
interface ReadUsersRolesStorage<T : UserRole> {
suspend fun getUsers(userRole: T): List<UserId>
suspend fun getRoles(userId: UserId): List<T>
suspend fun contains(userId: UserId, userRole: T): Boolean
suspend fun containsAny(userId: UserId, userRoles: List<T>): Boolean
}
interface WriteUsersRolesStorage<T : UserRole> {
suspend fun include(userId: UserId, userRole: T): Boolean
suspend fun exclude(userId: UserId, userRole: T): Boolean
}
interface UsersRolesStorage<T : UserRole> : ReadUsersRolesStorage<T>, WriteUsersRolesStorage<T>

View File

@@ -0,0 +1,5 @@
package dev.inmo.postssystem.features.roles.common.keyvalue
import dev.inmo.micro_utils.repos.KeyValuesRepo
typealias KeyValuesUsersRolesOriginalRepo = KeyValuesRepo<Long, String>

View File

@@ -0,0 +1,13 @@
package dev.inmo.postssystem.features.roles.common.keyvalue
import dev.inmo.postssystem.features.roles.common.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.StringFormat
open class KeyValueUsersRolesStorage<T : UserRole>(
private val keyValuesRepo: KeyValuesUsersRolesOriginalRepo,
private val serializer: KSerializer<T>,
private val format: StringFormat = ReadKeyValueUsersRolesStorage.defaultJson
) : UsersRolesStorage<T>,
ReadUsersRolesStorage<T> by ReadKeyValueUsersRolesStorage(keyValuesRepo, serializer, format),
WriteUsersRolesStorage<T> by WriteKeyValueUsersRolesStorage(keyValuesRepo, serializer, format)

View File

@@ -0,0 +1,59 @@
package dev.inmo.postssystem.features.roles.common.keyvalue
import dev.inmo.postssystem.features.common.common.default
import dev.inmo.postssystem.features.roles.common.ReadUsersRolesStorage
import dev.inmo.postssystem.features.roles.common.UserRole
import dev.inmo.postssystem.features.users.common.UserId
import dev.inmo.micro_utils.pagination.changeResults
import dev.inmo.micro_utils.pagination.utils.getAllByWithNextPaging
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import kotlinx.serialization.KSerializer
import kotlinx.serialization.StringFormat
import kotlinx.serialization.json.Json
open class ReadKeyValueUsersRolesStorage<T : UserRole>(
private val keyValuesRepo: ReadKeyValuesRepo<Long, String>,
private val serializer: KSerializer<T>,
private val format: StringFormat = defaultJson
) : ReadUsersRolesStorage<T> {
override suspend fun getUsers(userRole: T): List<UserId> {
val serialized = format.encodeToString(serializer, userRole)
return keyValuesRepo.getAllByWithNextPaging {
keys(serialized, it).let { paginationResult ->
paginationResult.changeResults(
paginationResult.results.map { UserId(it) }
)
}
}
}
override suspend fun getRoles(userId: UserId): List<T> {
return keyValuesRepo.getAllByWithNextPaging {
get(userId.long, it).let { paginationResult ->
paginationResult.changeResults(
paginationResult.results.map { serialized ->
format.decodeFromString(serializer, serialized)
}
)
}
}
}
override suspend fun contains(userId: UserId, userRole: T): Boolean {
val serialized = format.encodeToString(serializer, userRole)
return keyValuesRepo.contains(userId.long, serialized)
}
override suspend fun containsAny(userId: UserId, userRoles: List<T>): Boolean {
return userRoles.any {
contains(userId, it)
}
}
companion object {
internal val defaultJson = Json.default
}
}

View File

@@ -0,0 +1,34 @@
package dev.inmo.postssystem.features.roles.common.keyvalue
import dev.inmo.postssystem.features.roles.common.UserRole
import dev.inmo.postssystem.features.roles.common.WriteUsersRolesStorage
import dev.inmo.postssystem.features.users.common.UserId
import dev.inmo.micro_utils.repos.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.StringFormat
open class WriteKeyValueUsersRolesStorage<T : UserRole>(
private val keyValuesRepo: WriteKeyValuesRepo<Long, String>,
private val serializer: KSerializer<T>,
private val format: StringFormat = ReadKeyValueUsersRolesStorage.defaultJson
) : WriteUsersRolesStorage<T> {
override suspend fun include(userId: UserId, userRole: T): Boolean {
return runCatching {
keyValuesRepo.add(
userId.long,
format.encodeToString(serializer, userRole)
)
true
}.getOrElse { false }
}
override suspend fun exclude(userId: UserId, userRole: T): Boolean {
return runCatching {
keyValuesRepo.remove(
userId.long,
format.encodeToString(serializer, userRole)
)
true
}.getOrElse { false }
}
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.postssystem.features.roles.common"/>

View File

@@ -0,0 +1,19 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":postssystem.features.roles.manager.common")
api project(":postssystem.features.common.client")
api project(":postssystem.features.roles.client")
}
}
}
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.postssystem.features.roles.manager.client"/>

View File

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

View File

@@ -0,0 +1,32 @@
package dev.inmo.postssystem.features.roles.manager.common
import dev.inmo.postssystem.features.roles.common.UserRole
import dev.inmo.postssystem.features.roles.common.UserRoleSerializer
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
import kotlinx.serialization.Serializable
@Serializable(RolesManagerRoleSerializer::class)
interface RolesManagerRole : UserRole {
companion object {
fun serializer() = RolesManagerRoleSerializer
}
}
@Serializable
object GeneralRolesManagerRole : RolesManagerRole {
override fun toString(): String = "GeneralRolesManagerRole"
}
private const val KEY = "roles_manager"
object RolesManagerRoleSerializer : TypedSerializer<RolesManagerRole>(
RolesManagerRole::class,
mapOf(
"${KEY}_general" to GeneralRolesManagerRole.serializer()
)
) {
init {
UserRoleSerializer.includeSerializer(KEY, RolesManagerRoleSerializer)
serializers.forEach { (k, v) -> UserRoleSerializer.includeSerializer(k, v) }
}
}

View File

@@ -0,0 +1,16 @@
package dev.inmo.postssystem.features.roles.manager.common
import dev.inmo.postssystem.features.common.common.default
import dev.inmo.postssystem.features.roles.common.UsersRolesStorage
import dev.inmo.postssystem.features.roles.common.keyvalue.*
import kotlinx.serialization.StringFormat
import kotlinx.serialization.json.Json
class RolesManagerRoleStorage(
keyValuesRepo: KeyValuesUsersRolesOriginalRepo,
format: StringFormat = Json.default
) : UsersRolesStorage<RolesManagerRole>, KeyValueUsersRolesStorage<RolesManagerRole>(
keyValuesRepo,
RolesManagerRole.serializer(),
format
)

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.postssystem.features.roles.manager.common"/>

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.manager.common")
api project(":postssystem.features.common.server")
api project(":postssystem.features.roles.server")
}
}
}
}

View File

@@ -0,0 +1,18 @@
package dev.inmo.postssystem.features.roles.manager.server
import dev.inmo.postssystem.features.roles.common.ReadUsersRolesStorage
import dev.inmo.postssystem.features.roles.common.UserRole
import dev.inmo.postssystem.features.roles.manager.common.GeneralRolesManagerRole
import dev.inmo.postssystem.features.roles.server.RolesChecker
import dev.inmo.postssystem.features.users.common.User
import io.ktor.application.ApplicationCall
object RolesManagerRolesChecker : RolesChecker<UserRole> {
override val key: String
get() = "RolesManagerRolesChecker"
override suspend fun ApplicationCall.invoke(
usersRolesStorage: ReadUsersRolesStorage<UserRole>,
user: User
): Boolean = usersRolesStorage.contains(user.id, GeneralRolesManagerRole)
}

View File

@@ -0,0 +1,15 @@
package dev.inmo.postssystem.features.roles.manager.server
import dev.inmo.postssystem.features.roles.common.UsersRolesStorage
import dev.inmo.postssystem.features.roles.manager.common.RolesManagerRole
import dev.inmo.postssystem.features.roles.manager.common.RolesManagerRoleSerializer
import dev.inmo.postssystem.features.roles.server.UsersRolesStorageWriteServerRoutesConfigurator
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
class RolesManagerUsersRolesStorageServerRoutesConfigurator(
storage: UsersRolesStorage<RolesManagerRole>
) : ApplicationRoutingConfigurator.Element by UsersRolesStorageWriteServerRoutesConfigurator(
storage,
RolesManagerRoleSerializer,
RolesManagerRolesChecker.key
)

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
)
)
}
}
}
}
}