Array.map for Rego或如何将RBAC与api路由结合

问题描述

我想在JSON数据中定义权限,例如:

"permissions": [
 {
  "resource": ["users",":uid","salary"],"action": "GET"
 }
]

现在评估时,我想将:uid替换为input.subject。我将如何处理? Rego中是否有类似array.prototype.map()的东西?

PS:例如,我知道我可以做到。

allow {
    input.action = ["GET","POST"][_]
    input.resource = ["users",uid,"salary"]
    input.subject = uid
}

但是,我不想使用策略中的每个路径,而是想使用RBAC(角色+权限),以便可以将这些API端点权限作为JSON数据传递。有可能吗?

解决方法

您当然可以编写一个策略,以扫描所有权限并检查是否存在匹配项。这是一个简单(但完整)的示例:

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
}

这种方法的缺点是,每次评估都必须扫描所有权限。随着添加更多权限,评估将花费更长的时间。根据权限集可以获取的大小,这可能无法满足延迟要求。

对此的典型答案是使用部分评估来预先评估权限数据并生成一个规则集,由于规则索引,该规则集可以在恒定时间内进行评估。 Policy Performance页面上介绍了此方法。例如,如果您对此策略进行部分评估,则输出为:

$ 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",input.subject) = input.resource |
|           | }                                                                       |
|           |                                                                         |
|           | allow {                                                                 |
|           |   "POST" = input.action                                                 |
|           |                                                                         |
|           |   replace("/metrics",input.subject) = input.resource           |
|           | }                                                                       |
+-----------+-------------------------------------------------------------------------+

在这种情况下,相等性语句将由规则索引器识别。但是,由于进行了... = input.resource调用,索引器将无法有效地索引replace()语句。

挑战的一部分是该策略不是纯RBAC ...这是一个基于属性的策略,它将相等性检查(在路径段和主题之间)编码为权限数据。如果我们稍微调整权限数据,我们可以解决此问题:

package play2

permissions = [
    {
        "owner": "subject","resource": "salary",{
        "resource": "metrics",]

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

现在,规则索引器可以识别规则主体上的所有条件,并且评估等待时间将根据可能与输入匹配的规则数量进行缩放。当然,这里的权衡是,每当权限更改时,都必须重新执行部分评估。