PermissionService fundamental

This commit is contained in:
Him188 2020-09-06 14:24:50 +08:00
parent 77176f5242
commit 78d5f56c49
11 changed files with 319 additions and 15 deletions

View File

@ -32,6 +32,8 @@ import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
import net.mamoe.mirai.console.internal.data.castOrNull
import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permissible
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScopeContext
@ -132,7 +134,8 @@ import kotlin.internal.LowPriorityInOverloadResolution
* @see toCommandSender
* @see asCommandSender
*/
public interface CommandSender : CoroutineScope {
@OptIn(ExperimentalPermission::class)
public interface CommandSender : CoroutineScope, Permissible {
/**
* 与这个 [CommandSender] 相关的 [Bot].
* 当通过控制台执行时为 `null`.
@ -158,6 +161,10 @@ public interface CommandSender : CoroutineScope {
*/
public val name: String
@ExperimentalPermission
override val identifier: String
get() = user?.id?.toString() ?: bot?.id?.toString() ?: error("Internal error: bot user and bot are null")
/**
* 立刻发送一条消息.
*

View File

@ -2,11 +2,13 @@ package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.extension.AbstractExtensionPoint
import net.mamoe.mirai.console.extension.SingletonExtension
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionService
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
@ExperimentalPermission
@ConsoleExperimentalAPI
public interface PermissionServiceProvider : SingletonExtension<PermissionService> {
public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> {
public companion object ExtensionPoint :
AbstractExtensionPoint<PermissionServiceProvider>(PermissionServiceProvider::class)
}

View File

@ -16,7 +16,7 @@ import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.data.runCatchingLog
import net.mamoe.mirai.console.internal.data.mkdir
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Identifier
import net.mamoe.mirai.console.permission.PermissionIdentifier
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.PluginManager
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader
@ -47,8 +47,8 @@ internal abstract class JvmPluginInternal(
resourceContainerDelegate.getResourceAsStream(path)
@OptIn(ExperimentalPermission::class)
override fun allocate(identifierString: String): Identifier {
return Identifier(description.name)
override fun permissionIdentifier(identifierString: String): PermissionIdentifier {
return PermissionIdentifier(description.name, identifierString)
}
// region JvmPlugin

View File

@ -0,0 +1,23 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.permission
import kotlin.annotation.AnnotationTarget.*
/**
* 标记一个实验性的权限系统 API
*/
@Retention(AnnotationRetention.BINARY)
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
@MustBeDocumented
public annotation class ExperimentalPermission(
val message: String = ""
)

View File

@ -0,0 +1,25 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE", "unused")
package net.mamoe.mirai.console.permission
@ExperimentalPermission
public interface Permissible {
public val identifier: String
}
@ExperimentalPermission
public inline fun Permissible.hasPermission(permission: Permission): Boolean =
PermissionService.run { permission.testPermission(this@hasPermission) }
@ExperimentalPermission
public inline fun Permissible.hasPermission(permission: PermissionIdentifier): Boolean =
PermissionService.run { permission.testPermission(this@hasPermission) }

View File

@ -0,0 +1,21 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.permission
/**
* 一个权限节点
*/
@ExperimentalPermission
public interface Permission {
public val identifier: PermissionIdentifier
public val description: String
public val base: PermissionIdentifier?
}

View File

@ -0,0 +1,19 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("unused")
package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.command.CommandSender
@ExperimentalPermission
public fun interface PermissionChecker {
public fun CommandSender.testPermission(): Boolean
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.command.CommandSender
import kotlin.reflect.KProperty
@ExperimentalPermission
public abstract class PermissionGroup(
private val identifierNamespace: PermissionIdentifierNamespace,
) {
@ExperimentalPermission
public inner class PermissionBuilder {
@JvmField
internal var description: String = "<no description given>"
@JvmField
internal var basePermission: PermissionIdentifier? = null
@JvmField
internal var permissionChecker: PermissionChecker? = null
public fun description(description: String): PermissionBuilder = apply { this.description = description }
public fun dependsOn(basePermission: PermissionIdentifier?): PermissionBuilder =
apply { this.basePermission = basePermission }
public fun dependsOn(basePermission: Permission?): PermissionBuilder =
apply { this.basePermission = basePermission?.identifier }
public fun basePermission(basePermission: PermissionIdentifier?): PermissionBuilder =
apply { this.basePermission = basePermission }
public fun basePermission(basePermission: Permission?): PermissionBuilder =
apply { this.basePermission = basePermission?.identifier }
public fun defaults(permissionChecker: PermissionChecker?): PermissionBuilder =
apply { this.permissionChecker = permissionChecker }
public fun build(property: KProperty<*>): Permission {
return PermissionService.register(
identifierNamespace.permissionIdentifier(property.name),
description,
basePermission
)
}
}
public infix fun String.dependsOn(permission: Permission): PermissionBuilder {
return PermissionBuilder().apply { description(this@dependsOn);dependsOn(permission) }
}
public infix fun PermissionBuilder.defaults(permission: PermissionChecker): PermissionBuilder {
return apply { defaults(permission) }
}
public infix fun PermissionBuilder.defaults(permission: CommandSender.() -> Boolean): PermissionBuilder {
return apply { defaults(permission) }
}
public infix fun String.defaults(permission: PermissionChecker): PermissionBuilder {
return PermissionBuilder().apply { defaults(permission) }
}
public operator fun String.invoke(block: PermissionBuilder.() -> Unit): PermissionBuilder {
return PermissionBuilder().apply(block)
}
public operator fun String.provideDelegate(thisRef: PermissionGroup, property: KProperty<*>): Permission =
PermissionBuilder().apply { description(this@provideDelegate) }.build(property)
public operator fun Permission.getValue(thisRef: PermissionGroup, property: KProperty<*>): Permission = this
public operator fun PermissionBuilder.getValue(thisRef: PermissionGroup, property: KProperty<*>): Permission =
this.build(property)
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.permission
import kotlinx.serialization.Serializable
@Serializable
@ExperimentalPermission
public data class PermissionIdentifier(
public val namespace: String,
public val id: String
)
@ExperimentalPermission
public interface PermissionIdentifierNamespace {
@ExperimentalPermission
public fun permissionIdentifier(identifierString: String): PermissionIdentifier
}

View File

@ -7,28 +7,122 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE", "unused")
package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.extension.useExtensions
import net.mamoe.mirai.console.data.AutoSavePluginConfig
import net.mamoe.mirai.console.data.Value
import net.mamoe.mirai.console.data.value
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import java.util.concurrent.ConcurrentLinkedQueue
/**
* [PermissionServiceProvider]
*/
@ConsoleExperimentalAPI
public interface PermissionService {
@ExperimentalPermission
public interface PermissionService<P : Permission> {
public fun register(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier? = null
): P
public operator fun get(identifier: PermissionIdentifier): P?
public companion object : PermissionService {
public fun getGrantedPermissions(permissible: Permissible): List<PermissionIdentifier>
public fun testPermission(permissible: Permissible, permission: P): Boolean =
permissible.getGrantedPermissions().any { it == permission.identifier }
public companion object INSTANCE : PermissionService<Permission> {
private val builtIn: PermissionService<out Permission> get() = TODO("PS IMPL")
@Suppress("UNCHECKED_CAST")
private val instance by lazy {
PermissionServiceProvider.useExtensions { }
PermissionServiceProvider.getExtensions().singleOrNull()?.extension?.instance
?: builtIn // TODO: 2020/9/4 ask for further choice
as PermissionService<Permission>
}
override fun register(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier?
): Permission = instance.register(identifier, description, base)
override fun get(identifier: PermissionIdentifier): Permission? = instance[identifier]
override fun getGrantedPermissions(permissible: Permissible): List<PermissionIdentifier> =
instance.getGrantedPermissions(permissible)
}
}
public interface proprietary
@ExperimentalPermission
public interface HotDeploymentSupportPermissionService<P : Permission> : PermissionService<P> {
public fun grant(permissible: Permissible, permission: P)
public fun deny(permissible: Permissible, permission: P)
}
@ConsoleExperimentalAPI
public interface Permission
@ExperimentalPermission
public open class HotDeploymentNotSupportedException : Exception {
public constructor() : super()
public constructor(message: String?) : super(message)
public constructor(message: String?, cause: Throwable?) : super(message, cause)
public constructor(cause: Throwable?) : super(cause)
}
/**
* [PermissionServiceProvider]
*/
@ExperimentalPermission
public abstract class AbstractPermissionService<P : Permission> : AutoSavePluginConfig(), PermissionService<P> {
@JvmField
protected val permissions: ConcurrentLinkedQueue<P> = ConcurrentLinkedQueue()
@JvmField
protected val grantedPermissionMap: Value<MutableMap<String, MutableList<PermissionIdentifier>>> = value()
public override fun getGrantedPermissions(permissible: Permissible): List<PermissionIdentifier> =
grantedPermissionMap.value[permissible.identifier].orEmpty()
public override operator fun get(identifier: PermissionIdentifier): P? =
permissions.find { it.identifier == identifier }
public override fun testPermission(permissible: Permissible, permission: P): Boolean =
permissible.getGrantedPermissions().any { it == permission.identifier }
}
@ExperimentalPermission
public inline fun Permissible.getGrantedPermissions(): List<PermissionIdentifier> =
PermissionService.run { this.getGrantedPermissions(this@getGrantedPermissions) }
@ExperimentalPermission
public inline fun Permission.testPermission(permissible: Permissible): Boolean =
PermissionService.run { testPermission(permissible, this@testPermission) }
@ExperimentalPermission
public inline fun PermissionIdentifier.testPermission(permissible: Permissible): Boolean {
val p = PermissionService[this] ?: return false
return p.testPermission(permissible)
}
@OptIn(ExperimentalPermission::class)
private class PermissionServiceImpl : AbstractPermissionService<PermissionServiceImpl.PermissionImpl>() {
private val instances: ConcurrentLinkedQueue<PermissionImpl> = ConcurrentLinkedQueue()
private class PermissionImpl(
override val identifier: PermissionIdentifier,
override val description: String,
override val base: PermissionIdentifier?
) : Permission
override fun register(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier?
): PermissionImpl = PermissionImpl(identifier, description, base)
}

View File

@ -21,6 +21,8 @@ import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
import net.mamoe.mirai.console.data.PluginConfig
import net.mamoe.mirai.console.data.PluginData
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionIdentifierNamespace
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.PluginFileExtensions
import net.mamoe.mirai.console.plugin.ResourceContainer
@ -41,8 +43,9 @@ import net.mamoe.mirai.utils.MiraiLogger
* @see JvmPlugin 支持文件系统扩展
* @see ResourceContainer 支持资源获取 ( Jar 中的资源文件)
*/
@OptIn(ExperimentalPermission::class)
public interface JvmPlugin : Plugin, CoroutineScope,
PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder {
PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder, PermissionIdentifierNamespace {
/** 日志 */
public val logger: MiraiLogger