mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat: add template RBAC/groups (#4235)
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
@ -11,17 +12,10 @@ import (
|
||||
|
||||
func TestCompileQuery(t *testing.T) {
|
||||
t.Parallel()
|
||||
opts := ast.ParserOptions{
|
||||
AllFutureKeywords: true,
|
||||
}
|
||||
|
||||
t.Run("EmptyQuery", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expression, err := Compile(®o.PartialQueries{
|
||||
Queries: []ast.Body{
|
||||
must(ast.ParseBody("")),
|
||||
},
|
||||
Support: []*ast.Module{},
|
||||
})
|
||||
expression, err := Compile(partialQueries(t, ""))
|
||||
require.NoError(t, err, "compile empty")
|
||||
|
||||
require.Equal(t, "true", expression.RegoString(), "empty query is rego 'true'")
|
||||
@ -30,12 +24,7 @@ func TestCompileQuery(t *testing.T) {
|
||||
|
||||
t.Run("TrueQuery", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expression, err := Compile(®o.PartialQueries{
|
||||
Queries: []ast.Body{
|
||||
must(ast.ParseBody("true")),
|
||||
},
|
||||
Support: []*ast.Module{},
|
||||
})
|
||||
expression, err := Compile(partialQueries(t, "true"))
|
||||
require.NoError(t, err, "compile")
|
||||
|
||||
require.Equal(t, "true", expression.RegoString(), "true query is rego 'true'")
|
||||
@ -44,49 +33,118 @@ func TestCompileQuery(t *testing.T) {
|
||||
|
||||
t.Run("ACLIn", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expression, err := Compile(®o.PartialQueries{
|
||||
Queries: []ast.Body{
|
||||
ast.MustParseBodyWithOpts(`"*" in input.object.acl_group_list.allUsers`, opts),
|
||||
},
|
||||
Support: []*ast.Module{},
|
||||
})
|
||||
expression, err := Compile(partialQueries(t, `"*" in input.object.acl_group_list.allUsers`))
|
||||
require.NoError(t, err, "compile")
|
||||
|
||||
require.Equal(t, `internal.member_2("*", input.object.acl_group_list.allUsers)`, expression.RegoString(), "convert to internal_member")
|
||||
require.Equal(t, `group_acl->allUsers ? '*'`, expression.SQLString(DefaultConfig()), "jsonb in")
|
||||
require.Equal(t, `group_acl->'allUsers' ? '*'`, expression.SQLString(DefaultConfig()), "jsonb in")
|
||||
})
|
||||
|
||||
t.Run("Complex", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expression, err := Compile(®o.PartialQueries{
|
||||
Queries: []ast.Body{
|
||||
ast.MustParseBodyWithOpts(`input.object.org_owner != ""`, opts),
|
||||
ast.MustParseBodyWithOpts(`input.object.org_owner in {"a", "b", "c"}`, opts),
|
||||
ast.MustParseBodyWithOpts(`input.object.org_owner != ""`, opts),
|
||||
ast.MustParseBodyWithOpts(`"read" in input.object.acl_group_list.allUsers`, opts),
|
||||
ast.MustParseBodyWithOpts(`"read" in input.object.acl_user_list.me`, opts),
|
||||
},
|
||||
Support: []*ast.Module{},
|
||||
})
|
||||
expression, err := Compile(partialQueries(t,
|
||||
`input.object.org_owner != ""`,
|
||||
`input.object.org_owner in {"a", "b", "c"}`,
|
||||
`input.object.org_owner != ""`,
|
||||
`"read" in input.object.acl_group_list.allUsers`,
|
||||
`"read" in input.object.acl_user_list.me`,
|
||||
))
|
||||
require.NoError(t, err, "compile")
|
||||
require.Equal(t, `(organization_id :: text != '' OR `+
|
||||
`organization_id :: text = ANY(ARRAY ['a','b','c']) OR `+
|
||||
`organization_id :: text != '' OR `+
|
||||
`group_acl->allUsers ? 'read' OR `+
|
||||
`user_acl->me ? 'read')`,
|
||||
`group_acl->'allUsers' ? 'read' OR `+
|
||||
`user_acl->'me' ? 'read')`,
|
||||
expression.SQLString(DefaultConfig()), "complex")
|
||||
})
|
||||
|
||||
t.Run("SetDereference", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expression, err := Compile(®o.PartialQueries{
|
||||
Queries: []ast.Body{
|
||||
ast.MustParseBodyWithOpts(`"*" in input.object.acl_group_list[input.object.org_owner]`, opts),
|
||||
},
|
||||
Support: []*ast.Module{},
|
||||
})
|
||||
expression, err := Compile(partialQueries(t,
|
||||
`"*" in input.object.acl_group_list[input.object.org_owner]`,
|
||||
))
|
||||
require.NoError(t, err, "compile")
|
||||
require.Equal(t, `group_acl->organization_id :: text ? '*'`,
|
||||
expression.SQLString(DefaultConfig()), "set dereference")
|
||||
})
|
||||
|
||||
t.Run("JsonbLiteralDereference", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expression, err := Compile(partialQueries(t,
|
||||
`"*" in input.object.acl_group_list["4d30d4a8-b87d-45ac-b0d4-51b2e68e7e75"]`,
|
||||
))
|
||||
require.NoError(t, err, "compile")
|
||||
require.Equal(t, `group_acl->'4d30d4a8-b87d-45ac-b0d4-51b2e68e7e75' ? '*'`,
|
||||
expression.SQLString(DefaultConfig()), "literal dereference")
|
||||
})
|
||||
|
||||
t.Run("NoACLColumns", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expression, err := Compile(partialQueries(t,
|
||||
`"*" in input.object.acl_group_list["4d30d4a8-b87d-45ac-b0d4-51b2e68e7e75"]`,
|
||||
))
|
||||
require.NoError(t, err, "compile")
|
||||
require.Equal(t, `true`,
|
||||
expression.SQLString(NoACLConfig()), "literal dereference")
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalQuery(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("GroupACL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expression, err := Compile(partialQueries(t,
|
||||
`"read" in input.object.acl_group_list["4d30d4a8-b87d-45ac-b0d4-51b2e68e7e75"]`,
|
||||
))
|
||||
require.NoError(t, err, "compile")
|
||||
|
||||
result := expression.Eval(Object{
|
||||
Owner: "not-me",
|
||||
OrgID: "random",
|
||||
Type: "workspace",
|
||||
ACLUserList: map[string][]Action{},
|
||||
ACLGroupList: map[string][]Action{
|
||||
"4d30d4a8-b87d-45ac-b0d4-51b2e68e7e75": {"read"},
|
||||
},
|
||||
})
|
||||
require.True(t, result, "eval")
|
||||
})
|
||||
}
|
||||
|
||||
func partialQueries(t *testing.T, queries ...string) *PartialAuthorizer {
|
||||
opts := ast.ParserOptions{
|
||||
AllFutureKeywords: true,
|
||||
}
|
||||
|
||||
astQueries := make([]ast.Body, 0, len(queries))
|
||||
for _, q := range queries {
|
||||
astQueries = append(astQueries, ast.MustParseBodyWithOpts(q, opts))
|
||||
}
|
||||
|
||||
prepareQueries := make([]rego.PreparedEvalQuery, 0, len(queries))
|
||||
for _, q := range astQueries {
|
||||
var prepped rego.PreparedEvalQuery
|
||||
var err error
|
||||
if q.String() == "" {
|
||||
prepped, err = rego.New(
|
||||
rego.Query("true"),
|
||||
).PrepareForEval(context.Background())
|
||||
} else {
|
||||
prepped, err = rego.New(
|
||||
rego.ParsedQuery(q),
|
||||
).PrepareForEval(context.Background())
|
||||
}
|
||||
require.NoError(t, err, "prepare query")
|
||||
prepareQueries = append(prepareQueries, prepped)
|
||||
}
|
||||
return &PartialAuthorizer{
|
||||
partialQueries: ®o.PartialQueries{
|
||||
Queries: astQueries,
|
||||
Support: []*ast.Module{},
|
||||
},
|
||||
preparedQueries: prepareQueries,
|
||||
input: nil,
|
||||
alwaysTrue: false,
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user