简介

在 Casbin 中, 访问控制模型被抽象为基于PERM(Policy(策略), Effect(效果), Request(请求), Matcher(匹配器))的一个文件。 因此,切换或升级项目的授权机制与修改配置一样简单。 您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中获得RBAC角色和ABAC属性,并共享一组policy规则

PERM 模型

PERM(Policy, Effect, Request, Matchers)模型很简单, 但是反映了权限的本质 – 访问控制

  • Policy: 定义权限的规则
  • Effect: 定义组合了多个 Policy 之后的结果, allow/deny
  • Request: 访问请求, 也就是谁想操作什么
  • Matcher: 判断 Request 是否满足 Policy

image.png

MODEL例子

  1. [request_definition] // 用于request的定义,它明确了e.Enforce(...)函数中参数的含义
  2. r = sub, obj, act // sub:用户/角色 obj:资源 act:操作
  3. [policy_definition] // 对policy的定义,例如policy的格式为:p, alice, data1, read
  4. p = sub, obj, act
  5. [policy_effect] // 对policy生效范围的定义
  6. e = some(where (p.eft == allow)) //表示有任意一条 policy rule 满足, 则最终结果为 allow
  7. [matchers] // 定义了 request 和 policy 匹配的方式, p.eft 是 allow 还是 deny, 就是基于此来决定的
  8. m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
  9. [role_definition]
  10. g = _, _ // _, _ 表示用户和角色
  11. g2 = _, _, _ // _, _, _ 表示用户, 角色, 域(也就是租户)

ACL

  1. [request_definition]
  2. r = sub, obj, act
  3. [policy_definition]
  4. p = sub, obj, act
  5. [policy_effect]
  6. e = some(where (p.eft == allow))
  7. [matchers]
  8. m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
  9. -------------------------------------------------------------------
  10. p alice data1 read
  11. p bob data2 write

ACL带超级用户

  1. [request_definition]
  2. r = sub, obj, act
  3. [policy_definition]
  4. p = sub, obj, act
  5. [policy_effect]
  6. e = some(where (p.eft == allow))
  7. [matchers]
  8. m = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"
  9. -------------------------------------------------------------------
  10. p alice data1 read
  11. p bob data2 write

ACL无用户

  1. [request_definition]
  2. r = obj, act
  3. [policy_definition]
  4. p = obj, act
  5. [policy_effect]
  6. e = some(where (p.eft == allow))
  7. [matchers]
  8. m = r.obj == p.obj && r.act == p.act
  9. ---------------------------------------------------------------------------
  10. p data1 read
  11. p data2 write

RBAC

  1. [request_definition]
  2. r = sub, obj, act
  3. [policy_definition]
  4. p = sub, obj, act
  5. [role_definition]
  6. g = _, _
  7. [policy_effect]
  8. e = some(where (p.eft == allow))
  9. [matchers]
  10. m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
  11. ---------------------------------------------------------------------------
  12. p, alice, data1, read
  13. p, bob, data2, write
  14. p, data2_admin, data2, read
  15. p, data2_admin, data2, write
  16. g, alice, data2_admin

RBAC支持域/用户

  1. [request_definition]
  2. r = sub, dom, obj, act
  3. [policy_definition]
  4. p = sub, dom, obj, act
  5. [role_definition]
  6. g = _, _, _
  7. [policy_effect]
  8. e = some(where (p.eft == allow))
  9. [matchers]
  10. m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
  11. ---------------------------------------------------------------------------
  12. p, admin, domain1, data1, read
  13. p, admin, domain1, data1, write
  14. p, admin, domain2, data2, read
  15. p, admin, domain2, data2, write
  16. g, alice, admin, domain1
  17. g, bob, admin, domain2

