# Model语法
* Model CONF 至少应包含四个部分:`[request_definition], [policy_definition], [policy_effect], [matchers]`。
* 如果 model 使用 RBAC, 还需要添加`[role_definition]`部分。
* A model CONF can contain comments. The comments start with`#`, and`#`will comment the rest of the line.
## Request定义
`[request_definition]`部分用于request的定义,它明确了`e.Enforce(...)`函数中参数的含义。
~~~ini
[request_definition]
r = sub, obj, act
~~~
`sub, obj, act`表示经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action)。 但是, 你可以自定义你自己的请求表单, 如果不需要指定特定资源,则可以这样定义`sub、act`,或者如果有两个访问实体, 则为`sub、sub2、obj、act`。
## Policy定义
`[policy_definition]`部分是对policy的定义,以下文的 model 配置为例:
~~~ini
[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告知我们](https://github.com/casbin/casbin/issues/new%E6%8F%90%E5%87%BAissue%E5%91%8A%E7%9F%A5%E6%88%91%E4%BB%AC)。
**注2**: policy定义中的元素始终被视为字符串(`string`)对待, 如果您对此有疑问,请移步[https://github.com/casbin/casbin/issues/113](https://github.com/casbin/casbin/issues/113)
## Policy effect定义
`[policy_effect]`部分是对policy生效范围的定义, 原语定义了当多个policy rule同时匹配访问请求request时,该如何对多个决策结果进行集成以实现统一决策。 以下示例展示了一个只有一条规则生效,其余都被拒绝的情况:
~~~ini
[policy_effect]
e = some(where (p.eft == allow))
~~~
该Effect原语表示如果存在任意一个决策结果为`allow`的匹配规则,则最终决策结果为`allow`,即allow-override。 其中`p.eft`表示策略规则的决策结果,可以为`allow`或者`deny`,当不指定规则的决策结果时,取默认值`allow`。 通常情况下,policy的`p.eft`默认为`allow`, 因此前面例子中都使用了这个默认值。
这是另一个policy effect的例子:
~~~ini
[policy_effect]
e = !some(where (p.eft == deny))
~~~
该Effect原语表示不存在任何决策结果为`deny`的匹配规则,则最终决策结果为`allow`,即deny-override。`some`量词判断是否存在一条策略规则满足匹配器。`any`量词则判断是否所有的策略规则都满足匹配器 (此处未使用)。 policy effect还可以利用逻辑运算符进行连接:
~~~ini
[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原语中求值,从而进行策略决策。
~~~ini
[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](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](https://github.com/casbin/casbin/blob/master/examples/keymatch_model.conf)/[keymatch\_policy.csv](https://github.com/casbin/casbin/blob/master/examples/keymatch_policy.csv) |
| keyMatch2(arg1, arg2) | 参数 arg1 是一个 URL 路径,例如`/alice_data/resource1`,参数 arg2 可以是 URL 路径或者是一个`:`模式,例如`/alice_data/:resource`。此函数返回 arg1 是否与 arg2 匹配。 | [keymatch2\_model.conf](https://github.com/casbin/casbin/blob/master/examples/keymatch2_model.conf)/[keymatch2\_policy.csv](https://github.com/casbin/casbin/blob/master/examples/keymatch2_policy.csv) |
| regexMatch(arg1, arg2) | arg1 可以是任何字符串。arg2 是一个正则表达式。它返回 arg1 是否匹配 arg2。 | [keymatch\_model.conf](https://github.com/casbin/casbin/blob/master/examples/keymatch_model.conf)/[keymatch\_policy.csv](https://github.com/casbin/casbin/blob/master/examples/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](https://github.com/casbin/casbin/blob/master/examples/ipmatch_model.conf)/[ipmatch\_policy.csv](https://github.com/casbin/casbin/blob/master/examples/ipmatch_policy.csv) |
### 如何添加自定义函数
首先准备好一个有几个参数和一个布尔值返回值的函数:
~~~go
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{}`类型包装此函数:
~~~go
func KeyMatchFunc(args ...interface{}) (interface{}, error) {
name1 := args[0].(string)
name2 := args[1].(string)
return (bool)(KeyMatch(name1, name2)), nil
}
~~~
最后, 将该函数注册到 Casbin enforcer:
~~~go
e.AddFunction("my_func", KeyMatchFunc)
~~~
现在, 您可以像下面这样使用 model 中的函数:
~~~ini
[matchers]
m = r.sub == p.sub && my_func(r.obj, p.obj) && r.act == p.act
~~~