跳到主要内容

权限控制

PepperBot如何实现权限控制

PepperBot中,可以通过validator型路由,实现路由级别的权限控制

也可以通过available装饰器,实现class级别、method级别的权限控制

validator型路由和available的区别

路由中,我们介绍了validator型路由

validator,针对一组handler或者command,

如果校验通过,该组皆可用

available,针对单个handler method或者command method,

如果校验通过,该handler或者method允许执行

使用validator的典型场景,说人话就是

  • 假设你的用户分为普通用户和超级用户
  • 假设你有10个指令,其中5个所有用户都可以用,另外5个只有超级用户能用
  • 这种场景,就适合用validator实现,设置2个BotRoute,其中一个匹配任意用户,另一个,通过validator校验用户是否为超级用户,给予使用权限
  • 这种场景,如果用available实现,则你需要为那5个(甚至更多)仅有超级用户能用的指令,每个都套一遍available,太麻烦了
[
BotRoute(
commands=[Command1, Command2, ...],
friends="*"
),
BotRoute(
commands=[Command6, Command7, ...],
friends=[is_super_user],
),
]

使用available的典型场景,说人话就是

  • 你仅有少数几个command需要鉴权
  • 你需要method精度的权限控制(不同的method可以应用不同的checker)
    • validator只能class精度

比如,你想对command实现次数限制,普通用户5次,超级用户不限次

也就是说,这个command是所有用户都可以使用的(都可以触发),也就是不进行class精度的鉴权

这种场景,就比较适合对command中,具体实现业务逻辑的method进行鉴权

@as_command(...)
class MyCommand:
@available(rate_limit)
async def initial(self, ...):
...
return self.initial

根据自己的需求,这两者可以结合使用

available的构成

available是一个装饰器,可以向该装饰器传入一个或多个checker

checker可以是同步的,也可以是异步的

每个checker都需要返回一个bool值,表示是否通过校验

TODO 必须显式返回bool值,会通过AST检查checker的代码,如果不是bool,会报错

如果checker抛出异常,会被忽略,也就是视为校验成功

同一个available接受的多个checker之间,是并发执行的

available的可用性

作为一个符合直觉的框架,你可以用同一个available装饰器,同时应用的class_handlerclass_command的class、method上

class handler的class上

@available(checker1, checker2, ...)
class MyHandler:
...

class handler的method上

@available(checker1, checker2, ...)
class MyHandler:

@available(checker3, checker4, ...)
async def group_message(self, ...):
...

class command的class上

@available(checker1, checker2, ...)
@as_command(...)
class MyCommand:
...

或者

@as_command(...)
@available(checker1, checker2, ...)
class MyCommand:
...

不讲究应用available的顺序

class command的method上

@available(checker1, checker2, ...)
@as_command(...)
class MyCommand:

@available(checker3, checker4, ...)
async def initial(self, ...):
...

initial

initial生命周期应用available比较特殊

因为initial是指令的入口,所以会先验证是否满足前缀,

满足前缀,才会进入到initialavailable校验

如果此时指令尚未运行,如果校验失败,会继续对其他指令进行判断,看看是否有可用的指令

如果此时该指令已经运行,如果校验失败,那就失败了,不会继续对其他指令进行判断

因为对于同一种交互策略(锁定用户的话,就是同一个用户,或者锁定群=>同一个群),同一时间,PepperBot只允许有一个运行中的指令

其他method

如果能够触发除initial以外的method,说明当前的指令,已经在运行中了

此时,如果校验成功,会正常执行对应的method

如果校验失败,指令的指向不会改变,也就是说,下一次触发该指令时,还是会执行相同的method

需要注意的是,哪怕available校验失败,也会修改指令的update_time,这个update_time,是用来判断指令是否超时的

生命周期

TODO 不允许对生命周期应用available,在启动PepperBot时,会通过AST检查代码,如果发现有这种情况,会报错

“最难走的路”

假设你应用了validator型路由,对class_command的class上应用了available,对class_command的method上应用了available

那么如图,就是需要经历的流程

checker的参数

一贯的PepperBot风格

  • 只需要列出需要的参数即可,动态注入
  • 必须提供类型

所有checker都会接受的参数

per_event

per_propagation_group

危险

还没想好这个怎么实现

class handlerevent handler(比如group_message)的checker会接受的参数

和正常的event handler一样,查看[事件一览]即可

class commandmethodchecker会接受的参数

和正常的method一样,查看[指令]部分即可

手动用装饰器实现权限控制

这里的概念,比较深入,需要你对python的装饰器有一定的了解

需要手动处理参数

不管在available之前还是之后应用自己的装饰器,都会在检查完availablechecker之后,才会执行自己的装饰器