PermissionService fundamental

This commit is contained in:
Him188 2020-09-07 12:12:27 +08:00
parent 599c0b6d59
commit 8e3e328672
12 changed files with 310 additions and 96 deletions

View File

@ -161,10 +161,6 @@ public interface CommandSender : CoroutineScope, Permissible {
*/
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

@ -33,6 +33,9 @@ import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope
import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.HotDeploymentSupportPermissionService
import net.mamoe.mirai.console.permission.PermissionService
import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.PluginManager
import net.mamoe.mirai.console.plugin.center.PluginCenter
@ -80,7 +83,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
override fun createLogger(identity: String?): MiraiLogger = instance.createLogger(identity)
@OptIn(ConsoleExperimentalAPI::class)
@OptIn(ConsoleExperimentalAPI::class, ExperimentalPermission::class)
internal fun doStart() {
val buildDateFormatted =
buildDate.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
@ -102,6 +105,11 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
mainLogger.info { "Reloading configurations..." }
ConsoleDataScope.reloadAll()
PermissionService // init
if (PermissionService.INSTANCE is HotDeploymentSupportPermissionService<*>) {
}
BuiltInCommands.registerAll()
mainLogger.info { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
CommandManager

View File

@ -0,0 +1,101 @@
package net.mamoe.mirai.console.internal.permission
import net.mamoe.mirai.console.command.ConsoleCommandSender
import net.mamoe.mirai.console.command.UserCommandSender
import net.mamoe.mirai.console.data.AutoSavePluginConfig
import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault
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.permission.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
/**
* [PermissionServiceProvider]
*/
@Suppress("RedundantVisibilityModifier")
@ExperimentalPermission
internal abstract class AbstractPermissionService<TPermission : Permission, TPermissibleIdentifier> :
PermissionService<TPermission> {
protected abstract val Permissible.identifier: TPermissibleIdentifier
@JvmField
protected val permissions: MutableMap<PermissionIdentifier, TPermission> = ConcurrentHashMap()
@JvmField
protected val grantedPermissionMap: MutableMap<TPermissibleIdentifier, MutableList<PermissionIdentifier>> =
ConcurrentHashMap()
public override fun getGrantedPermissions(permissible: Permissible): Sequence<TPermission> =
grantedPermissionMap[permissible.identifier]?.asSequence()?.mapNotNull { get(it) }.orEmpty()
public override operator fun get(identifier: PermissionIdentifier): TPermission? = permissions[identifier]
public override fun testPermission(permissible: Permissible, permission: TPermission): Boolean =
permissible.getGrantedPermissions().any { it == permission }
}
/**
* [PermissionServiceProvider]
*/
@Suppress("RedundantVisibilityModifier")
@ExperimentalPermission
internal abstract class AbstractHotDeploymentSupportPermissionService<TPermission : Permission, TPermissibleIdentifier> :
PermissionService<TPermission>,
HotDeploymentSupportPermissionService<TPermission>, AutoSavePluginConfig() {
protected abstract val Permissible.identifier: TPermissibleIdentifier
@JvmField
protected val permissions: MutableMap<PermissionIdentifier, TPermission> = ConcurrentHashMap()
@JvmField
protected val grantedPermissionMap: Value<MutableMap<TPermissibleIdentifier, MutableList<PermissionIdentifier>>> =
value<MutableMap<TPermissibleIdentifier, MutableList<PermissionIdentifier>>>().withEmptyDefault()
public override fun getGrantedPermissions(permissible: Permissible): Sequence<TPermission> =
grantedPermissionMap.value[permissible.identifier]?.asSequence()?.mapNotNull { get(it) }.orEmpty()
public override operator fun get(identifier: PermissionIdentifier): TPermission? = permissions[identifier]
public override fun testPermission(permissible: Permissible, permission: TPermission): Boolean =
permissible.getGrantedPermissions().any { it == permission }
}
internal data class LiteralPermissibleIdentifier(
val context: String,
val value: String
)
@OptIn(ExperimentalPermission::class)
private object PermissionServiceImpl :
AbstractHotDeploymentSupportPermissionService<PermissionImpl, LiteralPermissibleIdentifier>() {
override fun register(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier?
): PermissionImpl = PermissionImpl(identifier, description, base)
override fun grant(permissible: Permissible, permission: PermissionImpl) {
grantedPermissionMap.value[permissible.identifier]!!.add(permission.identifier)
}
override fun deny(permissible: Permissible, permission: PermissionImpl) {
grantedPermissionMap.value[permissible.identifier]!!.remove(permission.identifier)
}
override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class
override val Permissible.identifier: LiteralPermissibleIdentifier
get() = LiteralPermissibleIdentifier(
"",
when (this) {
is ConsoleCommandSender -> "CONSOLE"
is UserCommandSender -> this.user.id.toString()
else -> ""
}
)
}

View File

@ -0,0 +1,58 @@
/*
* 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 java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
@ExperimentalPermission
public object AllGrantPermissionService : PermissionService<PermissionImpl> {
private val all = ConcurrentHashMap<PermissionIdentifier, PermissionImpl>()
override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class
override fun register(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier?
): PermissionImpl {
val new = PermissionImpl(identifier, description, base)
if (all.putIfAbsent(identifier, new) != null) {
throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}")
}
return new
}
override fun get(identifier: PermissionIdentifier): PermissionImpl? = all[identifier]
override fun getGrantedPermissions(permissible: Permissible): Sequence<PermissionImpl> = all.values.asSequence()
}
@ExperimentalPermission
public object AllDenyPermissionService : PermissionService<PermissionImpl> {
private val all = ConcurrentHashMap<PermissionIdentifier, PermissionImpl>()
override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class
override fun register(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier?
): PermissionImpl {
val new = PermissionImpl(identifier, description, base)
if (all.putIfAbsent(identifier, new) != null) {
throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}")
}
return new
}
override fun get(identifier: PermissionIdentifier): PermissionImpl? = all[identifier]
override fun getGrantedPermissions(permissible: Permissible): Sequence<PermissionImpl> = emptySequence()
}

View File

@ -0,0 +1,27 @@
/*
* 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 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)
}
@ExperimentalPermission
public open class DuplicatedRegistrationException : 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)
}

View File

@ -0,0 +1,17 @@
/*
* 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 HotDeploymentSupportPermissionService<P : Permission> : PermissionService<P> {
public fun grant(permissible: Permissible, permission: P)
public fun deny(permissible: Permissible, permission: P)
}

View File

@ -11,10 +11,12 @@
package net.mamoe.mirai.console.permission
/**
*
* 注意: 请不要自主实现 [Permissible]
*/
@ExperimentalPermission
public interface Permissible {
public val identifier: String
}
public interface Permissible
@ExperimentalPermission
public inline fun Permissible.hasPermission(permission: Permission): Boolean =

View File

@ -19,3 +19,13 @@ public interface Permission {
public val description: String
public val base: PermissionIdentifier?
}
/**
* [Permission] 的简单实现
*/
@ExperimentalPermission
public open class PermissionImpl(
override val identifier: PermissionIdentifier,
override val description: String,
override val base: PermissionIdentifier?
) : Permission

View File

@ -44,7 +44,7 @@ public abstract class PermissionGroup(
apply { this.permissionChecker = permissionChecker }
public fun build(property: KProperty<*>): Permission {
return PermissionService.register(
return PermissionService.INSTANCE.register(
identifierNamespace.permissionIdentifier(property.name),
description,
basePermission

View File

@ -9,15 +9,34 @@
package net.mamoe.mirai.console.permission
import kotlinx.serialization.Serializable
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.console.internal.data.map
@Serializable
@ExperimentalPermission
public data class PermissionIdentifier(
public val namespace: String,
public val id: String
)
) {
init {
require(!namespace.contains(':')) {
"':' is not allowed in namespace"
}
require(!id.contains(':')) {
"':' is not allowed in id"
}
}
@Serializer(forClass = PermissionIdentifier::class)
public object AsClassSerializer
public object AsStringSerializer : KSerializer<PermissionIdentifier> by String.serializer().map(
serializer = { it.namespace + ":" + it.id },
deserializer = { it.split(':').let { (namespace, id) -> PermissionIdentifier(namespace, id) } }
)
}
@ExperimentalPermission
public interface PermissionIdentifierNamespace {

View File

@ -11,17 +11,19 @@
package net.mamoe.mirai.console.permission
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 java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
/**
* [PermissionServiceProvider]
*/
@ExperimentalPermission
public interface PermissionService<P : Permission> {
@ExperimentalPermission
public val permissionType: KClass<P>
@Throws(DuplicatedRegistrationException::class)
public fun register(
identifier: PermissionIdentifier,
description: String,
@ -30,99 +32,73 @@ public interface PermissionService<P : Permission> {
public operator fun get(identifier: PermissionIdentifier): P?
public fun getGrantedPermissions(permissible: Permissible): List<PermissionIdentifier>
public fun getGrantedPermissions(permissible: Permissible): Sequence<P>
public fun testPermission(permissible: Permissible, permission: P): Boolean =
permissible.getGrantedPermissions().any { it == permission.identifier }
permissible.getGrantedPermissions().any { it == permission }
public companion object {
private val builtIn: PermissionService<out Permission> get() = AllGrantPermissionService
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.getExtensions().singleOrNull()?.extension?.instance
?: builtIn // TODO: 2020/9/4 ask for further choice
as PermissionService<Permission>
@get:JvmName("getInstance")
@JvmStatic
public val INSTANCE: PermissionService<out Permission> by lazy {
PermissionServiceProvider.getExtensions().singleOrNull()?.extension?.instance ?: builtIn
// TODO: 2020/9/4 ExtensionSelector
}
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)
}
}
@ExperimentalPermission
public interface HotDeploymentSupportPermissionService<P : Permission> : PermissionService<P> {
public fun grant(permissible: Permissible, permission: P)
public fun deny(permissible: Permissible, permission: P)
}
public abstract class AbstractPermissionService<P : Permission> : PermissionService<P> {
protected val all: MutableMap<PermissionIdentifier, P> = ConcurrentHashMap<PermissionIdentifier, P>()
@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
protected abstract fun createPermission(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier?
): P
override fun register(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier?
): PermissionImpl = PermissionImpl(identifier, description, base)
): P {
val new = createPermission(identifier, description, base)
if (all.putIfAbsent(identifier, new) != null) {
throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}")
}
return new
}
override fun get(identifier: PermissionIdentifier): P? = all[identifier]
override fun getGrantedPermissions(permissible: Permissible): Sequence<P> = all.values.asSequence()
}
@ExperimentalPermission
public inline fun Permissible.getGrantedPermissions(): Sequence<Permission> =
PermissionService.INSTANCE.run {
getGrantedPermissions(this@getGrantedPermissions)
}
@ExperimentalPermission
public inline fun Permission.testPermission(permissible: Permissible): Boolean =
PermissionService.INSTANCE.run {
require(permissionType.isInstance(this@testPermission)) {
"Custom-constructed Permission instance is not allowed. " +
"Please obtain Permission from PermissionService.INSTANCE.register or PermissionService.INSTANCE.get"
}
@Suppress("UNCHECKED_CAST")
this as PermissionService<Permission>
testPermission(permissible, this@testPermission)
}
@ExperimentalPermission
public inline fun PermissionIdentifier.testPermission(permissible: Permissible): Boolean {
val p = PermissionService.INSTANCE[this] ?: return false
return p.testPermission(permissible)
}

View File

@ -18,7 +18,7 @@
object Versions {
const val core = "1.2.2"
const val console = "1.0-M4-dev-4"
const val console = "1.0-M4-dev-5"
const val consoleGraphical = "0.0.7"
const val consoleTerminal = "0.1.0"
const val consolePure = console