您当然可以编写一个扫描所有权限并检查是否匹配的策略。这是一个简单(但完整)的示例:
package play
permissions = [
{
"resource": "/users/:uid/salary",
"action": "GET"
},
{
"resource": "/metrics",
"action": "GET"
}
]
default allow = false
allow {
some p
matching_permission[p]
}
matching_permission[p] {
some p
matching_permission_action[p]
matching_permission_resource[p]
}
matching_permission_action[p] {
some p
permissions[p].action == input.action
}
matching_permission_resource[p] {
some p
path := replace(permissions[p].resource, ":uid", input.subject)
path == input.resource
}
这种方法的缺点是每次评估都必须在最坏的情况下扫描所有权限。随着更多权限的添加,评估将需要更长的时间。根据权限集可以获得的大小,这可能无法满足延迟要求。
对此的典型答案是使用部分评估来预先评估权限数据并生成一个规则集,由于规则索引,该规则集可以在恒定时间内进行评估。政策绩效页面介绍了这种方法。例如,如果您对此策略运行部分评估,则输出如下:
$ opa eval -d play.rego -f pretty 'data.play.allow' -p --disable-inlining data.play.allow
+-----------+-------------------------------------------------------------------------+
| Query 1 | data.partial.play.allow |
+-----------+-------------------------------------------------------------------------+
| Support 1 | package partial.play |
| | |
| | allow { |
| | "GET" = input.action |
| | |
| | replace("/users/:uid/salary", ":uid", input.subject) = input.resource |
| | } |
| | |
| | allow { |
| | "POST" = input.action |
| | |
| | replace("/metrics", ":uid", input.subject) = input.resource |
| | } |
+-----------+-------------------------------------------------------------------------+
在这种情况下,规则索引器将识别等式语句。... = input.resource
但是,由于replace()
调用,索引器将无法有效地索引语句。
部分挑战在于该策略不是纯粹的 RBAC……它是一种基于属性的策略,将(路径段和主题之间的)相等性检查编码到权限数据中。如果我们稍微重构一下权限数据,我们可以解决这个问题:
package play2
permissions = [
{
"owner": "subject",
"resource": "salary",
"action": "GET"
},
{
"resource": "metrics",
"action": "GET"
},
]
allow {
some p
matching_permission[p]
}
matching_permission[p] {
some p
matching_permission_action[p]
matching_permission_resource[p]
matching_permission_owner[p]
}
matching_permission_action[p] {
some p
permissions[p].action == input.action
}
matching_permission_resource[p] {
some p
permissions[p].resource == input.resource
}
matching_permission_owner[p] {
some p
permissions[p]
not permissions[p].owner
}
matching_permission_owner[p] {
some p
owner := permissions[p].owner
input.owner = input[owner]
}
这个版本非常相似,只是我们将所有权明确编码到权限模型中。“所有者”字段表示资源所有者(在输入文档中提供的"owner"
键下)必须等于指定的输入值(在本例中为input.subject
)。
在此版本上运行部分评估会产生以下输出:
$ opa eval -d play2.rego -f pretty 'data.play2.allow' -p --disable-inlining data.play2.allow
+-----------+-------------------------------+
| Query 1 | data.partial.play2.allow |
+-----------+-------------------------------+
| Support 1 | package partial.play2 |
| | |
| | allow { |
| | "GET" = input.action |
| | |
| | "salary" = input.resource |
| | |
| | input.owner = input.subject |
| | } |
| | |
| | allow { |
| | "GET" = input.action |
| | |
| | "metrics" = input.resource |
| | } |
+-----------+-------------------------------+
规则索引器现在可以识别规则主体上的所有条件,并且评估延迟将随着可能与输入匹配的规则数量而扩展。当然,这里的权衡是每当权限发生变化时,都必须重新执行部分评估。