简介
开放策略代理(Open Policy Agnet)是一个开源的通用策略引擎,可统一整个堆栈的策略执行。OPA 提供了一种高级声明性语言,可让您将策略指定为代码和简单的 API,以从您的软件中卸载策略决策。
文档:https://www.openpolicyagent.org/docs/latest/
rego
Rego 是 OPA 定义的一套规则,我们可以利用 rego 来实现我们的需求,这里不详细介绍它的语法
playgroud: https://play.openpolicyagent.org/
同时 playgroud 里提供了几个模板,有助于我们学习 rego
case
RABC
:::warning
与官方提供的demo的不一样,返回值这里只有 一个 allow
input 与 data 参考 playground 的 RABC 例子
:::
package app.rbac
import future.keywords.in
default allow := false
# 如果是 admin 直接放行
# 注意:多个 allow 结果是 "||"
allow {
user_is_admin(null)
}
user_is_admin(_) {
"admin" in data.user_roles[input.user]
}
allow {
user_is_granted(null)
}
user_is_granted(_) {
# some 和 every 都是循环,区别是 some 只要有一个为true,就会终止循环
# 以下可以理解为一个嵌套循环
# 先找 user 的角色
some role in data.user_roles[input.user]
# 根据 角色 再找到 授予权限
some grant in data.role_grants[role]
input.action == grant.action
input.type == grant.type
}
资源限定
package project.submodel
import future.keywords.in
import future.keywords.every
default allow := false
allow {
# 定义在一起的,逻辑是 "&&", 以这个为例子,以下3个条件 要全是 true,allow 才是 true
normal_policy(input.payload)
authentication_policy(input.payload)
resource(null) # 如果这个函数 被定义了多次,逻辑是 "||"
}
# 定义函数,必须得有个参数
resource(_) {
# 如果 input["resource"]["resource"] 不存在,返回true
not input["resource"]["resource"]
}
resource(_) {
input.packaging[input["resource"]["resource"]]["enable"] == true
}
authentication_policy(payload) {
# every 遍历所有
every policy, _ in payload["policies"]["authentication"] {
input.packaging["policies"]["authentication"]["methods"][policy]["enabled"] == true
}
}
authentication_policy(payload) {
not payload["policies"]["authentication"]
}
normal_policy(payload) {
every policy, _ in payload["policies"] {
input.packaging["policies"][policy]["enabled"] == true
}
}
normal_policy(payload) {
not payload["policies"]
}
自定义的权限
package cloud.authz
import future.keywords.in
default allow := false
allow {
method := input.method
resource := input.resource
# 提前将 scope resource method 定义好,放在 role 里
# 当发生请求时,将请求的行为,为role 里的行为做对比
# some 只要一个满足即可,并为打断循环
some role in input.roles
role.role.permissions[resource.scope][resource.resource][method] == true
}
allow {
# 或 role 为 admin 时,直接放行
some role in input.roles
role["admin"] == true
}
go-client
文档:https://pkg.go.dev/github.com/open-policy-agent/opa/rego
var (
//go:embed pricing.rego
module string
)
func main() {
query, err := rego.New(rego.Query("data.project.submodel.allow"), rego.Module("project.submodel.rego", module))
.PrepareForEval(ctx.GlobalContext)
if err != nil {
panic(err)
}
input := map[string]interface{}{
"payload": payload,
input["packaging"] = plan.(*types.Plan).Packaging
}
if resource != consts.AppResourceType && resource != consts.APIResourceType {
input["resource"] = resource
}
result, err := query.Eval(c, rego.EvalInput(input))
if err != nil {
panic(err)
}
fmt.Println(result.Allowed())
}