【问题标题】:Array.map for Rego or how to combine RBAC with api routesRego 的 Array.map 或如何将 RBAC 与 api 路由相结合
【发布时间】:2020-09-18 04:29:19
【问题描述】:

我想在 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 数据传递。有可能吗?

【问题讨论】:

    标签: open-policy-agent rego


    【解决方案1】:

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

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

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

    部分挑战在于该策略不是纯粹的 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”字段表示资​​源所有者(在"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  |
    |           | }                             |
    +-----------+-------------------------------+
    

    规则索引器现在可以识别规则主体上的所有条件,并且评估延迟将随着可能与输入匹配的规则数量而扩展。当然,这里的权衡是每当权限发生变化时,都必须重新执行部分评估。

    【讨论】:

    • 感谢您的详细回答,包括选项之间的权衡。
    猜你喜欢
    • 2016-07-13
    • 2017-09-01
    • 2018-11-17
    • 2021-03-15
    • 2011-01-24
    • 1970-01-01
    • 2018-04-01
    • 2018-01-03
    • 2017-02-13
    相关资源
    最近更新 更多