# Mirai Console Backend - Permissions

Mirai Console 权限系统。

> 优先使用 Mirai Console 权限系统管理权限是最好的选择

[`PermissionService`]: ../backend/mirai-console/src/permission/PermissionService.kt
[`Permission`]: ../backend/mirai-console/src/permission/Permission.kt
[`RootPermission`]: ../backend/mirai-console/src/permission/Permission.kt#L82
[`PermissionId`]: ../backend/mirai-console/src/permission/PermissionId.kt
[`PermissionIdNamespace`]: ../backend/mirai-console/src/permission/PermissionIdNamespace.kt
[`Permittee`]: ../backend/mirai-console/src/permission/Permittee.kt
[`PermitteeId`]: ../backend/mirai-console/src/permission/PermitteeId.kt
[`AbstractPermitteeId`]: ../backend/mirai-console/src/permission/PermitteeId.kt#L77
[`CommandSender`]: ../backend/mirai-console/src/command/CommandSender.kt

## 权限

每个权限都由 [`Permission`] 对象表示。

一个 [`Permission`] 拥有这些信息：
```kotlin
interface Permission {
    val id: PermissionId // 唯一识别 ID
    val description: String // 描述信息
    val parent: Permission // 父权限
}
```

「权限」表示的意义是 “做一项工作的能力”。如 “执行指令 /stop”，“操作数据库” 都能叫作权限。

[`Permission`] 对象由 Console 内置或者由特定权限插件实现。其他插件不能实现 [`Permission`] 接口，只能从 `PermissionService` 注册并获取。

### 权限 ID

```kotlin
data class PermissionId(
    val namespace: String, // 命名空间
    val name: String, // 名称
)
```

[`PermissionId`] 是 [`Permission`] 的唯一标识符。知道 [`PermissionId`] 就可以获取到对应的 [`Permission`]。

字符串表示为 `$namespace:$name`，如 `console:command.stop`, `*:*`

> 一般情况下使用位于插件对象(`JvmPlugin`) 的 `permissionId` 为插件分配一个 [`PermissionId`]

#### 命名空间

命名空间（`namespace`）用于限定权限的创建者，避免冲突。

一些常见命名空间：

| 用途          | 命名空间      |
|:-------------|:------------|
| 根权限         | `"*"`       |
| Console 内置  | `"console"` |
| ID 为 A 的插件 | `"A"`       |

#### 名称

名称则表示特定的含义。如一个指令，某一项操作等。

一些常见名称：

| 用途                       | 名称           |
|:--------------------------|:--------------|
| 根权限                     | `"*"`         |
| Console 内置的名为 A 的指令  | `"command.A"` |
| ID 为 A 的插件的名为 B 的指令 | `"command.B"` |

#### 根权限

[`RootPermission`] 是所有权限的父权限。其 ID 为 `*:*`

> 如果 [`Permittee`] (见下文) 拥有根权限, 相当于 [`Permittee`] 拥有全部权限 (内置实现)

## 被许可人

```kotlin
interface Permittee {
    val permitteeId: PermitteeId
}
```

[`Permittee`] 表示一个可被赋予权限的对象，即 '被许可人'。

[`CommandSender`] 实现 [`Permittee`]。

被许可人自身不持有拥有的权限列表，而是拥有 [`PermitteeId`]，标识自己的身份，供 [权限服务][`PermissionService`] 处理。

**注意**：请不要自主实现 [`Permittee`]。

### 被许可人 ID

