具有范围和角色的 OPA 授权策略

OPA authorization policies with scopes and roles

我将 Open Policy Agent 作为授权组件与支持 OIDC 的应用程序一起使用。

我从应用程序中输入的格式为:

{
    "token": {
        "scopes": [
            "read:books",
            "write:books"
        ]
    },
    "principal": {
        "roles": [
            "user",
            "moderator"
        ]
    },
    "context": {
        "action": "read",
        "resource": "books"
    }
}

然后我有访问映射格式的数据:

{
    "user": [
        "read:books"
    ],
    "moderator": [
        "read:books",
        "write:books"
    ],
    "administrator": [
        "read:books",
        "write:books",
        "read:store",
        "write:store"
    ]
}

目前的政策是这样的:

package whatever.authz

context_scope := concat(":", [input.context.action, input.context.resource])

default allow = false

allow {
    token_has_context_scope
    principal_has_resource_access
}

token_has_context_scope {
    context_scope == input.token.scopes[_]
}

principal_has_resource_access {
    principal_role := input.principal.roles[_]
    context_scope == data[principal_role][_]
}

这会产生以下错误:

2 errors occurred:
policy.rego:16: rego_recursion_error: rule principal_has_resource_access is recursive: principal_has_resource_access -> principal_has_resource_access
policy.rego:7: rego_recursion_error: rule allow is recursive: allow -> principal_has_resource_access -> allow

导致错误的是 principal_has_resource_access 函数中的递归查找。

我需要检查是否允许委托人的角色之一访问上下文指定的资源。由于角色是一个数组,我需要找到数据中所有访问范围的联合,并查看其中一个是否与上下文范围匹配。我在政策中做错了什么?

可以在 Rego Playground 中找到代码片段 https://play.openpolicyagent.org/p/KhovLRgMup

OPA 将所有 数据存储在data 路径下,包括策略和规则。编译器无法知道您提供的输入没有引用递归的策略本身(即 data["whatever"])。解决此问题的最简单方法是简单地为您的数据使用与您的策略不同的顶级属性(即包名称),如下所示:

{
    "attributes": {
        "user": [
            "read:books"
        ],
        "moderator": [
            "read:books",
            "write:books"
        ],
        "administrator": [
            "read:books",
            "write:books",
            "read:store",
            "write:store"
        ]
    }
}

并更新您的政策以引用此内容:

context_scope == data["attributes"][principal_role][_]

因为 data.attributes != data.whatever.authz 没有递归的风险,编译器也不会报错。您可能想要一个比“属性”更好的名称,但我会把它留给您:)