权限系统
约 2066 字大约 7 分钟
2025-03-27
权限分级
NcatBot 的基本权限机制包括 user
, admin
, root
三级:
user
权限: 使用user
级别功能,user
权限默认分配给所有用户。admin
权限:user
的全部权限以及admin
级别功能.root
权限:admin
的全部权限以及root
级别功能.
权限存储
正常退出后,会保存权限信息到工作目录下 data/rbac.json
文件中,强制关闭则不会保存。
提示
Ctrl+C
关闭(前台模式)或调用 BotClient.exit()
关闭(后台模式)会被视为正常退出。
NcatBot 停止运行时,可以格式化文件后手动修改权限。
root 权限账号只能通过配置项指定。
权限机制
RBAC 机制
NcatBot 使用基于角色的访问控制 (Role-Based Access Control, RBAC) 机制来管理权限。
一个用户可以被赋予多个角色, 每个角色可以被赋予多个权限路径。用户自身也可以被直接赋予权限路径。
授予权限路径时可以选择白名单或黑名单模式。
权限路径
权限路径是一个用点号 (.
) 分隔的字符串, 例如 plugin.demo.read
。
可以在权限路径中使用通配符 (*
) 来表示任意字符串, 例如 plugin.*
表示 plugin
下的所有权限。
注意
通配符作为独立的路径段使用,例如,授予 plugin.*
白名单不会解除 plugin.demo.read
的黑名单限制。必须手动 revoke_permissions_from_user
或 revoke_permissions_from_role
来解除黑名单限制。
同理,授予 plugin.demo
白名单不会解除 plugin.*
的黑名单限制。
权限判定
使用 check_permission
判定用户是否拥有某权限路径时,会收集用户所有的角色和用户自身的权限路径, 然后按以下顺序进行检查:
- 检查精确黑名单路径,如果匹配则拒绝。
- 检查精确白名单路径,如果匹配则允许。
- 检查通配符黑名单路径,如果匹配则拒绝。
- 检查通配符白名单路径,如果匹配则允许。
联动
权限和功能
通过功能注册的功能会自动绑定权限, 权限路径为 <plugin_name>.<func_name>
。
可以使用上面的接口控制权限路径。
内置权限管理命令
内置了授予 admin 角色命令 来管理 admin 角色。这个命令是仅限 root
执行的。
统一注册器
使用 admin_filter 的命令会过滤掉没有 admin
角色 的用户。
权限接口
所有的 NcatBotPlugin
都持有 self.rbac_manager
权限管理实例,通过它可以来操作权限系统。
状态量 中也持有一个 rbac_manager
实例。
user_exists
函数原型
class RBACManager:
...
def user_exists(self, user_name: str) -> bool:
""""
判断用户是否存在。
Args:
user_name: 用户名
Returns:
bool,存在为 True。
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
if self.rbac_manager.user_exists("alice"):
print("User 'alice' exists.")
role_exists
函数原型
class RBACManager:
...
def role_exists(self, role_name: str) -> bool:
"""
判断角色是否存在。
Args:
role_name: 角色名
Returns:
bool,存在为 True。
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
if self.rbac_manager.role_exists("auditor"):
print("Role 'auditor' exists.")
permission_path_exists
函数原型
class RBACManager:
...
def permission_path_exists(self, permissions_path: str) -> bool:
"""
判断权限路径是否已在权限 Trie 中注册。
Args:
permissions_path: 权限路径(大小写敏感)
Returns:
bool,存在为 True。
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
if not self.rbac_manager.permission_path_exists("plugin.demo.read"):
self.rbac_manager.add_permissions("plugin.demo.read")
check_permission
检查用户是否拥有某权限路径。
函数原型
from typing import Literal
class RBACManager:
...
def check_permission(self, user_name: str, path: str, create_user_if_not_exists: bool = True) -> bool:
"""
检查用户是否拥有某权限路径。
Args:
user_name: 用户名
path: 权限路径
create_user_if_not_exists: 用户不存在时是否自动创建(默认 True)
Returns:
bool,允许为 True。
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
# 常规用法:
allowed = self.rbac_manager.check_permission("alice", "plugin.demo.read")
if not allowed:
print("no permission")
# 严格控制用户生命周期时:
try:
allowed = self.rbac_manager.check_permission("bob", "plugin.demo.read", create_user_if_not_exists=False)
except IndexError:
print("user not exists")
user_has_role
函数原型
class RBACManager:
...
def user_has_role(self, user_name: str, role_name: str) -> bool:
"""
判断用户是否拥有指定角色。
Args:
user_name: 用户名
role_name: 角色名
Returns:
bool,拥有为 True。
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
if not self.rbac_manager.user_has_role("alice", "auditor"):
print("alice has no 'auditor' role")
add_role
函数原型
class RBACManager:
...
def add_role(self, role_name: str, ignore_if_exists: bool = True):
"""
创建新角色。
Args:
role_name: 角色名
ignore_if_exists: 若已存在是否忽略(默认 True)
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.add_role("auditor")
add_user
函数原型
class RBACManager:
...
def add_user(self, user_name: str, role_list: list[str] | None = None, ignore_if_exists: bool = True):
"""
创建新用户,可选地赋予一组角色。
Args:
user_name: 用户名
role_list: 角色列表(可选)
ignore_if_exists: 已存在时是否忽略(默认 True)
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.add_user("alice")
assign_role_to_user
函数原型
class RBACManager:
...
def assign_role_to_user(self, user_name: str, role_name: str, create_user_if_not_exists: bool = True):
"""
为用户赋予角色。
Args:
user_name: 用户名
role_name: 角色名
create_user_if_not_exists: 用户不存在是否自动创建(默认 True)
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.assign_role_to_user("alice", "auditor")
assign_permissions_to_role
函数原型
from typing import Literal
class RBACManager:
...
def assign_permissions_to_role(self, role_name: str, permissions_path: str, mode: Literal["white", "black"] = "white", create_path_if_not_exists: bool = True):
"""
为角色分配权限(白名单或黑名单)。
Args:
role_name: 角色名
permissions_path: 权限路径,支持通配符
mode: "white" 或 "black"(默认 "white")
create_path_if_not_exists: 权限不存在时是否自动创建(默认 True)
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.add_role("auditor")
self.rbac_manager.add_role("superadmin")
self.rbac_manager.add_permissions("plugin.demo.read")
self.rbac_manager.add_permissions("plugin.demo.write")
self.rbac_manager.assign_permissions_to_role("auditor", "plugin.demo.read", mode="white") # 只授予读取权限
self.rbac_manager.assign_permissions_to_role("superadmin", "plugin.demo.*", mode="white") # 授予所有 plugin.demo 下的权限
assign_permissions_to_user
当 assign_permissions_to_role
的结果和 assign_permissions_to_user
的结果冲突时,用户权限优先级更高。
函数原型
from typing import Literal
class RBACManager:
...
def assign_permissions_to_user(self, user_name: str, permissions_path: str, mode: Literal["white", "black"] = "white", create_path_if_not_exists: bool = True):
"""
为用户直接分配权限(白/黑名单)。
Args:
user_name: 用户名
permissions_path: 权限路径,支持通配符
mode: "white" 或 "black"(默认 "white")
create_path_if_not_exists: 权限不存在时是否自动创建(默认 True)
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.add_permissions("plugin.demo.write")
self.rbac_manager.assign_permissions_to_user("alice", "plugin.demo.write", mode="white")
unassign_role_to_user
函数原型
class RBACManager:
...
def unassign_role_to_user(self, user_name: str, role_name: str):
"""
撤销用户的某个角色。
Args:
user_name: 用户名
role_name: 角色名
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.unassign_role_to_user("alice", "auditor")
ban_permissions_to_role
等价于将该权限路径以黑名单形式分配给角色。
函数原型
class RBACManager:
...
def ban_permissions_to_role(self, role_name: str, permissions_path: str):
"""
从角色撤销某权限。
Args:
role_name: 角色名
permissions_path: 权限路径,支持通配符
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.ban_permissions_to_role("auditor", "plugin.demo.read")
ban_permissions_to_user
等价于将该权限路径以黑名单形式分配给用户。
函数原型
class RBACManager:
...
def ban_permissions_to_user(self, user_name: str, permissions_path: str):
"""
从用户撤销某权限。
Args:
user_name: 用户名
permissions_path: 权限路径,支持通配符
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.ban_permissions_to_user("alice", "plugin.demo.write")
revoke_permissions_from_role
函数原型
class RBACManager:
...
def revoke_permissions_from_role(self, role_name: str, permissions_path: str):
"""
撤销角色的某个权限分配(无论白名单或黑名单)。
Args:
role_name: 角色名
permissions_path: 权限路径,支持通配符
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.revoke_permissions_from_role("auditor", "plugin.demo.read")
revoke_permissions_from_user
函数原型
class RBACManager:
...
def revoke_permissions_from_user(self, user_name: str, permissions_path: str):
"""
撤销用户的某个权限分配(无论白名单或黑名单)。
Args:
user_name: 用户名
permissions_path: 权限路径,支持通配符
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.revoke_permissions_from_user("alice", "plugin.demo.write")
add_role_inheritance
函数原型
class RBACManager:
...
def add_role_inheritance(self, role: str, inherited_role: str):
"""
设置角色继承关系(role 继承 inherited_role 的权限)。
Args:
role: 子角色
inherited_role: 被继承的父角色
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def on_load(self):
# 第一次创建时才会发生 "无角色"
if not self.rbac_manager.role_exists("auditor"):
self.rbac_manager.add_role("auditor")
self.rbac_manager.add_role("superadmin")
self.rbac_manager.add_role_inheritance("superadmin", "auditor") # superadmin 继承 auditor 的权限
add_permissions
函数原型
class RBACManager:
...
def add_permissions(self, permissions_path: str, ignore_if_exists: bool = True):
"""
向权限 Trie 注册一个权限路径。
Args:
permissions_path: 权限路径
ignore_if_exists: 已存在时是否忽略(默认 True)
"""
...
示例用法
class MyPlugin(NcatBotPlugin):
...
async def some_method(self):
self.rbac_manager.add_permissions("plugin.demo.read")
版权所有
版权归属:huan-yp