Merge pull request #156 from mamoe/permission

PermissionService & Permissions
This commit is contained in:
Him188 2020-09-08 20:16:12 +08:00 committed by GitHub
commit 102f359a11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 412 additions and 425 deletions

View File

@ -6,7 +6,6 @@ import java.time.Instant
plugins { plugins {
kotlin("jvm") kotlin("jvm")
kotlin("plugin.serialization") kotlin("plugin.serialization")
kotlin("kapt")
id("java") id("java")
`maven-publish` `maven-publish`
id("com.jfrog.bintray") id("com.jfrog.bintray")
@ -81,9 +80,9 @@ dependencies {
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.2.0") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.2.0")
val autoService = "1.0-rc7" // val autoService = "1.0-rc7"
kapt("com.google.auto.service", "auto-service", autoService) // kapt("com.google.auto.service", "auto-service", autoService)
compileOnly("com.google.auto.service", "auto-service-annotations", autoService) // compileOnly("com.google.auto.service", "auto-service-annotations", autoService)
} }
ext.apply { ext.apply {

View File

@ -62,17 +62,14 @@ public object BuiltInCommands {
public object Managers : CompositeCommand( public object Managers : CompositeCommand(
ConsoleCommandOwner, "managers", ConsoleCommandOwner, "managers",
description = "Manage the managers for each bot", description = "Manage the managers for each bot"
permission = CommandPermission.Console or CommandPermission.Manager
), BuiltInCommand { ), BuiltInCommand {
@Permission(CommandPermission.Console::class)
@SubCommand @SubCommand
public suspend fun CommandSender.add(target: User) { public suspend fun CommandSender.add(target: User) {
target.bot.addManager(target.id) target.bot.addManager(target.id)
sendMessage("已成功添加 ${target.render()}${target.bot.render()} 的管理员") sendMessage("已成功添加 ${target.render()}${target.bot.render()} 的管理员")
} }
@Permission(CommandPermission.Console::class)
@SubCommand @SubCommand
public suspend fun CommandSender.remove(target: User) { public suspend fun CommandSender.remove(target: User) {
target.bot.removeManager(target.id) target.bot.removeManager(target.id)

View File

@ -15,7 +15,11 @@ import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
import net.mamoe.mirai.console.command.java.JCommand import net.mamoe.mirai.console.command.java.JCommand
import net.mamoe.mirai.console.internal.command.createCommandPermission
import net.mamoe.mirai.console.internal.command.isValidSubName import net.mamoe.mirai.console.internal.command.isValidSubName
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.SingleMessage
@ -51,7 +55,7 @@ public interface Command {
/** /**
* 指令权限 * 指令权限
*/ */
public val permission: CommandPermission public val permission: Permission
/** /**
* `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 * `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选
@ -95,13 +99,13 @@ public suspend inline fun Command.onCommand(sender: CommandSender, args: Message
* @see CompositeCommand * @see CompositeCommand
* @see RawCommand * @see RawCommand
*/ */
public abstract class AbstractCommand @JvmOverloads constructor( public abstract class AbstractCommand
@JvmOverloads constructor(
/** 指令拥有者. */ /** 指令拥有者. */
public override val owner: CommandOwner, public override val owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "<no description available>", description: String = "<no description available>",
/** 指令权限 */ basePermission: PermissionId? = null,
public override val permission: CommandPermission = CommandPermission.Default,
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
public override val prefixOptional: Boolean = false public override val prefixOptional: Boolean = false
) : Command { ) : Command {
@ -111,4 +115,6 @@ public abstract class AbstractCommand @JvmOverloads constructor(
list.firstOrNull { !it.isValidSubName() }?.let { error("Invalid name: $it") } list.firstOrNull { !it.isValidSubName() }?.let { error("Invalid name: $it") }
}.toTypedArray() }.toTypedArray()
@OptIn(ExperimentalPermission::class)
public override val permission: Permission by lazy { createCommandPermission(basePermission) }
} }

View File

@ -10,6 +10,9 @@
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.console.permission.PermissionIdNamespace
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
/** /**
@ -20,9 +23,12 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
* *
* @see JvmPlugin 是一个 [CommandOwner] * @see JvmPlugin 是一个 [CommandOwner]
*/ */
public interface CommandOwner public interface CommandOwner : PermissionIdNamespace
/** /**
* 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner]. * 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner].
*/ */
internal object ConsoleCommandOwner : CommandOwner internal object ConsoleCommandOwner : CommandOwner {
@ExperimentalPermission
override fun permissionId(id: String): PermissionId = PermissionId("console", id)
}

View File

@ -1,143 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("unused", "NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate")
package net.mamoe.mirai.console.command
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.internal.command.AndCommandPermissionImpl
import net.mamoe.mirai.console.internal.command.OrCommandPermissionImpl
import net.mamoe.mirai.console.util.BotManager.INSTANCE.isManager
import net.mamoe.mirai.contact.isAdministrator
import net.mamoe.mirai.contact.isOperator
import net.mamoe.mirai.contact.isOwner
/**
* 指令权限.
*
* [CommandManager.executeCommand] 时将会检查权限.
*
* @see Command.permission 从指令获取权限
*/
public fun interface CommandPermission {
/**
* 判断 [this] 是否拥有这个指令的权限
*
* @see CommandSender.hasPermission
* @see CommandPermission.testPermission
*/
public fun CommandSender.hasPermission(): Boolean
/**
* 满足两个权限其中一个即可使用指令
*/ // no extension for Java
public infix fun or(another: CommandPermission): CommandPermission = OrCommandPermissionImpl(this, another)
/**
* 同时拥有两个权限才能使用指令
*/ // no extension for Java
public infix fun and(another: CommandPermission): CommandPermission = AndCommandPermissionImpl(this, another)
/**
* 任何人都可以使用这个指令
*/
public object Any : CommandPermission {
public override fun CommandSender.hasPermission(): Boolean = true
}
/**
* 任何人都不能使用这个指令. 指令只能通过调用 [Command.onCommand] 执行.
*/
public object None : CommandPermission {
public override fun CommandSender.hasPermission(): Boolean = false
}
/**
* 来自任何 [Bot] 的任何一个管理员或群主都可以使用这个指令
*/
public object Operator : CommandPermission {
public override fun CommandSender.hasPermission(): Boolean {
return this is MemberCommandSender && this.user.isOperator()
}
}
/**
* 来自任何 [Bot] 的任何一个群主都可以使用这个指令
*/
public object GroupOwner : CommandPermission {
public override fun CommandSender.hasPermission(): Boolean {
return this is MemberCommandSender && this.user.isOwner()
}
}
/**
* 管理员 (不包含群主) 可以使用这个指令
*/
public object GroupAdmin : CommandPermission {
public override fun CommandSender.hasPermission(): Boolean {
return this is MemberCommandSender && this.user.isAdministrator()
}
}
/**
* 任何 [Bot] manager 都可以使用这个指令
*/
public object Manager : CommandPermission {
public override fun CommandSender.hasPermission(): Boolean {
return this is MemberCommandSender && this.user.isManager
}
}
/**
* 仅控制台能使用和这个指令
*/
public object Console : CommandPermission {
public override fun CommandSender.hasPermission(): Boolean = this is ConsoleCommandSender
}
/**
* 默认权限.
*
* @return [Manager] or [Console]
*/
public object Default : CommandPermission by (Manager or Console)
}
/**
* 判断 [this] 是否拥有权限 [permission]
*
* @see CommandSender.hasPermission
* @see CommandPermission.testPermission
* @see CommandPermission.hasPermission
*/
public inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean =
permission.run { this@hasPermission.hasPermission() }
/**
* 判断 [sender] 是否拥有权限 [this]
*
* @see CommandSender.hasPermission
* @see CommandPermission.testPermission
* @see CommandPermission.hasPermission
*/
public inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() }
/**
* 判断 [sender] 是否拥有权限 [Command.permission]
*
* @see CommandSender.hasPermission
* @see CommandPermission.testPermission
* @see CommandPermission.hasPermission
*/
public inline fun Command.testPermission(sender: CommandSender): Boolean = sender.hasPermission(this.permission)

View File

@ -32,8 +32,10 @@ import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
import net.mamoe.mirai.console.internal.data.castOrNull import net.mamoe.mirai.console.internal.data.castOrNull
import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf
import net.mamoe.mirai.console.permission.AbstractPermissibleIdentifier
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permissible import net.mamoe.mirai.console.permission.Permissible
import net.mamoe.mirai.console.permission.PermissibleIdentifier
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScopeContext import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScopeContext
@ -512,6 +514,9 @@ public abstract class ConsoleCommandSender @ConsoleFrontEndImplementation constr
public final override val name: String get() = NAME public final override val name: String get() = NAME
public final override fun toString(): String = NAME public final override fun toString(): String = NAME
@ExperimentalPermission
public final override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.Console
public companion object INSTANCE : ConsoleCommandSender(), CoroutineScope { public companion object INSTANCE : ConsoleCommandSender(), CoroutineScope {
public const val NAME: String = "ConsoleCommandSender" public const val NAME: String = "ConsoleCommandSender"
public override val coroutineContext: CoroutineContext by lazy { MiraiConsole.childScopeContext(NAME) } public override val coroutineContext: CoroutineContext by lazy { MiraiConsole.childScopeContext(NAME) }
@ -607,6 +612,9 @@ public open class FriendCommandSender internal constructor(
public override val subject: Contact get() = user public override val subject: Contact get() = user
public override fun toString(): String = "FriendCommandSender($user)" public override fun toString(): String = "FriendCommandSender($user)"
@ExperimentalPermission
public override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.ExactFriend(user.id)
@JvmBlockingBridge @JvmBlockingBridge
public override suspend fun sendMessage(message: String): MessageReceipt<Friend> = sendMessage(PlainText(message)) public override suspend fun sendMessage(message: String): MessageReceipt<Friend> = sendMessage(PlainText(message))
@ -623,10 +631,13 @@ public open class MemberCommandSender internal constructor(
) : AbstractUserCommandSender(), ) : AbstractUserCommandSender(),
GroupAwareCommandSender, GroupAwareCommandSender,
CoroutineScope by user.childScope("MemberCommandSender") { CoroutineScope by user.childScope("MemberCommandSender") {
public override val group: Group get() = user.group public final override val group: Group get() = user.group
public override val subject: Group get() = group public override val subject: Group get() = group
public override fun toString(): String = "MemberCommandSender($user)" public override fun toString(): String = "MemberCommandSender($user)"
@ExperimentalPermission
public override val identifier: PermissibleIdentifier = AbstractPermissibleIdentifier.ExactMember(group.id, user.id)
@JvmBlockingBridge @JvmBlockingBridge
public override suspend fun sendMessage(message: String): MessageReceipt<Group> = sendMessage(PlainText(message)) public override suspend fun sendMessage(message: String): MessageReceipt<Group> = sendMessage(PlainText(message))
@ -647,6 +658,10 @@ public open class TempCommandSender internal constructor(
public override val subject: Contact get() = group public override val subject: Contact get() = group
public override fun toString(): String = "TempCommandSender($user)" public override fun toString(): String = "TempCommandSender($user)"
@ExperimentalPermission
public override val identifier: PermissibleIdentifier =
AbstractPermissibleIdentifier.ExactTemp(user.group.id, user.id)
@JvmBlockingBridge @JvmBlockingBridge
public override suspend fun sendMessage(message: String): MessageReceipt<Member> = sendMessage(PlainText(message)) public override suspend fun sendMessage(message: String): MessageReceipt<Member> = sendMessage(PlainText(message))

View File

@ -20,11 +20,12 @@ package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.description.* import net.mamoe.mirai.console.command.description.*
import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand
import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.FUNCTION import kotlin.annotation.AnnotationTarget.FUNCTION
import kotlin.reflect.KClass
/** /**
* 复合指令. 指令注册时候会通过反射构造指令解析器. * 复合指令. 指令注册时候会通过反射构造指令解析器.
@ -85,10 +86,10 @@ public abstract class CompositeCommand(
owner: CommandOwner, owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "no description available", description: String = "no description available",
permission: CommandPermission = CommandPermission.Default, basePermission: PermissionId? = null,
prefixOptional: Boolean = false, prefixOptional: Boolean = false,
overrideContext: CommandArgumentContext = EmptyCommandArgumentContext overrideContext: CommandArgumentContext = EmptyCommandArgumentContext
) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional), ) : Command, AbstractReflectionCommand(owner, names, description, basePermission, prefixOptional),
CommandArgumentContextAware { CommandArgumentContextAware {
/** /**
@ -112,7 +113,8 @@ public abstract class CompositeCommand(
/** 指定子指令要求的权限 */ /** 指定子指令要求的权限 */
@Retention(RUNTIME) @Retention(RUNTIME)
@Target(FUNCTION) @Target(FUNCTION)
protected annotation class Permission(val value: KClass<out CommandPermission>) @ExperimentalPermission
protected annotation class Permission(val value: String)
/** 指令描述 */ /** 指令描述 */
@Retention(RUNTIME) @Retention(RUNTIME)

View File

@ -14,6 +14,10 @@ package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.command.java.JRawCommand import net.mamoe.mirai.console.command.java.JRawCommand
import net.mamoe.mirai.console.internal.command.createCommandPermission
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
/** /**
@ -40,10 +44,13 @@ public abstract class RawCommand(
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */ /** 指令描述, 用于显示在 [BuiltInCommands.Help] */
public override val description: String = "<no descriptions given>", public override val description: String = "<no descriptions given>",
/** 指令权限 */ /** 指令权限 */
public override val permission: CommandPermission = CommandPermission.Default, basePermission: PermissionId? = null,
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
public override val prefixOptional: Boolean = false public override val prefixOptional: Boolean = false
) : Command { ) : Command {
@OptIn(ExperimentalPermission::class)
public override val permission: Permission by lazy { createCommandPermission(basePermission) }
/** /**
* 在指令被执行时调用. * 在指令被执行时调用.
* *

View File

@ -22,6 +22,7 @@ import net.mamoe.mirai.console.command.description.*
import net.mamoe.mirai.console.command.java.JSimpleCommand import net.mamoe.mirai.console.command.java.JSimpleCommand
import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand
import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
/** /**
@ -51,10 +52,10 @@ public abstract class SimpleCommand(
owner: CommandOwner, owner: CommandOwner,
vararg names: String, vararg names: String,
description: String = "no description available", description: String = "no description available",
permission: CommandPermission = CommandPermission.Default, basePermission: PermissionId? = null,
prefixOptional: Boolean = false, prefixOptional: Boolean = false,
overrideContext: CommandArgumentContext = EmptyCommandArgumentContext overrideContext: CommandArgumentContext = EmptyCommandArgumentContext
) : Command, AbstractReflectionCommand(owner, names, description, permission, prefixOptional), ) : Command, AbstractReflectionCommand(owner, names, description, basePermission, prefixOptional),
CommandArgumentContextAware { CommandArgumentContextAware {
/** /**

View File

@ -9,8 +9,13 @@
package net.mamoe.mirai.console.command.java package net.mamoe.mirai.console.command.java
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.BuiltInCommands
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandOwner
import net.mamoe.mirai.console.command.CompositeCommand
import net.mamoe.mirai.console.command.description.buildCommandArgumentContext import net.mamoe.mirai.console.command.description.buildCommandArgumentContext
import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
/** /**
@ -64,16 +69,17 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
* @see buildCommandArgumentContext * @see buildCommandArgumentContext
*/ */
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
public abstract class JCompositeCommand( public abstract class JCompositeCommand @JvmOverloads constructor(
owner: CommandOwner, owner: CommandOwner,
vararg names: String vararg names: String,
) : CompositeCommand(owner, *names) { basePermission: PermissionId? = null,
) : CompositeCommand(owner, *names, basePermission = basePermission) {
/** 指令描述, 用于显示在 [BuiltInCommands.Help] */ /** 指令描述, 用于显示在 [BuiltInCommands.Help] */
public final override var description: String = "<no descriptions given>" public final override var description: String = "<no descriptions given>"
protected set protected set
/** 指令权限 */ @OptIn(ExperimentalPermission::class)
public final override var permission: CommandPermission = CommandPermission.Default public final override var permission: net.mamoe.mirai.console.permission.Permission = super.permission
protected set protected set
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */

View File

@ -13,6 +13,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute import net.mamoe.mirai.console.command.CommandManager.INSTANCE.execute
import net.mamoe.mirai.console.internal.command.createCommandPermission
import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.SingleMessage
@ -42,14 +45,15 @@ import net.mamoe.mirai.message.data.SingleMessage
* *
* @see JRawCommand * @see JRawCommand
*/ */
public abstract class JRawCommand( public abstract class JRawCommand @JvmOverloads constructor(
/** /**
* 指令拥有者. * 指令拥有者.
* @see CommandOwner * @see CommandOwner
*/ */
public override val owner: CommandOwner, public override val owner: CommandOwner,
/** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */ /** 指令名. 需要至少有一个元素. 所有元素都不能带有空格 */
public override vararg val names: String public override vararg val names: String,
basePermission: PermissionId? = null,
) : Command { ) : Command {
/** 用法说明, 用于发送给用户 */ /** 用法说明, 用于发送给用户 */
public override var usage: String = "<no usages given>" public override var usage: String = "<no usages given>"
@ -60,7 +64,7 @@ public abstract class JRawCommand(
protected set protected set
/** 指令权限 */ /** 指令权限 */
public final override var permission: CommandPermission = CommandPermission.Default public final override var permission: Permission = createCommandPermission(basePermission)
protected set protected set
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */ /** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */

View File

@ -12,9 +12,10 @@ package net.mamoe.mirai.console.command.java
import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.command.CommandOwner import net.mamoe.mirai.console.command.CommandOwner
import net.mamoe.mirai.console.command.CommandPermission
import net.mamoe.mirai.console.command.SimpleCommand import net.mamoe.mirai.console.command.SimpleCommand
import net.mamoe.mirai.console.command.description.CommandArgumentContext import net.mamoe.mirai.console.command.description.CommandArgumentContext
import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.permission.PermissionId
/** /**
* Java 实现: * Java 实现:
@ -41,11 +42,12 @@ import net.mamoe.mirai.console.command.description.CommandArgumentContext
*/ */
public abstract class JSimpleCommand( public abstract class JSimpleCommand(
owner: CommandOwner, owner: CommandOwner,
vararg names: String vararg names: String,
) : SimpleCommand(owner, *names) { basePermission: PermissionId,
) : SimpleCommand(owner, *names, basePermission = basePermission) {
public override var description: String = super.description public override var description: String = super.description
protected set protected set
public override var permission: CommandPermission = super.permission public override var permission: Permission = super.permission
protected set protected set
public override var prefixOptional: Boolean = super.prefixOptional public override var prefixOptional: Boolean = super.prefixOptional
protected set protected set

View File

@ -21,6 +21,9 @@ import kotlin.contracts.contract
import kotlin.internal.LowPriorityInOverloadResolution import kotlin.internal.LowPriorityInOverloadResolution
import kotlin.reflect.KClass import kotlin.reflect.KClass
/**
* 表示一个扩展点
*/
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
public open class AbstractExtensionPoint<T : Extension>( public open class AbstractExtensionPoint<T : Extension>(
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
@ -38,7 +41,7 @@ public open class AbstractExtensionPoint<T : Extension>(
@Synchronized @Synchronized
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
public fun registerExtension(plugin: Plugin, extension: T) { public fun registerExtension(plugin: Plugin, extension: T) {
require(plugin.isEnabled) { "Plugin $plugin must be enabled before registering an extension." } // require(plugin.isEnabled) { "Plugin $plugin must be enabled before registering an extension." }
instances.add(ExtensionRegistry(plugin, extension)) instances.add(ExtensionRegistry(plugin, extension))
} }

View File

@ -13,9 +13,17 @@ import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.extensions.PluginLoaderProvider import net.mamoe.mirai.console.extensions.PluginLoaderProvider
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
/**
* 表示一个扩展.
*
* Console 许多不容易通过
*/
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
public interface Extension public interface Extension
/**
* 增加一些函数 (方法)的扩展
*/
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
public interface FunctionExtension : Extension public interface FunctionExtension : Extension

View File

@ -4,10 +4,13 @@ import net.mamoe.mirai.console.extension.AbstractExtensionPoint
import net.mamoe.mirai.console.extension.SingletonExtension import net.mamoe.mirai.console.extension.SingletonExtension
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.permission.PermissionService
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
/**
* [权限服务][PermissionService] 提供器.
*
* 此扩展
*/
@ExperimentalPermission @ExperimentalPermission
@ConsoleExperimentalAPI
public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> { public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> {
public companion object ExtensionPoint : public companion object ExtensionPoint :
AbstractExtensionPoint<PermissionServiceProvider>(PermissionServiceProvider::class) AbstractExtensionPoint<PermissionServiceProvider>(PermissionServiceProvider::class)

View File

@ -12,7 +12,15 @@ package net.mamoe.mirai.console.extensions
import net.mamoe.mirai.console.extension.AbstractExtensionPoint import net.mamoe.mirai.console.extension.AbstractExtensionPoint
import net.mamoe.mirai.console.extension.FunctionExtension import net.mamoe.mirai.console.extension.FunctionExtension
/**
* Console 启动完成后立即在主线程调用的扩展. 用于进行一些必要的延迟初始化.
*
* 这些扩展只会, 且一定会被调用正好一次.
*/
public fun interface PostStartupExtension : FunctionExtension { public fun interface PostStartupExtension : FunctionExtension {
/**
* 将在 Console 主线程执行.
*/
public operator fun invoke() public operator fun invoke()
public companion object ExtensionPoint : AbstractExtensionPoint<PostStartupExtension>(PostStartupExtension::class) public companion object ExtensionPoint : AbstractExtensionPoint<PostStartupExtension>(PostStartupExtension::class)

View File

@ -34,8 +34,8 @@ import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope
import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
import net.mamoe.mirai.console.permission.ExperimentalPermission 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.permission.PermissionService
import net.mamoe.mirai.console.permission.StorablePermissionService
import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager
import net.mamoe.mirai.console.plugin.center.PluginCenter import net.mamoe.mirai.console.plugin.center.PluginCenter
@ -102,23 +102,27 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) } Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
} }
mainLogger.info { "Reloading configurations..." } mainLogger.verbose { "Loading configurations..." }
ConsoleDataScope.reloadAll() ConsoleDataScope.reloadAll()
PermissionService // init mainLogger.verbose { "Loading PermissionService..." }
if (PermissionService.INSTANCE is HotDeploymentSupportPermissionService<*>) { PermissionService.INSTANCE.let { ps ->
if (ps is StorablePermissionService<*>) {
ConsoleDataScope.addAndReloadConfig(ps.config)
mainLogger.verbose { "Reloaded PermissionService settings." }
}
} }
mainLogger.verbose { "Loading built-in commands..." }
BuiltInCommands.registerAll() BuiltInCommands.registerAll()
mainLogger.info { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" } mainLogger.verbose { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
CommandManager CommandManager
CommandManagerImpl.commandListener // start CommandManagerImpl.commandListener // start
mainLogger.info { "Loading plugins..." } mainLogger.verbose { "Loading plugins..." }
PluginManager PluginManager
PluginManagerImpl.loadEnablePlugins() PluginManagerImpl.loadEnablePlugins()
mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." } mainLogger.verbose { "${PluginManager.plugins.size} plugin(s) loaded." }
mainLogger.info { "mirai-console started successfully." } mainLogger.info { "mirai-console started successfully." }
runBlocking { runBlocking {

View File

@ -1,32 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.internal.command
import net.mamoe.mirai.console.command.CommandPermission
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.hasPermission
internal class OrCommandPermissionImpl(
private val first: CommandPermission,
private val second: CommandPermission
) : CommandPermission {
override fun CommandSender.hasPermission(): Boolean {
return this.hasPermission(first) || this.hasPermission(second)
}
}
internal class AndCommandPermissionImpl(
private val first: CommandPermission,
private val second: CommandPermission
) : CommandPermission {
override fun CommandSender.hasPermission(): Boolean {
return this.hasPermission(first) && this.hasPermission(second)
}
}

View File

@ -16,11 +16,15 @@ import net.mamoe.mirai.console.command.Command.Companion.primaryName
import net.mamoe.mirai.console.command.description.CommandArgumentContext import net.mamoe.mirai.console.command.description.CommandArgumentContext
import net.mamoe.mirai.console.command.description.CommandArgumentContextAware import net.mamoe.mirai.console.command.description.CommandArgumentContextAware
import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip
import net.mamoe.mirai.console.permission.*
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KAnnotatedElement
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KFunction import kotlin.reflect.KFunction
import kotlin.reflect.full.* import kotlin.reflect.full.callSuspend
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.isSubclassOf
internal object CompositeCommandSubCommandAnnotationResolver : internal object CompositeCommandSubCommandAnnotationResolver :
AbstractReflectionCommand.SubCommandAnnotationResolver { AbstractReflectionCommand.SubCommandAnnotationResolver {
@ -44,13 +48,13 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
owner: CommandOwner, owner: CommandOwner,
names: Array<out String>, names: Array<out String>,
description: String = "<no description available>", description: String = "<no description available>",
permission: CommandPermission = CommandPermission.Default, basePermission: PermissionId? = null,
prefixOptional: Boolean = false prefixOptional: Boolean = false
) : Command, AbstractCommand( ) : Command, AbstractCommand(
owner, owner,
names = names, names = names,
description = description, description = description,
permission = permission, basePermission = basePermission,
prefixOptional = prefixOptional prefixOptional = prefixOptional
), CommandArgumentContextAware { ), CommandArgumentContextAware {
internal abstract val subCommandAnnotationResolver: SubCommandAnnotationResolver internal abstract val subCommandAnnotationResolver: SubCommandAnnotationResolver
@ -70,7 +74,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy { internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy {
DefaultSubCommandDescriptor( DefaultSubCommandDescriptor(
"", "",
permission, createCommandPermission(basePermission),
onCommand = { sender: CommandSender, args: MessageChain -> onCommand = { sender: CommandSender, args: MessageChain ->
sender.onDefault(args) sender.onDefault(args)
} }
@ -115,7 +119,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
internal class DefaultSubCommandDescriptor( internal class DefaultSubCommandDescriptor(
val description: String, val description: String,
val permission: CommandPermission, val permission: Permission,
val onCommand: suspend (sender: CommandSender, rawArgs: MessageChain) -> Unit val onCommand: suspend (sender: CommandSender, rawArgs: MessageChain) -> Unit
) )
@ -123,7 +127,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
val names: Array<out String>, val names: Array<out String>,
val params: Array<CommandParameter<*>>, val params: Array<CommandParameter<*>>,
val description: String, val description: String,
val permission: CommandPermission, val permission: Permission,
val onCommand: suspend (sender: CommandSender, parsedArgs: Array<out Any>) -> Boolean, val onCommand: suspend (sender: CommandSender, parsedArgs: Array<out Any>) -> Boolean,
val context: CommandArgumentContext val context: CommandArgumentContext
) { ) {
@ -209,10 +213,6 @@ internal fun Any.flattenCommandComponents(): MessageChain = buildMessageChain {
internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean = internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =
findAnnotation<T>() != null findAnnotation<T>() != null
internal inline fun <T : Any> KClass<out T>.getInstance(): T {
return this.objectInstance ?: this.createInstance()
}
internal val KClass<*>.qualifiedNameOrTip: String get() = this.qualifiedName ?: "<anonymous class>" internal val KClass<*>.qualifiedNameOrTip: String get() = this.qualifiedName ?: "<anonymous class>"
internal fun Array<AbstractReflectionCommand.SubCommandDescriptor>.createUsage(baseCommand: AbstractReflectionCommand): String = internal fun Array<AbstractReflectionCommand.SubCommandDescriptor>.createUsage(baseCommand: AbstractReflectionCommand): String =
@ -246,6 +246,7 @@ internal fun AbstractReflectionCommand.SubCommandDescriptor.createUsage(baseComm
appendLine() appendLine()
}.trimEnd() }.trimEnd()
@OptIn(ExperimentalPermission::class)
internal fun AbstractReflectionCommand.createSubCommand( internal fun AbstractReflectionCommand.createSubCommand(
function: KFunction<*>, function: KFunction<*>,
context: CommandArgumentContext context: CommandArgumentContext
@ -322,8 +323,8 @@ internal fun AbstractReflectionCommand.createSubCommand(
return SubCommandDescriptor( return SubCommandDescriptor(
commandName, commandName,
params, params,
subDescription, subDescription, // overridePermission?.value
overridePermission?.value?.getInstance() ?: permission, overridePermission?.value?.let { PermissionService.INSTANCE[PermissionId.parseFromString(it)] } ?: permission,
onCommand = { sender: CommandSender, args: Array<out Any> -> onCommand = { sender: CommandSender, args: Array<out Any> ->
val result = if (notStatic) { val result = if (notStatic) {
if (hasSenderParam) { if (hasSenderParam) {

View File

@ -10,6 +10,8 @@
package net.mamoe.mirai.console.internal.command package net.mamoe.mirai.console.internal.command
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.Command.Companion.primaryName
import net.mamoe.mirai.console.permission.*
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
@ -136,12 +138,17 @@ internal fun Group.fuzzySearchMember(
} }
} }
@OptIn(ExperimentalPermission::class)
internal fun Command.createCommandPermission(basePermission: PermissionId?): Permission {
return PermissionService.INSTANCE.register(owner.permissionId(primaryName), description, basePermission)
}
//// internal //// internal
@JvmSynthetic @JvmSynthetic
internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Array(size - n) { this[n + it] } internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Array(size - n) { this[n + it] }
@OptIn(ExperimentalPermission::class)
@JvmSynthetic @JvmSynthetic
@Throws(CommandExecutionException::class) @Throws(CommandExecutionException::class)
internal suspend fun CommandSender.executeCommandInternal( internal suspend fun CommandSender.executeCommandInternal(
@ -150,7 +157,7 @@ internal suspend fun CommandSender.executeCommandInternal(
commandName: String, commandName: String,
checkPermission: Boolean checkPermission: Boolean
): CommandExecuteResult { ): CommandExecuteResult {
if (checkPermission && !command.testPermission(this)) { if (checkPermission && !command.permission.testPermission(this)) {
return CommandExecuteResult.PermissionDenied(command, commandName) return CommandExecuteResult.PermissionDenied(command, commandName)
} }

View File

@ -21,8 +21,13 @@ import net.mamoe.mirai.utils.minutesToMillis
internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") { internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") {
private val data: Array<out PluginData> = arrayOf() private val data: List<PluginData> = mutableListOf()
private val configs: Array<out PluginConfig> = arrayOf(ManagersConfig, AutoLoginConfig) private val configs: MutableList<PluginConfig> = mutableListOf(ManagersConfig, AutoLoginConfig)
fun addAndReloadConfig(config: PluginConfig) {
configs.add(config)
ConsoleBuiltInPluginConfigStorage.load(ConsoleBuiltInPluginConfigHolder, config)
}
fun reloadAll() { fun reloadAll() {
data.forEach { dt -> data.forEach { dt ->

View File

@ -1,101 +0,0 @@
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

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

View File

@ -9,50 +9,85 @@
package net.mamoe.mirai.console.permission package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.data.AutoSavePluginConfig
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.createType
@ExperimentalPermission @ExperimentalPermission
public object AllGrantPermissionService : PermissionService<PermissionImpl> { public object AllGrantPermissionService : PermissionService<PermissionImpl> {
private val all = ConcurrentHashMap<PermissionIdentifier, PermissionImpl>() private val all = ConcurrentHashMap<PermissionId, PermissionImpl>()
override val permissionType: KClass<PermissionImpl> override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class get() = PermissionImpl::class
override fun register( override fun register(
identifier: PermissionIdentifier, id: PermissionId,
description: String, description: String,
base: PermissionIdentifier? base: PermissionId?
): PermissionImpl { ): PermissionImpl {
val new = PermissionImpl(identifier, description, base) val new = PermissionImpl(id, description, base)
if (all.putIfAbsent(identifier, new) != null) { if (all.putIfAbsent(id, new) != null) {
throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}") throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[id]}")
} }
return new return new
} }
override fun get(identifier: PermissionIdentifier): PermissionImpl? = all[identifier] override fun get(id: PermissionId): PermissionImpl? = all[id]
override fun getGrantedPermissions(permissible: Permissible): Sequence<PermissionImpl> = all.values.asSequence() override fun getGrantedPermissions(permissible: Permissible): Sequence<PermissionImpl> = all.values.asSequence()
override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) {
}
override fun testPermission(permissible: Permissible, permission: PermissionImpl): Boolean = true
override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) {
}
} }
@ExperimentalPermission @ExperimentalPermission
public object AllDenyPermissionService : PermissionService<PermissionImpl> { public object AllDenyPermissionService : PermissionService<PermissionImpl> {
private val all = ConcurrentHashMap<PermissionIdentifier, PermissionImpl>() private val all = ConcurrentHashMap<PermissionId, PermissionImpl>()
override val permissionType: KClass<PermissionImpl> override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class get() = PermissionImpl::class
override fun register( override fun register(
identifier: PermissionIdentifier, id: PermissionId,
description: String, description: String,
base: PermissionIdentifier? base: PermissionId?
): PermissionImpl { ): PermissionImpl {
val new = PermissionImpl(identifier, description, base) val new = PermissionImpl(id, description, base)
if (all.putIfAbsent(identifier, new) != null) { if (all.putIfAbsent(id, new) != null) {
throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}") throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[id]}")
} }
return new return new
} }
override fun get(identifier: PermissionIdentifier): PermissionImpl? = all[identifier] override fun get(id: PermissionId): PermissionImpl? = all[id]
override fun getGrantedPermissions(permissible: Permissible): Sequence<PermissionImpl> = emptySequence() override fun getGrantedPermissions(permissible: Permissible): Sequence<PermissionImpl> = emptySequence()
override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) {
}
override fun testPermission(permissible: Permissible, permission: PermissionImpl): Boolean = false
override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: PermissionImpl) {
}
} }
@ExperimentalPermission
internal object BuiltInPermissionService : AbstractConcurrentPermissionService<PermissionImpl>(),
StorablePermissionService<PermissionImpl> {
@ExperimentalPermission
override val permissionType: KClass<PermissionImpl>
get() = PermissionImpl::class
override val permissions: MutableMap<PermissionId, PermissionImpl> get() = config.permissions
override val grantedPermissionsMap: MutableMap<PermissionId, MutableCollection<PermissibleIdentifier>> get() = config.grantedPermissionMap
override fun createPermission(id: PermissionId, description: String, base: PermissionId?): PermissionImpl =
PermissionImpl(id, description, base)
override val config: StorablePermissionService.ConcurrentSaveData<PermissionImpl> =
StorablePermissionService.ConcurrentSaveData(
PermissionImpl::class.createType(),
"PermissionService",
AutoSavePluginConfig()
)
}

View File

@ -1,17 +0,0 @@
/*
* 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

@ -7,21 +7,88 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("NOTHING_TO_INLINE", "unused") @file:Suppress("NOTHING_TO_INLINE", "unused", "MemberVisibilityCanBePrivate")
package net.mamoe.mirai.console.permission package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.command.CommandSender
/** /**
* 可拥有权限的对象.
*
* 典型的实例为 [CommandSender]
* *
* 注意: 请不要自主实现 [Permissible] * 注意: 请不要自主实现 [Permissible]
*
* @see CommandSender
*/ */
@ExperimentalPermission @ExperimentalPermission
public interface Permissible public interface Permissible {
public val identifier: PermissibleIdentifier
}
@ExperimentalPermission("Classname is subject to change")
public interface PermissibleIdentifier {
public val parents: Array<out PermissibleIdentifier>
public companion object {
@ExperimentalPermission
public infix fun PermissibleIdentifier.grantedWith(with: PermissibleIdentifier): Boolean {
return allParentsWithSelf().any { it == with }
}
internal fun PermissibleIdentifier.allParentsWithSelf(): Sequence<PermissibleIdentifier> {
return sequence {
yield(this@allParentsWithSelf)
yieldAll(parents.asSequence())
}
}
}
}
/**
*
*/
@ExperimentalPermission
public sealed class AbstractPermissibleIdentifier(
public final override vararg val parents: PermissibleIdentifier
) : PermissibleIdentifier {
public object AnyGroup : AbstractPermissibleIdentifier(AnyContact)
public data class ExactGroup(public val groupId: Long) : AbstractPermissibleIdentifier(AnyGroup)
public data class AnyMember(public val groupId: Long) : AbstractPermissibleIdentifier(AnyMemberFromAnyGroup)
public object AnyMemberFromAnyGroup : AbstractPermissibleIdentifier(AnyUser)
public data class ExactMember(
public val groupId: Long,
public val memberId: Long
) : AbstractPermissibleIdentifier(AnyMember(groupId), ExactUser(memberId))
public object AnyFriend : AbstractPermissibleIdentifier(AnyUser)
public data class ExactFriend(
public val id: Long
) : AbstractPermissibleIdentifier(ExactUser(id))
public object AnyTemp : AbstractPermissibleIdentifier(AnyUser)
public data class ExactTemp(
public val groupId: Long,
public val id: Long
) : AbstractPermissibleIdentifier(ExactUser(groupId)) // TODO: 2020/9/8 ExactMember ?
public object AnyUser : AbstractPermissibleIdentifier(AnyContact)
public data class ExactUser(
public val id: Long
) : AbstractPermissibleIdentifier(AnyUser)
public object AnyContact : AbstractPermissibleIdentifier()
public object Console : AbstractPermissibleIdentifier()
}
@ExperimentalPermission @ExperimentalPermission
public inline fun Permissible.hasPermission(permission: Permission): Boolean = public inline fun Permissible.hasPermission(permission: Permission): Boolean =
PermissionService.run { permission.testPermission(this@hasPermission) } PermissionService.run { permission.testPermission(this@hasPermission) }
@ExperimentalPermission @ExperimentalPermission
public inline fun Permissible.hasPermission(permission: PermissionIdentifier): Boolean = public inline fun Permissible.hasPermission(permission: PermissionId): Boolean =
PermissionService.run { permission.testPermission(this@hasPermission) } PermissionService.run { permission.testPermission(this@hasPermission) }

View File

@ -9,23 +9,26 @@
package net.mamoe.mirai.console.permission package net.mamoe.mirai.console.permission
import kotlinx.serialization.Serializable
/** /**
* 一个权限节点 * 一个权限节点
*/ */
@ExperimentalPermission @ExperimentalPermission
public interface Permission { public interface Permission {
public val identifier: PermissionIdentifier public val id: PermissionId
public val description: String public val description: String
public val base: PermissionIdentifier? public val base: PermissionId?
} }
/** /**
* [Permission] 的简单实现 * [Permission] 的简单实现
*/ */
@Serializable
@ExperimentalPermission @ExperimentalPermission
public open class PermissionImpl( public class PermissionImpl(
override val identifier: PermissionIdentifier, override val id: PermissionId,
override val description: String, override val description: String,
override val base: PermissionIdentifier? override val base: PermissionId?
) : Permission ) : Permission

View File

@ -14,7 +14,7 @@ import kotlin.reflect.KProperty
@ExperimentalPermission @ExperimentalPermission
public abstract class PermissionGroup( public abstract class PermissionGroup(
private val identifierNamespace: PermissionIdentifierNamespace, private val idNamespace: PermissionIdNamespace,
) { ) {
@ExperimentalPermission @ExperimentalPermission
public inner class PermissionBuilder { public inner class PermissionBuilder {
@ -22,30 +22,30 @@ public abstract class PermissionGroup(
internal var description: String = "<no description given>" internal var description: String = "<no description given>"
@JvmField @JvmField
internal var basePermission: PermissionIdentifier? = null internal var basePermission: PermissionId? = null
@JvmField @JvmField
internal var permissionChecker: PermissionChecker? = null internal var permissionChecker: PermissionChecker? = null
public fun description(description: String): PermissionBuilder = apply { this.description = description } public fun description(description: String): PermissionBuilder = apply { this.description = description }
public fun dependsOn(basePermission: PermissionIdentifier?): PermissionBuilder = public fun dependsOn(basePermission: PermissionId?): PermissionBuilder =
apply { this.basePermission = basePermission } apply { this.basePermission = basePermission }
public fun dependsOn(basePermission: Permission?): PermissionBuilder = public fun dependsOn(basePermission: Permission?): PermissionBuilder =
apply { this.basePermission = basePermission?.identifier } apply { this.basePermission = basePermission?.id }
public fun basePermission(basePermission: PermissionIdentifier?): PermissionBuilder = public fun basePermission(basePermission: PermissionId?): PermissionBuilder =
apply { this.basePermission = basePermission } apply { this.basePermission = basePermission }
public fun basePermission(basePermission: Permission?): PermissionBuilder = public fun basePermission(basePermission: Permission?): PermissionBuilder =
apply { this.basePermission = basePermission?.identifier } apply { this.basePermission = basePermission?.id }
public fun defaults(permissionChecker: PermissionChecker?): PermissionBuilder = public fun defaults(permissionChecker: PermissionChecker?): PermissionBuilder =
apply { this.permissionChecker = permissionChecker } apply { this.permissionChecker = permissionChecker }
public fun build(property: KProperty<*>): Permission { public fun build(property: KProperty<*>): Permission {
return PermissionService.INSTANCE.register( return PermissionService.INSTANCE.register(
identifierNamespace.permissionIdentifier(property.name), idNamespace.permissionId(property.name),
description, description,
basePermission basePermission
) )

View File

@ -10,13 +10,15 @@
package net.mamoe.mirai.console.permission package net.mamoe.mirai.console.permission
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.Serializer import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.console.internal.data.map import net.mamoe.mirai.console.internal.data.map
@Serializable
@ExperimentalPermission @ExperimentalPermission
public data class PermissionIdentifier( public data class PermissionId(
public val namespace: String, public val namespace: String,
public val id: String public val id: String
) { ) {
@ -29,17 +31,26 @@ public data class PermissionIdentifier(
} }
} }
@Serializer(forClass = PermissionIdentifier::class) @Serializer(forClass = PermissionId::class)
public object AsClassSerializer public object AsClassSerializer
public object AsStringSerializer : KSerializer<PermissionIdentifier> by String.serializer().map( public object AsStringSerializer : KSerializer<PermissionId> by String.serializer().map(
serializer = { it.namespace + ":" + it.id }, serializer = { it.namespace + ":" + it.id },
deserializer = { it.split(':').let { (namespace, id) -> PermissionIdentifier(namespace, id) } } deserializer = ::parseFromString
) )
public override fun toString(): String {
return "$namespace:$id"
}
public companion object {
public fun parseFromString(string: String): PermissionId =
string.split(':').let { (namespace, id) -> PermissionId(namespace, id) }
}
} }
@ExperimentalPermission @ExperimentalPermission
public interface PermissionIdentifierNamespace { public interface PermissionIdNamespace {
@ExperimentalPermission @ExperimentalPermission
public fun permissionIdentifier(identifierString: String): PermissionIdentifier public fun permissionId(id: String): PermissionId
} }

View File

@ -12,7 +12,8 @@
package net.mamoe.mirai.console.permission package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.extensions.PermissionServiceProvider import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import java.util.concurrent.ConcurrentHashMap import net.mamoe.mirai.console.permission.PermissibleIdentifier.Companion.grantedWith
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -23,23 +24,32 @@ public interface PermissionService<P : Permission> {
@ExperimentalPermission @ExperimentalPermission
public val permissionType: KClass<P> public val permissionType: KClass<P>
@Throws(DuplicatedRegistrationException::class) ///////////////////////////////////////////////////////////////////////////
public fun register(
identifier: PermissionIdentifier,
description: String,
base: PermissionIdentifier? = null
): P
public operator fun get(identifier: PermissionIdentifier): P? public operator fun get(id: PermissionId): P?
public fun getGrantedPermissions(permissible: Permissible): Sequence<P> public fun getGrantedPermissions(permissible: Permissible): Sequence<P>
public fun testPermission(permissible: Permissible, permission: P): Boolean = public fun testPermission(permissible: Permissible, permission: P): Boolean =
permissible.getGrantedPermissions().any { it == permission } permissible.getGrantedPermissions().any { it == permission }
///////////////////////////////////////////////////////////////////////////
@Throws(DuplicatedRegistrationException::class)
public fun register(
id: PermissionId,
description: String,
base: PermissionId? = null
): P
///////////////////////////////////////////////////////////////////////////
public fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P)
public fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P)
public companion object { public companion object {
private val builtIn: PermissionService<out Permission> get() = AllGrantPermissionService private val builtIn: PermissionService<out Permission> get() = BuiltInPermissionService
@get:JvmName("getInstance") @get:JvmName("getInstance")
@JvmStatic @JvmStatic
@ -51,29 +61,48 @@ public interface PermissionService<P : Permission> {
} }
@ExperimentalPermission @ExperimentalPermission
public abstract class AbstractPermissionService<P : Permission> : PermissionService<P> { public abstract class AbstractConcurrentPermissionService<P : Permission> : PermissionService<P> {
protected val all: MutableMap<PermissionIdentifier, P> = ConcurrentHashMap<PermissionIdentifier, P>() protected abstract val permissions: MutableMap<PermissionId, P>
protected abstract val grantedPermissionsMap: MutableMap<PermissionId, MutableCollection<PermissibleIdentifier>>
protected abstract fun createPermission( protected abstract fun createPermission(
identifier: PermissionIdentifier, id: PermissionId,
description: String, description: String,
base: PermissionIdentifier? base: PermissionId?
): P ): P
override fun register( override fun get(id: PermissionId): P? = permissions[id]
identifier: PermissionIdentifier,
description: String, override fun register(id: PermissionId, description: String, base: PermissionId?): P {
base: PermissionIdentifier? grantedPermissionsMap[id] = CopyOnWriteArrayList() // mutations are not quite often performed
): P { val instance = createPermission(id, description, base)
val new = createPermission(identifier, description, base) if (permissions.putIfAbsent(id, instance) != null) {
if (all.putIfAbsent(identifier, new) != null) { throw DuplicatedRegistrationException("Duplicated Permission registry. new: $instance, old: ${permissions[id]}")
throw DuplicatedRegistrationException("Duplicated Permission registry: ${all[identifier]}")
} }
return new return instance
} }
override fun get(identifier: PermissionIdentifier): P? = all[identifier] override fun grant(permissibleIdentifier: PermissibleIdentifier, permission: P) {
override fun getGrantedPermissions(permissible: Permissible): Sequence<P> = all.values.asSequence() val id = permission.id
grantedPermissionsMap[id]?.add(permissibleIdentifier)
?: error("Bad PermissionService implementation: grantedPermissionsMap[id] is null.")
}
override fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P) {
grantedPermissionsMap[permission.id]?.remove(permissibleIdentifier)
}
public override fun getGrantedPermissions(permissible: Permissible): Sequence<P> = sequence<P> {
for ((permissionIdentifier, permissibleIdentifiers) in grantedPermissionsMap) {
val myIdentifier = permissible.identifier
val granted =
if (permissibleIdentifiers.isEmpty()) false
else permissibleIdentifiers.any { myIdentifier grantedWith it }
if (granted) get(permissionIdentifier)?.let { yield(it) }
}
}
} }
@ExperimentalPermission @ExperimentalPermission
@ -98,7 +127,7 @@ public inline fun Permission.testPermission(permissible: Permissible): Boolean =
} }
@ExperimentalPermission @ExperimentalPermission
public inline fun PermissionIdentifier.testPermission(permissible: Permissible): Boolean { public inline fun PermissionId.testPermission(permissible: Permissible): Boolean {
val p = PermissionService.INSTANCE[this] ?: return false val p = PermissionService.INSTANCE[this] ?: return false
return p.testPermission(permissible) return p.testPermission(permissible)
} }

View File

@ -0,0 +1,52 @@
package net.mamoe.mirai.console.permission
import net.mamoe.mirai.console.data.PluginConfig
import net.mamoe.mirai.console.data.PluginDataExtensions.withDefault
import net.mamoe.mirai.console.data.value
import net.mamoe.mirai.console.data.valueFromKType
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
import kotlin.reflect.KVariance
import kotlin.reflect.full.createType
@ExperimentalPermission
public interface StorablePermissionService<P : Permission> : PermissionService<P> {
/**
* The config to be stored
*/
public val config: PluginConfig
@ExperimentalPermission
public class ConcurrentSaveData<P : Permission> private constructor(
permissionType: KType,
public override val saveName: String,
delegate: PluginConfig,
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?
) : PluginConfig by delegate {
public val permissions: MutableMap<PermissionId, P>
by valueFromKType<MutableMap<PermissionId, P>>(
MutableMap::class.createType(
listOf(
KTypeProjection(KVariance.INVARIANT, PermissionId::class.createType()),
KTypeProjection(KVariance.INVARIANT, permissionType),
)
),
ConcurrentHashMap()
)
public val grantedPermissionMap: MutableMap<PermissionId, MutableCollection<PermissibleIdentifier>>
by value<MutableMap<PermissionId, MutableCollection<PermissibleIdentifier>>>(ConcurrentHashMap())
.withDefault { CopyOnWriteArrayList() }
public companion object {
@JvmStatic
public operator fun <P : Permission> invoke(
permissionType: KType,
saveName: String,
delegate: PluginConfig,
): ConcurrentSaveData<P> = ConcurrentSaveData(permissionType, saveName, delegate, null)
}
}
}

View File

@ -22,7 +22,7 @@ import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
import net.mamoe.mirai.console.data.PluginConfig import net.mamoe.mirai.console.data.PluginConfig
import net.mamoe.mirai.console.data.PluginData import net.mamoe.mirai.console.data.PluginData
import net.mamoe.mirai.console.permission.ExperimentalPermission import net.mamoe.mirai.console.permission.ExperimentalPermission
import net.mamoe.mirai.console.permission.PermissionIdentifierNamespace import net.mamoe.mirai.console.permission.PermissionIdNamespace
import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.PluginFileExtensions import net.mamoe.mirai.console.plugin.PluginFileExtensions
import net.mamoe.mirai.console.plugin.ResourceContainer import net.mamoe.mirai.console.plugin.ResourceContainer
@ -45,7 +45,7 @@ import net.mamoe.mirai.utils.MiraiLogger
*/ */
@OptIn(ExperimentalPermission::class) @OptIn(ExperimentalPermission::class)
public interface JvmPlugin : Plugin, CoroutineScope, public interface JvmPlugin : Plugin, CoroutineScope,
PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder, PermissionIdentifierNamespace { PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder, PermissionIdNamespace {
/** 日志 */ /** 日志 */
public val logger: MiraiLogger public val logger: MiraiLogger

View File

@ -1,7 +1,6 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
kotlin("plugin.serialization") kotlin("plugin.serialization")
kotlin("kapt")
id("java") id("java")
`maven-publish` `maven-publish`
id("com.jfrog.bintray") id("com.jfrog.bintray")
@ -46,10 +45,10 @@ dependencies {
testApi(project(":mirai-console")) testApi(project(":mirai-console"))
val autoService = "1.0-rc7" // val autoService = "1.0-rc7"
kapt("com.google.auto.service", "auto-service", autoService) // kapt("com.google.auto.service", "auto-service", autoService)
compileOnly("com.google.auto.service", "auto-service-annotations", autoService) // compileOnly("com.google.auto.service", "auto-service-annotations", autoService)
testCompileOnly("com.google.auto.service", "auto-service-annotations", autoService) // testCompileOnly("com.google.auto.service", "auto-service-annotations", autoService)
} }
ext.apply { ext.apply {