使用 opa for abac 根据定义的策略检查用户声明
Using opa for abac to check user claims agains defined policies
因此,我正在尝试为我的应用程序实施一个相当简单的 ABAC 系统,并在调查期间遇到了开放策略代理。它似乎很适合我的需要,但我无法让它适用于我的用例,在我的用例中,我有一个从 jwt 声明中读取的用户对象,看起来像这样:{ email: "1@1.com", role: "admin", location: "us" }
。我想检查该用户是否有权访问特定路径(作为输入提供,与用户相同)。因此,例如,如果 user.role == admin and user.location == us
,我想授予对 /admin/us
的访问权限。我在 rego 游乐场上创建了一个示例,只要用户拥有与政策中所写的完全相同的声明,它就可以正常工作,但如果用户有任何其他声明,它就会失败:
package play
default allow = false
allow {
some p
policy := data.policies[p]
policy.request_path == input.request_path
# check if all input.user[x] matches to a policy
policy.user == input.user # works only if objects have the same keys and values
}
我在想我可以使用交集来获取匹配的键并将其与这样的策略定义进行比较:
# check if all input.user[x] matches to a policy
count(intersection(input.user[data.user])) == count(policy.user)
但这不起作用,很可能是因为语法不正确。
我还尝试使用理解来过滤掉用户的密钥,然后将其与所有策略的完全相等进行比较,但也无法使其工作。
谁能给我指路或提供一些opa/rego的学习资料(官方文档有点缺乏)。
这是 rego 游乐场上的完整示例:https://play.openpolicyagent.org/p/ijtOjxXRKk
我找到了一种使用 object.union()
:
的方法
patchedpolicy := object.union(input.user, policy.user)
patchedpolicy == input.user
https://play.openpolicyagent.org/p/7LKqnCz7Wr
它基本上将所有丢失的密钥从用户复制到策略,然后将其与输入进行比较。
但是,当用户根本没有策略中定义的密钥时,这将不起作用,例如:
# this will evaluate to true even though it is not desired
user = { "email": "1@1.com", location: "us" }
policy = { "path": "/admin", user: { role: "admin" }
这可以通过附加规则来修复,以根据修补策略检查 input.user 密钥:
some k
input.user[k] == patchedpolicy[k]
https://play.openpolicyagent.org/p/muHCb2TBv3
最后一个 link 非常有效,通过了我所有的测试,但我觉得这可以简单得多。我愿意看到更简单或更好的解决方案。
你在这里遇到的是 Rego 缺乏“for all”或“universal quantification”。请参阅有关主题 here.
的文档
正如您已经注意到的那样,仍然有很多方法可以做到这一点。一种是在辅助规则中使用否定(即 policy.user
中的某些 key/value 是 input.user
中的 而不是 。另一种方法是使用理解并将所有匹配项的计数与所需属性的计数进行比较:
package play
default allow = false
allow {
some p
policy := data.policies[p]
policy.request_path == input.request_path
required := count(policy.user)
matches := count([v | v := policy.user[k]; v == input.user[k]])
required == matches
}
OPA 的未来版本可能会做到这一点 easier。
因此,我正在尝试为我的应用程序实施一个相当简单的 ABAC 系统,并在调查期间遇到了开放策略代理。它似乎很适合我的需要,但我无法让它适用于我的用例,在我的用例中,我有一个从 jwt 声明中读取的用户对象,看起来像这样:{ email: "1@1.com", role: "admin", location: "us" }
。我想检查该用户是否有权访问特定路径(作为输入提供,与用户相同)。因此,例如,如果 user.role == admin and user.location == us
,我想授予对 /admin/us
的访问权限。我在 rego 游乐场上创建了一个示例,只要用户拥有与政策中所写的完全相同的声明,它就可以正常工作,但如果用户有任何其他声明,它就会失败:
package play
default allow = false
allow {
some p
policy := data.policies[p]
policy.request_path == input.request_path
# check if all input.user[x] matches to a policy
policy.user == input.user # works only if objects have the same keys and values
}
我在想我可以使用交集来获取匹配的键并将其与这样的策略定义进行比较:
# check if all input.user[x] matches to a policy
count(intersection(input.user[data.user])) == count(policy.user)
但这不起作用,很可能是因为语法不正确。
我还尝试使用理解来过滤掉用户的密钥,然后将其与所有策略的完全相等进行比较,但也无法使其工作。
谁能给我指路或提供一些opa/rego的学习资料(官方文档有点缺乏)。
这是 rego 游乐场上的完整示例:https://play.openpolicyagent.org/p/ijtOjxXRKk
我找到了一种使用 object.union()
:
patchedpolicy := object.union(input.user, policy.user)
patchedpolicy == input.user
https://play.openpolicyagent.org/p/7LKqnCz7Wr
它基本上将所有丢失的密钥从用户复制到策略,然后将其与输入进行比较。
但是,当用户根本没有策略中定义的密钥时,这将不起作用,例如:
# this will evaluate to true even though it is not desired
user = { "email": "1@1.com", location: "us" }
policy = { "path": "/admin", user: { role: "admin" }
这可以通过附加规则来修复,以根据修补策略检查 input.user 密钥:
some k
input.user[k] == patchedpolicy[k]
https://play.openpolicyagent.org/p/muHCb2TBv3
最后一个 link 非常有效,通过了我所有的测试,但我觉得这可以简单得多。我愿意看到更简单或更好的解决方案。
你在这里遇到的是 Rego 缺乏“for all”或“universal quantification”。请参阅有关主题 here.
的文档正如您已经注意到的那样,仍然有很多方法可以做到这一点。一种是在辅助规则中使用否定(即 policy.user
中的某些 key/value 是 input.user
中的 而不是 。另一种方法是使用理解并将所有匹配项的计数与所需属性的计数进行比较:
package play
default allow = false
allow {
some p
policy := data.policies[p]
policy.request_path == input.request_path
required := count(policy.user)
matches := count([v | v := policy.user[k]; v == input.user[k]])
required == matches
}
OPA 的未来版本可能会做到这一点 easier。