RESTful

  1. [request_definition]
  2. r = sub, obj, act
  3. [policy_definition]
  4. p = sub, obj, act
  5. [policy_effect]
  6. e = some(where (p.eft == allow))
  7. [matchers]
  8. m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
  9. ---------------------------------------------------------------------------
  10. p alice /alice_data/* GET
  11. p alice /alice_data/resource1 POST
  12. p bob /alice_data/resource2 GET
  13. p bob /bob_data/* POST
  14. p cathy /cathy_data (GET)|(POST)

MODEL语法

Request定义

[request_definition]部分用于request的定义,它明确了e.Enforce(...)函数中参数的含义,同时e.Enforce(...)的参数个数,顺序必须与这里定义的保持一致

  1. [request_definition]
  2. r = sub, obj, act

sub, obj, act表示经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action)

Policy定义

[policy_definition]部分是对policy的定义,以下文的 model 配置为例:

  1. [policy_definition]
  2. p = sub, obj, act
  3. p2 = sub, act

这些是我们对policy规则的具体描述

  1. p, alice, data1, read
  2. p2, bob, write-all-objects

policy部分的每一行称之为一个策略规则
注1: 当前只支持形如p的单个policy定义, 形如p2类型的尚未支持。 通常情况下, 用户无需使用多个 policy 定义
注2:[policy_definition]中的p,必须与数据库中的保持一致,比如这里只定义了一个p,而数据库中有多个(p,p1…),是会报错的,如下:
image.png

role_definition定义

[role_definition]是RBAC角色继承关系的定义

  1. [role_definition]
  2. g = _, _, _ // 分别代表,用户 角色 域
  3. [role_definition]
  4. g = _, _ //分别代表,用户 角色

用户-角色

  1. // 举例:policy如下
  2. p, data2_admin, data2, read
  3. g, alice, data2_admin
  4. //策略规则
  5. [matchers]
  6. m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

上诉的含义为:data2_admin角色有data2的read权限,alice属于data2_admin,那么alice也就对data2有read权限

用户-角色-域

  1. p, admin, tenant1, data1, read
  2. p, admin, tenant2, data2, read
  3. g, alice, admin, tenant1
  4. g, alice, user, tenant2
  5. [matchers]
  6. m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act

意思:alice用户属于admin(有效域为tenant1)与user组(有效域为tenant2),admin角色在tenant1域中,对data1有read权限,在tenant2域中对data2有read权限,总结:alice在tenant1中对data1有read权限。
注1 :[role_definition]中的g,必须与数据库中的保持一致,比如这里只定义了一个g,而数据库中有多个(g,g1…),是会报错的

Policy effect定义

[policy_effect]部分是对policy生效范围的定义, 原语定义了当多个policy rule同时匹配访问请求request时,该如何对多个决策结果进行集成以实现统一决策。 以下示例展示了一个只有一条规则生效,其余都被拒绝的情况:默认使用这条就ok

  1. [policy_effect]
  2. e = some(where (p.eft == allow))

Matchers

[matchers]原语定义了策略规则如何与访问请求进行匹配的匹配器,其本质上是布尔表达式

  1. [matchers]
  2. m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

这是一个简单的例子,该Matcher原语表示,访问请求request中的subject、object、action三元组应与策略规则policy rule中的subject、object、action三元组分别对应相同。
Matcher原语支持+、 -、 、 /等算数运算符,==,、!=、 >、 <等关系运算符以及&& (与)、|| (或)、 ! (非)等逻辑运算符。
: 虽然可以像其他原语一样的编写多个类似于m1,m2的matcher, 当前只支持一个有效的 matcherm。 通常情况下,您可以在一个matcher中使用上文提到的逻辑运算符来实现复杂的逻辑判断, 因而我们认为目前不需要支持多个matcher。
matcher的强大与灵活之处在于您甚至可以在matcher中定义函数,这些函数可以是内置函数或自定义的函数。当前支持的内置函数如下:
*优先级

  1. 可以使用()来表达一个子表达式
  2. [matchers]
  3. m = g(r.sub, p.sub) && r.obj == p.obj && (r.act == p.act && x == y)

keyMatch(arg1, arg2)
参数 arg1 (请求)是一个 URL 路径,例如/alice_data/resource1,参数 arg2 (策略定义)可以是URL路径或者是一个*模式,例如/alice_data/*。此函数返回 arg1是否与 arg2 匹配。

  1. [matchers]
  2. m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)

keyMatch2(arg1, arg2)
参数 arg1 是一个 URL 路径,例如/alice_data/resource1,参数 arg2 可以是 URL 路径或者是一个:模式,例如/alice_data/:resource。此函数返回 arg1 是否与 arg2 匹配。

  1. [matchers]
  2. m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)

keyMatch3(arg1, arg2)
参数 arg1 是一个 URL 路径,例如/alice_data/resource1,参数 arg2 可以是 URL 路径或者是一个{}模式,例如/alice_data/{resource}。此函数返回 arg1 是否与 arg2 匹配。

  1. [matchers]
  2. m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)

keyMatch4(arg1, arg2)
参数 arg1 是一个 URL 路径,例如/alice_data/123/book/123,参数 arg2 可以是 URL 路径或者是一个{}模式,例如/alice_data/{id}/book/{id}。此函数返回 arg1 是否与 arg2 匹配。

  1. [matchers]
  2. m = r.sub == p.sub && keyMatch3(r.obj, p.obj) && regexMatch(r.act, p.act)

regexMatch(arg1, arg2)
arg1 可以是任何字符串。arg2 是一个正则表达式。它返回 arg1 是否匹配 arg2。

  1. [matchers]
  2. m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
  3. p cathy /cathy_data (GET)|(POST)

ipMatch(arg1, arg2)
arg1 是一个 IP 地址, 如192.168.2.123。arg2 可以是 IP 地址或 CIDR, 如192.168.2. 0/24。它返回 arg1 是否匹配 arg2。

  1. [matchers]
  2. m = ipMatch(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

自定义函数

  1. func KeyMatch(key1 string, key2 string) bool {
  2. i := strings.Index(key2, "*")
  3. if i == -1 {
  4. return key1 == key2
  5. }
  6. if len(key1) > i {
  7. return key1[:i] == key2[:i]
  8. }
  9. return key1 == key2[:i]
  10. }
  11. func KeyMatchFunc(args ...interface{}) (interface{}, error) {
  12. name1 := args[0].(string)
  13. name2 := args[1].(string)
  14. return KeyMatch(name1, name2), nil
  15. }
  16. e.AddFunction("my_func", KeyMatchFunc)
  17. ------------------------------------------------------------------------------
  18. [matchers]
  19. m = r.sub == p.sub && my_func(r.obj, p.obj) && r.act == p.act

POLICY

policy为定义的策略,格式需要与[policy_definition]中定义的保持一致