mirror of https://github.com/coder/coder.git
105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
package regosql
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/open-policy-agent/opa/ast"
|
|
|
|
"github.com/coder/coder/coderd/rbac/regosql/sqltypes"
|
|
)
|
|
|
|
var (
|
|
_ sqltypes.VariableMatcher = ACLGroupVar{}
|
|
_ sqltypes.Node = ACLGroupVar{}
|
|
)
|
|
|
|
// ACLGroupVar is a variable matcher that handles group_acl and user_acl.
|
|
// The sql type is a jsonb object with the following structure:
|
|
//
|
|
// "group_acl": {
|
|
// "<group_name>": ["<actions>"]
|
|
// }
|
|
//
|
|
// This is a custom variable matcher as json objects have arbitrary complexity.
|
|
type ACLGroupVar struct {
|
|
StructSQL string
|
|
// input.object.group_acl -> ["input", "object", "group_acl"]
|
|
StructPath []string
|
|
|
|
// FieldReference handles referencing the subfields, which could be
|
|
// more variables. We pass one in as the global one might not be correctly
|
|
// scoped.
|
|
FieldReference sqltypes.VariableMatcher
|
|
|
|
// Instance fields
|
|
Source sqltypes.RegoSource
|
|
GroupNode sqltypes.Node
|
|
}
|
|
|
|
func ACLGroupMatcher(fieldReference sqltypes.VariableMatcher, structSQL string, structPath []string) ACLGroupVar {
|
|
return ACLGroupVar{StructSQL: structSQL, StructPath: structPath, FieldReference: fieldReference}
|
|
}
|
|
|
|
func (ACLGroupVar) UseAs() sqltypes.Node { return ACLGroupVar{} }
|
|
|
|
func (g ACLGroupVar) ConvertVariable(rego ast.Ref) (sqltypes.Node, bool) {
|
|
// "left" will be a map of group names to actions in rego.
|
|
// {
|
|
// "all_users": ["read"]
|
|
// }
|
|
left, err := sqltypes.RegoVarPath(g.StructPath, rego)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
|
|
aclGrp := ACLGroupVar{
|
|
StructSQL: g.StructSQL,
|
|
StructPath: g.StructPath,
|
|
FieldReference: g.FieldReference,
|
|
|
|
Source: sqltypes.RegoSource(rego.String()),
|
|
}
|
|
|
|
// We expect 1 more term. Either a ref or a string.
|
|
if len(left) != 1 {
|
|
return nil, false
|
|
}
|
|
|
|
// If the remaining is a variable, then we need to convert it.
|
|
// Assuming we support variable fields.
|
|
ref, ok := left[0].Value.(ast.Ref)
|
|
if ok && g.FieldReference != nil {
|
|
groupNode, ok := g.FieldReference.ConvertVariable(ref)
|
|
if ok {
|
|
aclGrp.GroupNode = groupNode
|
|
return aclGrp, true
|
|
}
|
|
}
|
|
|
|
// If it is a string, we assume it is a literal
|
|
groupName, ok := left[0].Value.(ast.String)
|
|
if ok {
|
|
aclGrp.GroupNode = sqltypes.String(string(groupName))
|
|
return aclGrp, true
|
|
}
|
|
|
|
// If we have not matched it yet, then it is something we do not recognize.
|
|
return nil, false
|
|
}
|
|
|
|
func (g ACLGroupVar) SQLString(cfg *sqltypes.SQLGenerator) string {
|
|
return fmt.Sprintf("%s->%s", g.StructSQL, g.GroupNode.SQLString(cfg))
|
|
}
|
|
|
|
func (g ACLGroupVar) ContainsSQL(cfg *sqltypes.SQLGenerator, other sqltypes.Node) (string, error) {
|
|
switch other.UseAs().(type) {
|
|
// Only supports containing other strings.
|
|
case sqltypes.AstString:
|
|
return fmt.Sprintf("%s ? %s", g.SQLString(cfg), other.SQLString(cfg)), nil
|
|
default:
|
|
return "", xerrors.Errorf("unsupported acl group contains %T", other)
|
|
}
|
|
}
|