Model CONF 至少应包含四个部分:
[request_definition], [policy_definition], [policy_effect], [matchers]
。如果 model 使用 RBAC, 还需要添加
[role_definition]
部分。Model CONF 可以包含注释。注释以
#
开头,#
将注释整行。
Request定义
[request_definition]
部分用于request的定义,它明确了 e.Enforce(…)
函数中参数的含义。
[request_definition]
r = sub, obj, act
sub, obj, act
表示经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action)。 但是, 你可以自定义你自己的请求表单, 如果不需要指定特定资源,则可以这样定义 sub、act
,或者如果有两个访问实体, 则为 sub、sub2、obj、act
。
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部分的每一行称之为一个策略规则, 每条策略规则通常以形如p
, p2
的policy type
开头。 如果存在多个policy定义,那么我们会根据前文提到的policy type
与具体的某条定义匹配。 上面的policy的绑定关系将会在matcher中使用, 罗列如下:
(alice, data1, read) -> (p.sub, p.obj, p.act)
(bob, write-all-objects) -> (p2.sub, p2.act)
注1: 当前只支持形如 p
的单个policy定义, 形如p2
类型的尚未支持。 通常情况下, 用户无需使用多个 policy 定义, 如果您有其他情形的policy定义诉求,请在https://github.com/casbin/casbin/issues/new提出issue告知我们。
注2: policy定义中的元素始终被视为字符串(string
)对待, 如果您对此有疑问,请移步https://github.com/casbin/casbin/issues/113
Policy effect定义
[policy_effect]
部分是对policy生效范围的定义, 原语定义了当多个policy rule同时匹配访问请求request时,该如何对多个决策结果进行集成以实现统一决策。 以下示例展示了一个只有一条规则生效,其余都被拒绝的情况:
[policy_effect]
e = some(where (p.eft == allow))
该Effect原语表示如果存在任意一个决策结果为allow
的匹配规则,则最终决策结果为allow
,即allow-override。 其中p.eft
表示策略规则的决策结果,可以为allow
或者deny
,当不指定规则的决策结果时,取默认值allow
。 通常情况下,policy的p.eft
默认为allow
, 因此前面例子中都使用了这个默认值。
这是另一个policy effect的例子:
[policy_effect]
e = !some(where (p.eft == deny))
该Effect原语表示不存在任何决策结果为deny
的匹配规则,则最终决策结果为allow
,即deny-override。 some
量词判断是否存在一条策略规则满足匹配器。 any
量词则判断是否所有的策略规则都满足匹配器 (此处未使用)。 policy effect还可以利用逻辑运算符进行连接:
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
该Effect原语表示当至少存在一个决策结果为allow
的匹配规则,且不存在决策结果为deny
的匹配规则时,则最终决策结果为allow
。 这时allow
授权和deny
授权同时存在,但是deny
优先。
Matchers
[matchers]
原语定义了策略规则如何与访问请求进行匹配的匹配器,其本质上是布尔表达式,可以理解为Request、Policy等原语定义了关于策略和请求的变量,然后将这些变量代入Matcher原语中求值,从而进行策略决策。
[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, 但是当前我们只支持一个有效的 matcher m
。 通常情况下,您可以在一个matcher中使用上文提到的逻辑运算符来实现复杂的逻辑判断, 因而我们认为目前不需要支持多个matcher。 如果您对此有疑问,请告知我们(https://github.com/casbin/casbin/issues)。
matcher中的函数
matcher的强大与灵活之处在于您甚至可以在matcher中定义函数,这些函数可以是内置函数或自定义的函数。当前支持的内置函数如下:
函数 | 释义 | 示例 |
---|---|---|
keyMatch(arg1, arg2) | 参数 arg1 是一个 URL 路径,例如 /alice_data/resource1 ,参数 arg2 可以是URL路径或者是一个 模式,例如 /alice_data/ 。此函数返回 arg1是否与 arg2 匹配。 |
keymatch_model.conf/keymatch_policy.csv |
keyMatch2(arg1, arg2) | 参数 arg1 是一个 URL 路径,例如 /alice_data/resource1 ,参数 arg2 可以是 URL 路径或者是一个 : 模式,例如/alice_data/:resource 。此函数返回 arg1 是否与 arg2 匹配。 |
keymatch2_model.conf/keymatch2_policy.csv |
regexMatch(arg1, arg2) | arg1 可以是任何字符串。arg2 是一个正则表达式。它返回 arg1 是否匹配 arg2。 | keymatch_model.conf/keymatch_policy.csv |
ipMatch(arg1, arg2) | arg1 是一个 IP 地址, 如 192.168.2.123 。arg2 可以是 IP 地址或 CIDR, 如 192.168.2. 0/24 。它返回 arg1 是否匹配 arg2。 |
ipmatch_model.conf/ipmatch_policy.csv |
如何添加自定义函数
首先准备好一个有几个参数和一个布尔值返回值的函数:
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]
}
然后用 interface{}
类型包装此函数:
func KeyMatchFunc(args ...interface{}) (interface{}, error) {
name1 := args[0].(string)
name2 := args[1].(string)
return (bool)(KeyMatch(name1, name2)), nil
}
最后, 将该函数注册到 Casbin enforcer:
e.AddFunction("my_func", KeyMatchFunc)
现在, 您可以像下面这样使用 model 中的函数:
[matchers]
m = r.sub == p.sub && my_func(r.obj, p.obj) && r.act == p.act
Role定义
[role_definition]
原语定义了RBAC中的角色继承关系。 Casbin支持RBAC系统的多个实例,例如,用户可以有角色和继承关系,而资源也可以有角色和继承关系。 这两个RBAC系统不会干扰。
此部分是可选的。如果在模型中不使用 RBAC 角色, 则省略此部分。
[role_definition]
g = _, _
g2 = _, _
上述Role原语表示g
i是一个RBAC体系,g2
是另一个RBAC体系。 ,
表示角色继承关系的前项和后项,即前项继承后项角色的权限。 一般来讲,如果您需要进行角色和用户的绑定,直接使用g
即可。 当您需要表示角色(或者组)与用户和资源的绑定关系时,可以使用g
和 g2
这样的表现形式。 请参见 rbac_model 和 rbac_model_with_resource_roles 的示例。
在Casbin里,我们以policy表示中实际的用户角色映射关系 (或是资源-角色映射关系),例如:
p, data2_admin, data2, read
g, alice, data2_admin
上述策略规则表示alice
继承或具有角色data2_admin
,这里的alice可以为具体的某个用户、某种资源抑或某个角色,在Casbin中它将会被当作字符串(string
)来对待。
在 matcher 中,您应该以如下方式来校验角色信息:
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
它表示请求request中的sub
必须具有policy中定义的sub
角色。
注:
- Casbin 只存储用户角色的映射关系。
- Casbin 不验证用户是否为有效用户, 或者角色是否为有效角色。这应该由身份验证来处理。
- RBAC 系统中的用户名称和角色名称不应相同。因为Casbin将用户名和角色识别为字符串, 所以当前语境下Casbin无法得出这个字面量到底指代用户
alice
还是角色alice
。 这时,使用明确的role_alice
,问题便可迎刃而解。 - 假设
A
具有角色B
,B
具有角色C
,并且A
有角色C
。这种传递性在当前版本会造成死循环。
域租户的角色定义
在Casbin中的RBAC角色可以是全局或是基于特定于域的。 特定域的角色意味着当用户处于不同的域/租户群体时,用户所表现的角色也不尽相同。 这对于像云服务这样的大型系统非常有用,因为用户通常分属于不同的租户群体。
域/租户的角色定义应该类似于:
[role_definition]
g = _, _, _
原语中第三个_
表示域的概念,相对应的策略规则的实例如下:
p, admin, tenant1, data1, read
p, admin, tenant2, data2, read
g, alice, admin, tenant1
g, alice, user, tenant2
该实例表示tenant1
的域内角色admin
可以读取data1
, alice
在tenant1
域中具有admin
角色,但在tenant2
域中具有user
角色, 所以alice可以有读取data1
的权限。 同理,因为alice
不是tenant2
的admin
,所以她访问不了data2
。
接下来在matcher中,应该像下面的例子一样检查角色信息:
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
更多示例参见: rbac_model_with_domains.conf 。