mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-03 12:26:03 +02:00
155 lines
3.8 KiB
Go
155 lines
3.8 KiB
Go
// Package generator converts Pomerium Policy Language into Rego.
|
|
package generator
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/open-policy-agent/opa/ast"
|
|
|
|
"github.com/pomerium/pomerium/pkg/policy/parser"
|
|
"github.com/pomerium/pomerium/pkg/policy/rules"
|
|
)
|
|
|
|
// A Generator generates a rego script from a policy.
|
|
type Generator struct {
|
|
ids map[string]int
|
|
criteria map[string]Criterion
|
|
}
|
|
|
|
// An Option configures the Generator.
|
|
type Option func(*Generator)
|
|
|
|
// WithCriterion adds a Criterion to the generator's known criteria.
|
|
func WithCriterion(criterionConstructor CriterionConstructor) Option {
|
|
return func(g *Generator) {
|
|
c := criterionConstructor(g)
|
|
g.criteria[c.Name()] = c
|
|
}
|
|
}
|
|
|
|
// New creates a new Generator.
|
|
func New(options ...Option) *Generator {
|
|
g := &Generator{
|
|
ids: make(map[string]int),
|
|
criteria: make(map[string]Criterion),
|
|
}
|
|
for _, o := range options {
|
|
o(g)
|
|
}
|
|
return g
|
|
}
|
|
|
|
// GetCriterion gets a Criterion for the given name.
|
|
func (g *Generator) GetCriterion(name string) (Criterion, bool) {
|
|
c, ok := g.criteria[name]
|
|
return c, ok
|
|
}
|
|
|
|
// Generate generates the rego module from a policy.
|
|
func (g *Generator) Generate(policy *parser.Policy) (*ast.Module, error) {
|
|
rs := ast.NewRuleSet()
|
|
rs.Add(ast.MustParseRule(`default allow = [false, set()]`))
|
|
rs.Add(ast.MustParseRule(`default deny = [false, set()]`))
|
|
rs.Add(rules.InvertCriterionResult())
|
|
rs.Add(rules.NormalizeCriterionResult())
|
|
rs.Add(rules.ObjectUnion())
|
|
rs.Add(rules.MergeWithAnd())
|
|
rs.Add(rules.MergeWithOr())
|
|
|
|
for _, action := range []parser.Action{parser.ActionAllow, parser.ActionDeny} {
|
|
var terms []*ast.Term
|
|
for _, policyRule := range policy.Rules {
|
|
if policyRule.Action != action {
|
|
continue
|
|
}
|
|
|
|
if len(policyRule.And) > 0 {
|
|
subRule, err := g.generateAndRule(&rs, policyRule.And)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
terms = append(terms, ast.VarTerm(string(subRule.Head.Name)))
|
|
}
|
|
if len(policyRule.Or) > 0 {
|
|
subRule, err := g.generateOrRule(&rs, policyRule.Or)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
terms = append(terms, ast.VarTerm(string(subRule.Head.Name)))
|
|
}
|
|
if len(policyRule.Not) > 0 {
|
|
subRule, err := g.generateNotRule(&rs, policyRule.Not)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
terms = append(terms, ast.VarTerm(string(subRule.Head.Name)))
|
|
}
|
|
if len(policyRule.Nor) > 0 {
|
|
subRule, err := g.generateNorRule(&rs, policyRule.Nor)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
terms = append(terms, ast.VarTerm(string(subRule.Head.Name)))
|
|
}
|
|
}
|
|
if len(terms) > 0 {
|
|
rule := &ast.Rule{
|
|
Head: &ast.Head{
|
|
Name: ast.Var(action),
|
|
Value: ast.VarTerm("v"),
|
|
},
|
|
Body: append(ast.Body{
|
|
ast.Assign.Expr(ast.VarTerm("results"), ast.ArrayTerm(terms...)),
|
|
}, orBody...),
|
|
}
|
|
rs.Add(rule)
|
|
}
|
|
}
|
|
|
|
mod := &ast.Module{
|
|
Package: &ast.Package{
|
|
Path: ast.Ref{
|
|
ast.StringTerm("policy.rego"),
|
|
ast.StringTerm("pomerium"),
|
|
ast.StringTerm("policy"),
|
|
},
|
|
},
|
|
Rules: rs,
|
|
}
|
|
|
|
// move functions to the end
|
|
sort.SliceStable(mod.Rules, func(i, j int) bool {
|
|
return len(mod.Rules[i].Head.Args) < len(mod.Rules[j].Head.Args)
|
|
})
|
|
|
|
i := 1
|
|
ast.WalkRules(mod, func(r *ast.Rule) bool {
|
|
r.SetLoc(ast.NewLocation([]byte(r.String()), "", i, 1))
|
|
i++
|
|
return false
|
|
})
|
|
|
|
return mod, nil
|
|
}
|
|
|
|
// NewRuleFromTemplate creates a new rule from a template rule.
|
|
func (g *Generator) NewRuleFromTemplate(name string, template *ast.Rule) *ast.Rule {
|
|
id := g.ids[name]
|
|
g.ids[name]++
|
|
|
|
newRule := template.Copy()
|
|
newRule.Head.Name = ast.Var(fmt.Sprintf("%s_%d", name, id))
|
|
return newRule
|
|
}
|
|
|
|
// NewRule creates a new rule with a dynamically generated name.
|
|
func (g *Generator) NewRule(name string) *ast.Rule {
|
|
id := g.ids[name]
|
|
g.ids[name]++
|
|
return &ast.Rule{
|
|
Head: &ast.Head{
|
|
Name: ast.Var(fmt.Sprintf("%s_%d", name, id)),
|
|
},
|
|
}
|
|
}
|