• Model CONF 至少应包含四个部分: [request_definition], [policy_definition], [policy_effect], [matchers]

  • 如果 model 使用 RBAC, 还需要添加[role_definition]部分。

  • Model CONF 可以包含注释。注释以 # 开头,# 将注释整行。

Request定义

[request_definition] 部分用于request的定义,它明确了 e.Enforce(…) 函数中参数的含义。

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

sub, obj, act 表示经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action)。 但是, 你可以自定义你自己的请求表单, 如果不需要指定特定资源,则可以这样定义 sub、act ,或者如果有两个访问实体, 则为 sub、sub2、obj、act

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部分的每一行称之为一个策略规则, 每条策略规则通常以形如p, p2policy type开头。 如果存在多个policy定义,那么我们会根据前文提到的policy type与具体的某条定义匹配。 上面的policy的绑定关系将会在matcher中使用, 罗列如下:

  1. (alice, data1, read) -> (p.sub, p.obj, p.act)
  2. (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时,该如何对多个决策结果进行集成以实现统一决策。 以下示例展示了一个只有一条规则生效,其余都被拒绝的情况:

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

该Effect原语表示如果存在任意一个决策结果为allow的匹配规则,则最终决策结果为allow,即allow-override。 其中p.eft 表示策略规则的决策结果,可以为allow 或者deny,当不指定规则的决策结果时,取默认值allow 。 通常情况下,policy的p.eft默认为allow, 因此前面例子中都使用了这个默认值。

这是另一个policy effect的例子:

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

该Effect原语表示不存在任何决策结果为deny的匹配规则,则最终决策结果为allow ,即deny-override。 some 量词判断是否存在一条策略规则满足匹配器。 any 量词则判断是否所有的策略规则都满足匹配器 (此处未使用)。 policy effect还可以利用逻辑运算符进行连接:

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

该Effect原语表示当至少存在一个决策结果为allow的匹配规则,且不存在决策结果为deny的匹配规则时,则最终决策结果为allow。 这时allow授权和deny授权同时存在,但是deny优先。

Matchers

[matchers] 原语定义了策略规则如何与访问请求进行匹配的匹配器,其本质上是布尔表达式,可以理解为Request、Policy等原语定义了关于策略和请求的变量,然后将这些变量代入Matcher原语中求值,从而进行策略决策。

  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, 但是当前我们只支持一个有效的 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

如何添加自定义函数

首先准备好一个有几个参数和一个布尔值返回值的函数:

  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. }

然后用 interface{} 类型包装此函数:

  1. func KeyMatchFunc(args ...interface{}) (interface{}, error) {
  2. name1 := args[0].(string)
  3. name2 := args[1].(string)
  4. return (bool)(KeyMatch(name1, name2)), nil
  5. }

最后, 将该函数注册到 Casbin enforcer:

  1. e.AddFunction("my_func", KeyMatchFunc)

现在, 您可以像下面这样使用 model 中的函数:

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

Role定义

[role_definition]原语定义了RBAC中的角色继承关系。 Casbin支持RBAC系统的多个实例,例如,用户可以有角色和继承关系,而资源也可以有角色和继承关系。 这两个RBAC系统不会干扰。

此部分是可选的。如果在模型中不使用 RBAC 角色, 则省略此部分。

  1. [role_definition]
  2. g = _, _
  3. g2 = _, _

上述Role原语表示g i是一个RBAC体系,g2是另一个RBAC体系。 , 表示角色继承关系的前项和后项,即前项继承后项角色的权限。 一般来讲,如果您需要进行角色和用户的绑定,直接使用g 即可。 当您需要表示角色(或者组)与用户和资源的绑定关系时,可以使用gg2 这样的表现形式。 请参见 rbac_modelrbac_model_with_resource_roles 的示例。

在Casbin里,我们以policy表示中实际的用户角色映射关系 (或是资源-角色映射关系),例如:

  1. p, data2_admin, data2, read
  2. g, alice, data2_admin

上述策略规则表示alice继承或具有角色data2_admin,这里的alice可以为具体的某个用户、某种资源抑或某个角色,在Casbin中它将会被当作字符串(string)来对待。

在 matcher 中,您应该以如下方式来校验角色信息:

  1. [matchers]
  2. 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具有角色 BB 具有角色 C,并且 A 有角色 C。这种传递性在当前版本会造成死循环。

域租户的角色定义

在Casbin中的RBAC角色可以是全局或是基于特定于域的。 特定域的角色意味着当用户处于不同的域/租户群体时,用户所表现的角色也不尽相同。 这对于像云服务这样的大型系统非常有用,因为用户通常分属于不同的租户群体。

域/租户的角色定义应该类似于:

  1. [role_definition]
  2. g = _, _, _

原语中第三个_表示域的概念,相对应的策略规则的实例如下:

  1. p, admin, tenant1, data1, read
  2. p, admin, tenant2, data2, read
  3. g, alice, admin, tenant1
  4. g, alice, user, tenant2

该实例表示tenant1的域内角色admin 可以读取data1alicetenant1域中具有admin角色,但在tenant2域中具有user角色, 所以alice可以有读取data1的权限。 同理,因为alice不是tenant2admin,所以她访问不了data2

接下来在matcher中,应该像下面的例子一样检查角色信息:

  1. [matchers]
  2. 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

原文: https://casbin.org/docs/zh-CN/syntax-for-models