```kotlin
interface PermitteeId {
    val directParents: Array<out PermitteeId> // 直接父对象
    fun asString(): String // 转换为字符串表示
}
````

[`PermitteeId`] 是被许可人的标识符。

一个这样的标识符既可代表特定的单个 [`Permittee`], 也可以表示多个同类 [`Permittee`].

#### `directParents`
[`PermitteeId`] 允许拥有多个父对象。在检查权限时会首先检查自己, 再递归检查父类。

#### 衍生类型

[`PermitteeId`] 的实现通常是 [`AbstractPermitteeId`]

在 [`AbstractPermitteeId`] 查看其子类。

**注意**: 对应 [权限服务][`PermissionService`] 没明确说明可以自行实现时, 不要轻易实现 [`PermitteeId`]

#### 字符串表示

当使用 `PermitteeId.asString` 时, 不同的类型的返回值如下表所示. 这些格式也适用于 [权限指令](#使用内置权限服务指令).  
(不区分大小写. 不区分 Bot).

|    被许可人类型    | 字符串表示示例 | 备注                                 |
|:----------------:|:-----------:|:------------------------------------|
|      控制台       |   console   |                                     |
|   任意其他客户端    |   client*   | 即 Bot 自己发消息给自己                |
|      精确群       |   g123456   | 表示群, **而不表示群成员, 不表示群成员** |
|      精确好友      |   f123456   | 必须通过好友消息                       |
|   精确群临时会话    | t123456.789 | 群 123456 内的成员 789. 必须通过临时会话 |
|     精确群成员     | m123456.789 | 群 123456 内的成员 789. 同时包含临时会话 |
|      精确用户      |   u123456   | 同时包含群成员, 好友, 临时会话           |
|      任意群       |     g\*     | g 意为 group, **不表示群成员**           |
|  任意群的任意群员   |     m\*     | m 意为 member                        |
|  精确群的任意群员   | m123456.\*  | 群 123456 内的任意成员. 同时包含临时会话  |
|    任意临时会话    |     t\*      | t 意为 temp. 必须通过临时会话          |
| 精确群的任意临时会话 | t123456.\*  | 群 123456 内的任意成员. 必须通过临时会话  |
|      任意好友      |     f\*     | f 意为 friend                       |
|      任意用户      |     u\*     | u 意为 user. 任何人在任何环境           |
|     任意陌生人     |     s\*     | s 意为 stranger. 任何人在任何环境       |
|    任意联系对象    |      \*      | 即任何人, 任何群. 不包括控制台           |

### 获取被许可人

在 Kotlin 通常使用 `CommandSender.permitteeId`，在 Java 使用 `CommandSender.getPermitteeId( )`。  
也可以直接构造 [`AbstractPermitteeId`] 的子类。或者在 Kotlin 使用扩展 `User.permitteeId`。

## 权限服务

[`PermissionService`] 承载权限的授权和管理。 Console 的权限系统完全由 [`PermissionService`] 提供支持。
权限服务可以由插件提供（见 [扩展](Extensions.md)）。
在没有任何提供权限服务的插件时会使用 Console 内置实现。

在整个运行时 Console 只会使用同一个权限服务，如果安装多个提供权限服务的插件很有可能导致崩溃。

> 如果运行于 JVM 平台,
> 可以使用 [Karlatemp/LuckPerms-Mirai](https://github.com/Karlatemp/LuckPerms-Mirai)
> 以得到更好的使用体验 (支持权限组, 权限检查状态详细输出等)

### 判断权限

在 Kotlin，在该有扩展的对象上 Console 都为它们实现了扩展。可以使用：
```kotlin
fun Permittee.hasPermission(Permission): Boolean
fun Permission.testPermission(Permittee): Boolean
fun PermitteeId.hasPermission(Permission): Boolean
fun PermissionId.testPermission(Permittee): Boolean
fun Permittee.hasPermission(PermissionId): Boolean
fun Permission.testPermission(PermitteeId): Boolean
// ... 
```

在 Java，请查看 [`PermissionService`] 中的伴生对象。


> 查看使用示例: 
>
> - [Him188/mirai-console-example-plugin](https://github.com/Him188/mirai-console-example-plugin/blob/master/src/main/kotlin/org/example/my/plugin/MyPluginMain.kt#L116)
> - [Karlatemp/mirai-console-permission-service-example (gist)](https://gist.github.com/Karlatemp/38b1491dc033854755c0ec7367ba081b)

### 注册权限

每一条指令都会默认自动创建一个权限。

如果希望手动注册一个其他用途的权限，使用 `PermissionService.register`。

**注意**：
- 权限只能在插件 [启用](Plugins.md#启用) 之后才能注册。否则会得到一个异常。
- 使用 `PermissionService.register` 时对于同一个 [`PermissionId`] 只能注册一次, 如果多次注册会得到一个异常

### 使用内置权限服务指令

**指令**: "/permission", "/perm", "/权限"

使用指令而不带参数可以获取如下用法：
```
/permission cancel <被许可人 ID> <权限 ID>   取消授权一个权限
/permission cancelall <被许可人 ID> <权限 ID>   取消授权一个权限及其所有子权限
/permission listpermissions    查看所有权限列表
/permission permit <被许可人 ID> <权限 ID>   授权一个权限
/permission permittedpermissions <被许可人 ID>   查看被授权权限列表
```

其中, 被许可人 ID 使用 [字符串表示](#字符串表示), 权限 ID 参见 [权限 ID](#权限-id)

----

> 这是文档的最后一个章节。
>
> 返回 [开发文档索引](README.md#mirai-console)

