简介
在 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
MODEL例子
[request_definition] // 用于request的定义,它明确了e.Enforce(...)函数中参数的含义
r = sub, obj, act // sub:用户/角色 obj:资源 act:操作
[policy_definition] // 对policy的定义,例如policy的格式为:p, alice, data1, read
p = sub, obj, act
[policy_effect] // 对policy生效范围的定义
e = some(where (p.eft == allow)) //表示有任意一条 policy rule 满足, 则最终结果为 allow
[matchers] // 定义了 request 和 policy 匹配的方式, p.eft 是 allow 还是 deny, 就是基于此来决定的
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
[role_definition]
g = _, _ // _, _ 表示用户和角色
g2 = _, _, _ // _, _, _ 表示用户, 角色, 域(也就是租户)
ACL
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
-------------------------------------------------------------------
p alice data1 read
p bob data2 write
ACL带超级用户
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"
-------------------------------------------------------------------
p alice data1 read
p bob data2 write
ACL无用户
[request_definition]
r = obj, act
[policy_definition]
p = obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.obj == p.obj && r.act == p.act
---------------------------------------------------------------------------
p data1 read
p data2 write
RBAC
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
---------------------------------------------------------------------------
p, alice, data1, read
p, bob, data2, write
p, data2_admin, data2, read
p, data2_admin, data2, write
g, alice, data2_admin
RBAC支持域/用户
[request_definition]
r = sub, dom, obj, act
[policy_definition]
p = sub, dom, obj, act
[role_definition]
g = _, _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
---------------------------------------------------------------------------
p, admin, domain1, data1, read
p, admin, domain1, data1, write
p, admin, domain2, data2, read
p, admin, domain2, data2, write
g, alice, admin, domain1
g, bob, admin, domain2
RESTful
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
---------------------------------------------------------------------------
p alice /alice_data/* GET
p alice /alice_data/resource1 POST
p bob /alice_data/resource2 GET
p bob /bob_data/* POST
p cathy /cathy_data (GET)|(POST)
MODEL语法
Request定义
[request_definition]
部分用于request的定义,它明确了e.Enforce(...)
函数中参数的含义,同时e.Enforce(...)的参数个数,顺序必须与这里定义的保持一致
。
[request_definition]
r = sub, obj, act
sub, obj, act
表示经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action)
Policy定义
[policy_definition]
部分是对policy的定义,以下文的 model 配置为例:
[policy_definition]
p = sub, obj, act
p2 = sub, act
这些是我们对policy规则的具体描述
p, alice, data1, read
p2, bob, write-all-objects
policy部分的每一行称之为一个策略规则
注1: 当前只支持形如p
的单个policy定义, 形如p2
类型的尚未支持。 通常情况下, 用户无需使用多个 policy 定义
注2:[policy_definition]
中的p,必须与数据库中的保持一致,比如这里只定义了一个p,而数据库中有多个(p,p1…),是会报错的,如下:
role_definition定义
[role_definition]是RBAC角色继承关系的定义
[role_definition]
g = _, _, _ // 分别代表,用户 角色 域
[role_definition]
g = _, _ //分别代表,用户 角色
用户-角色
// 举例:policy如下
p, data2_admin, data2, read
g, alice, data2_admin
//策略规则
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
上诉的含义为:data2_admin角色有data2的read权限,alice属于data2_admin,那么alice也就对data2有read权限
用户-角色-域
p, admin, tenant1, data1, read
p, admin, tenant2, data2, read
g, alice, admin, tenant1
g, alice, user, tenant2
[matchers]
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
[policy_effect]
e = some(where (p.eft == allow))
Matchers
[matchers]
原语定义了策略规则如何与访问请求进行匹配的匹配器,其本质上是布尔表达式
[matchers]
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中定义函数,这些函数可以是内置函数或自定义的函数。当前支持的内置函数如下:
*优先级
可以使用()来表达一个子表达式
[matchers]
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 匹配。
[matchers]
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 匹配。
[matchers]
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 匹配。
[matchers]
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 匹配。
[matchers]
m = r.sub == p.sub && keyMatch3(r.obj, p.obj) && regexMatch(r.act, p.act)
regexMatch(arg1, arg2)
arg1 可以是任何字符串。arg2 是一个正则表达式。它返回 arg1 是否匹配 arg2。
[matchers]
m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
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。
[matchers]
m = ipMatch(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
自定义函数
func KeyMatch(key1 string, key2 string) bool {
i := strings.Index(key2, "*")
if i == -1 {
return key1 == key2
}
if len(key1) > i {
return key1[:i] == key2[:i]
}
return key1 == key2[:i]
}
func KeyMatchFunc(args ...interface{}) (interface{}, error) {
name1 := args[0].(string)
name2 := args[1].(string)
return KeyMatch(name1, name2), nil
}
e.AddFunction("my_func", KeyMatchFunc)
------------------------------------------------------------------------------
[matchers]
m = r.sub == p.sub && my_func(r.obj, p.obj) && r.act == p.act
POLICY
policy为定义的策略,格式需要与[policy_definition]中定义的保持一致