feat(permission_api): 增强权限API文档,添加详细注释和示例

fix(system_management): 修复系统命令执行时缺失用户信息的错误处理
This commit is contained in:
minecraft1024a
2025-11-29 20:20:59 +08:00
parent feb4e7155e
commit ad0f495911
3 changed files with 347 additions and 26 deletions

View File

@@ -1,80 +1,291 @@
"""纯异步权限API定义。所有外部调用方必须使用 await。"""
"""
纯异步权限API定义。
from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum
from typing import Any
这个模块提供了一套完整的权限管理系统,用于控制用户对各种功能的访问权限。
所有外部调用方必须使用 await 关键字因为这是异步API。
主要组件:
- PermissionLevel: 权限等级枚举
- PermissionNode: 权限节点数据类,描述单个权限项
- UserInfo: 用户信息数据类,标识用户身份
- IPermissionManager: 权限管理器抽象接口
- PermissionAPI: 对外暴露的权限API封装类
"""
from abc import ABC, abstractmethod # ABC: 抽象基类abstractmethod: 抽象方法装饰器
from dataclasses import dataclass # dataclass: 自动生成 __init__, __repr__ 等方法的装饰器
from enum import Enum # Enum: 枚举类型基类
from typing import Any # Any: 表示任意类型
from src.common.logger import get_logger
logger = get_logger(__name__)
logger = get_logger(__name__) # 获取当前模块的日志记录器
class PermissionLevel(Enum):
MASTER = "master"
"""
权限等级枚举类。
定义了系统中的权限等级,目前只有 MASTER管理员/主人)级别。
MASTER 用户拥有最高权限,可以执行所有操作。
"""
MASTER = "master" # 管理员/主人权限
@dataclass
class PermissionNode:
node_name: str
description: str
plugin_name: str
default_granted: bool = False
"""
权限节点数据类。
每个权限节点代表一个具体的权限项,例如"发送消息""管理用户"等。
属性:
node_name: 权限节点名称,例如 "plugin.chat.send_message"
description: 权限描述,用于向用户展示这个权限的用途
plugin_name: 注册这个权限的插件名称
default_granted: 是否默认授予所有用户False 表示需要显式授权
"""
node_name: str # 权限节点唯一标识名
description: str # 权限的人类可读描述
plugin_name: str # 所属插件名称
default_granted: bool = False # 默认是否授予(默认不授予)
@dataclass
class UserInfo:
platform: str
user_id: str
"""
用户信息数据类。
用于唯一标识一个用户,通过平台+用户ID的组合确定用户身份。
属性:
platform: 用户所在平台,例如 "qq", "telegram", "discord"
user_id: 用户在该平台上的唯一标识ID
"""
platform: str # 平台标识,如 "qq", "telegram"
user_id: str # 用户ID
def __post_init__(self):
"""
dataclass 的后初始化钩子。
确保 user_id 始终是字符串类型,即使传入的是数字也会被转换。
这样可以避免类型不一致导致的比较问题。
"""
self.user_id = str(self.user_id)
class IPermissionManager(ABC):
"""
权限管理器抽象接口Interface
这是一个抽象基类,定义了权限管理器必须实现的所有方法。
具体的权限管理实现类需要继承此接口并实现所有抽象方法。
使用抽象接口的好处:
1. 解耦PermissionAPI 不需要知道具体的实现细节
2. 可测试:可以轻松创建 Mock 实现用于测试
3. 可替换:可以随时更换不同的权限管理实现
"""
@abstractmethod
async def check_permission(self, user: UserInfo, permission_node: str) -> bool: ...
async def check_permission(self, user: UserInfo, permission_node: str) -> bool:
"""
检查用户是否拥有指定权限。
Args:
user: 要检查的用户信息
permission_node: 权限节点名称
Returns:
bool: True 表示用户拥有该权限False 表示没有
"""
...
@abstractmethod
async def is_master(self, user: UserInfo) -> bool: ... # 同步快速判断
async def is_master(self, user: UserInfo) -> bool:
"""
检查用户是否是管理员/主人。
管理员拥有最高权限,通常绕过所有权限检查。
Args:
user: 要检查的用户信息
Returns:
bool: True 表示是管理员False 表示不是
"""
...
@abstractmethod
async def register_permission_node(self, node: PermissionNode) -> bool: ...
async def register_permission_node(self, node: PermissionNode) -> bool:
"""
注册一个新的权限节点。
插件在加载时会调用此方法注册自己需要的权限。
Args:
node: 要注册的权限节点信息
Returns:
bool: True 表示注册成功False 表示失败(可能是重复注册)
"""
...
@abstractmethod
async def grant_permission(self, user: UserInfo, permission_node: str) -> bool: ...
async def grant_permission(self, user: UserInfo, permission_node: str) -> bool:
"""
授予用户指定权限。
Args:
user: 目标用户信息
permission_node: 要授予的权限节点名称
Returns:
bool: True 表示授权成功False 表示失败
"""
...
@abstractmethod
async def revoke_permission(self, user: UserInfo, permission_node: str) -> bool: ...
async def revoke_permission(self, user: UserInfo, permission_node: str) -> bool:
"""
撤销用户的指定权限。
Args:
user: 目标用户信息
permission_node: 要撤销的权限节点名称
Returns:
bool: True 表示撤销成功False 表示失败
"""
...
@abstractmethod
async def get_user_permissions(self, user: UserInfo) -> list[str]: ...
async def get_user_permissions(self, user: UserInfo) -> list[str]:
"""
获取用户拥有的所有权限列表。
Args:
user: 目标用户信息
Returns:
list[str]: 用户拥有的权限节点名称列表
"""
...
@abstractmethod
async def get_all_permission_nodes(self) -> list[PermissionNode]: ...
async def get_all_permission_nodes(self) -> list[PermissionNode]:
"""
获取系统中所有已注册的权限节点。
Returns:
list[PermissionNode]: 所有权限节点的列表
"""
...
@abstractmethod
async def get_plugin_permission_nodes(self, plugin_name: str) -> list[PermissionNode]: ...
async def get_plugin_permission_nodes(self, plugin_name: str) -> list[PermissionNode]:
"""
获取指定插件注册的所有权限节点。
Args:
plugin_name: 插件名称
Returns:
list[PermissionNode]: 该插件注册的权限节点列表
"""
...
class PermissionAPI:
"""
权限API封装类。
这是对外暴露的权限操作接口,插件和其他模块通过这个类来进行权限相关操作。
它封装了底层的 IPermissionManager提供更简洁的调用方式。
使用方式:
from src.plugin_system.apis.permission_api import permission_api
# 检查权限
has_perm = await permission_api.check_permission("qq", "12345", "chat.send")
# 检查是否是管理员
is_admin = await permission_api.is_master("qq", "12345")
设计模式:
这是一个单例模式的变体,模块级别的 permission_api 实例供全局使用。
"""
def __init__(self):
self._permission_manager: IPermissionManager | None = None
"""
初始化 PermissionAPI。
初始时权限管理器为 None需要在系统启动时通过 set_permission_manager 设置。
"""
self._permission_manager: IPermissionManager | None = None # 底层权限管理器实例
def set_permission_manager(self, manager: IPermissionManager):
"""
设置权限管理器实例。
这个方法应该在系统启动时被调用,注入具体的权限管理器实现。
Args:
manager: 实现了 IPermissionManager 接口的权限管理器实例
"""
self._permission_manager = manager
def _ensure_manager(self):
"""
确保权限管理器已设置(内部辅助方法)。
如果权限管理器未设置,抛出 RuntimeError 异常。
这是一个防御性编程措施,帮助开发者快速发现配置问题。
Raises:
RuntimeError: 当权限管理器未设置时
"""
if self._permission_manager is None:
raise RuntimeError("权限管理器未设置,请先调用 set_permission_manager")
async def check_permission(self, platform: str, user_id: str, permission_node: str) -> bool:
"""
检查用户是否拥有指定权限。
这是最常用的权限检查方法,在执行需要权限的操作前调用。
Args:
platform: 用户所在平台(如 "qq", "telegram"
user_id: 用户ID
permission_node: 要检查的权限节点名称
Returns:
bool: True 表示用户拥有权限False 表示没有
Example:
if await permission_api.check_permission("qq", "12345", "admin.ban_user"):
# 执行封禁操作
pass
"""
self._ensure_manager()
if not self._permission_manager:
return False
return await self._permission_manager.check_permission(UserInfo(platform, user_id), permission_node)
async def is_master(self, platform: str, user_id: str) -> bool:
"""
检查用户是否是管理员/主人。
管理员是系统的最高权限用户,通常在配置文件中指定。
Args:
platform: 用户所在平台
user_id: 用户ID
Returns:
bool: True 表示是管理员False 表示不是
"""
self._ensure_manager()
if not self._permission_manager:
return False
@@ -87,11 +298,35 @@ class PermissionAPI:
plugin_name: str,
default_granted: bool = False,
*,
allow_relative: bool = True,
allow_relative: bool = True, # 仅关键字参数,目前未使用,预留给相对权限名功能
) -> bool:
"""
注册一个新的权限节点。
插件在初始化时应调用此方法注册自己需要的权限节点。
Args:
node_name: 权限节点名称,建议使用 "插件名.功能.操作" 的格式
description: 权限描述,向用户解释这个权限的作用
plugin_name: 注册此权限的插件名称
default_granted: 是否默认授予所有用户(默认 False需要显式授权
allow_relative: 预留参数,是否允许相对权限名(目前未使用)
Returns:
bool: True 表示注册成功False 表示失败
Example:
await permission_api.register_permission_node(
node_name="my_plugin.chat.send_image",
description="允许发送图片消息",
plugin_name="my_plugin",
default_granted=True # 所有用户默认都能发图片
)
"""
self._ensure_manager()
original_name = node_name
original_name = node_name # 保存原始名称(预留给相对路径处理)
# 创建权限节点对象
node = PermissionNode(node_name, description, plugin_name, default_granted)
if not self._permission_manager:
return False
@@ -100,28 +335,90 @@ class PermissionAPI:
async def grant_permission(self, platform: str, user_id: str, permission_node: str) -> bool:
"""
授予用户指定权限。
通常由管理员调用,给某个用户赋予特定权限。
Args:
platform: 目标用户所在平台
user_id: 目标用户ID
permission_node: 要授予的权限节点名称
Returns:
bool: True 表示授权成功False 表示失败
Example:
# 授予用户管理权限
await permission_api.grant_permission("qq", "12345", "admin.manage_users")
"""
self._ensure_manager()
if not self._permission_manager:
return False
return await self._permission_manager.grant_permission(UserInfo(platform, user_id), permission_node)
async def revoke_permission(self, platform: str, user_id: str, permission_node: str) -> bool:
"""
撤销用户的指定权限。
通常由管理员调用,移除某个用户的特定权限。
Args:
platform: 目标用户所在平台
user_id: 目标用户ID
permission_node: 要撤销的权限节点名称
Returns:
bool: True 表示撤销成功False 表示失败
"""
self._ensure_manager()
if not self._permission_manager:
return False
return await self._permission_manager.revoke_permission(UserInfo(platform, user_id), permission_node)
async def get_user_permissions(self, platform: str, user_id: str) -> list[str]:
"""
获取用户拥有的所有权限列表。
可用于展示用户的权限信息,或进行批量权限检查。
Args:
platform: 目标用户所在平台
user_id: 目标用户ID
Returns:
list[str]: 用户拥有的所有权限节点名称列表
Example:
perms = await permission_api.get_user_permissions("qq", "12345")
print(f"用户拥有以下权限: {perms}")
"""
self._ensure_manager()
if not self._permission_manager:
return []
return await self._permission_manager.get_user_permissions(UserInfo(platform, user_id))
async def get_all_permission_nodes(self) -> list[dict[str, Any]]:
"""
获取系统中所有已注册的权限节点。
返回所有插件注册的权限节点信息,可用于权限管理界面展示。
Returns:
list[dict]: 权限节点信息列表,每个字典包含:
- node_name: 权限节点名称
- description: 权限描述
- plugin_name: 所属插件名称
- default_granted: 是否默认授予
Note:
返回字典而非 PermissionNode 对象便于序列化和API响应。
"""
self._ensure_manager()
if not self._permission_manager:
return []
nodes = await self._permission_manager.get_all_permission_nodes()
# 将 PermissionNode 对象转换为字典,便于序列化
return [
{
"node_name": n.node_name,
@@ -133,10 +430,22 @@ class PermissionAPI:
]
async def get_plugin_permission_nodes(self, plugin_name: str) -> list[dict[str, Any]]:
"""
获取指定插件注册的所有权限节点。
用于查看某个特定插件定义了哪些权限。
Args:
plugin_name: 插件名称
Returns:
list[dict]: 该插件的权限节点信息列表,格式同 get_all_permission_nodes
"""
self._ensure_manager()
if not self._permission_manager:
return []
nodes = await self._permission_manager.get_plugin_permission_nodes(plugin_name)
# 将 PermissionNode 对象转换为字典
return [
{
"node_name": n.node_name,
@@ -148,4 +457,9 @@ class PermissionAPI:
]
# ============================================================
# 模块级单例实例
# ============================================================
# 全局权限API实例供其他模块导入使用
# 使用方式: from src.plugin_system.apis.permission_api import permission_api
permission_api = PermissionAPI()

View File

@@ -68,7 +68,7 @@ class PlusCommand(ABC):
self.log_prefix = "[PlusCommand]"
# chat_stream 会在运行时被 bot.py 设置
self.chat_stream: "ChatStream | None" = None
self.chat_stream: "ChatStream"
# 解析命令参数
self._parse_command()

View File

@@ -31,6 +31,7 @@ from src.plugin_system.base.component_types import (
from src.plugin_system.base.config_types import ConfigField
from src.plugin_system.base.plus_command import PlusCommand
from src.plugin_system.utils.permission_decorators import require_permission
from src.plugin_system.apis.permission_api import permission_api
logger = get_logger("SystemManagement")
@@ -48,9 +49,15 @@ class SystemCommand(PlusCommand):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@require_permission("access", deny_message="❌ 你没有权限使用此命令")
async def execute(self, args: CommandArgs) -> tuple[bool, str | None, bool]:
"""执行系统管理命令"""
if not self.chat_stream.user_info:
logger.error("chat_stream缺失用户信息,请报告开发者")
return False, "chat_stream缺失用户信息,请报告开发者", True
has_permission = await permission_api.check_permission(platform=self.chat_stream.platform,user_id=self.chat_stream.user_info.user_id,permission_node="access")
if has_permission:
logger.warning("没有权限使用此命令")
return False, "没有权限使用此命令", True
if args.is_empty:
await self._show_help("all")
return True, "显示帮助信息", True