Skip to content

Commit

Permalink
feat: refactor OpaModuleConfig to accept more modules
Browse files Browse the repository at this point in the history
  • Loading branch information
davidebianchi committed Oct 25, 2024
1 parent 4763de2 commit d5e7385
Show file tree
Hide file tree
Showing 13 changed files with 449 additions and 144 deletions.
33 changes: 24 additions & 9 deletions core/opaevaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,32 +220,42 @@ func buildRolesMap(roles []types.Role) map[string][]string {
}

type OPAModuleConfig struct {
Name string
Content string
Modules []Module

compiler *ast.Compiler
}

func NewOPAModuleConfig(name string, content string) (*OPAModuleConfig, error) {
type Module struct {
Name string
Content string
}

func NewOPAModuleConfig(modules []Module) (*OPAModuleConfig, error) {
compiler := ast.NewCompiler().WithBuiltins(map[string]*ast.Builtin{
custom_builtins.GetHeaderDecl.Name: custom_builtins.GetHeaderDecl,
custom_builtins.MongoFindOneDecl.Name: custom_builtins.MongoFindOneDecl,
custom_builtins.MongoFindManyDecl.Name: custom_builtins.MongoFindManyDecl,
})
compiler.Compile(map[string]*ast.Module{name: ast.MustParseModule(content)})

modulesToCompile := map[string]*ast.Module{}
for _, m := range modules {
parsedModule := ast.MustParseModule(m.Content)
modulesToCompile[m.Name] = parsedModule
}
compiler.Compile(modulesToCompile)

if compiler.Failed() {
return nil, fmt.Errorf("fails to compile the module: %s", compiler.Errors)
}

return &OPAModuleConfig{
Name: name,
Content: content,
Modules: modules,
compiler: compiler,
}, nil
}

func MustNewOPAModuleConfig(name string, content string) *OPAModuleConfig {
opaModule, err := NewOPAModuleConfig(name, content)
func MustNewOPAModuleConfig(modules []Module) *OPAModuleConfig {
opaModule, err := NewOPAModuleConfig(modules)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -285,7 +295,12 @@ func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) {
return nil, fmt.Errorf("%w: %s", ErrRegoModuleReadFailed, err.Error())
}

return NewOPAModuleConfig(filepath.Base(regoModulePath), string(fileContent))
return NewOPAModuleConfig([]Module{
{
Name: filepath.Base(regoModulePath),
Content: string(fileContent),
},
})
}

func processResults(results rego.ResultSet) (allowed bool, responseBodyOverwriter any) {
Expand Down
36 changes: 28 additions & 8 deletions core/partialevaluators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import (
func TestPartialResultEvaluators(t *testing.T) {
logger := logging.NewNoOpLogger()

opaModule := MustNewOPAModuleConfig("example.rego", `package policies
opaModule := MustNewOPAModuleConfig([]Module{
{
Name: "example.rego",
Content: `package policies
allow {
true
}
Expand All @@ -40,7 +43,9 @@ func TestPartialResultEvaluators(t *testing.T) {
column_policy{
false
}
`)
`,
},
})
rondInput := Input{
Request: InputRequest{},
Response: InputResponse{},
Expand Down Expand Up @@ -207,7 +212,10 @@ func TestPartialResultEvaluators(t *testing.T) {
},
}

opaModule := MustNewOPAModuleConfig("example.rego", `
opaModule := MustNewOPAModuleConfig([]Module{
{
Name: "example.rego",
Content: `
package policies
filter_projects {
field := input.user.properties.field
Expand All @@ -218,7 +226,9 @@ func TestPartialResultEvaluators(t *testing.T) {
query := data.resources[_]
query.filterField == myCollDoc.filterField
}
`)
`,
},
})

evalOpts := OPAEvaluatorOptions{
MongoClient: mocks.MongoClientMock{
Expand Down Expand Up @@ -271,7 +281,10 @@ func TestPartialResultEvaluators(t *testing.T) {
},
}

opaModule := MustNewOPAModuleConfig("example.rego", `
opaModule := MustNewOPAModuleConfig([]Module{
{
Name: "example.rego",
Content: `
package policies
filter_projects {
field := input.user.properties.field
Expand All @@ -282,7 +295,9 @@ func TestPartialResultEvaluators(t *testing.T) {
query := data.resources[_]
query.filterField == myCollDoc.filterField
}
`)
`,
},
})

evalOpts := OPAEvaluatorOptions{
MongoClient: mocks.MongoClientMock{
Expand Down Expand Up @@ -362,10 +377,15 @@ func TestPartialResultEvaluators(t *testing.T) {
Logger: logger,
}

opaModule := MustNewOPAModuleConfig("example.rego", `package policies
opaModule := MustNewOPAModuleConfig([]Module{
{
Name: "example.rego",
Content: `package policies
check_metadata {
input.metadata.field == "ok"
}`)
}`,
},
})

err := partialEvaluators.AddFromConfig(context.Background(), logger, opaModule, rondConfig, &evalOpts)
require.NoError(t, err)
Expand Down
16 changes: 13 additions & 3 deletions core/rego_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ import (

func TestNewOPAEvaluator(t *testing.T) {
t.Run("policy sanitization", func(t *testing.T) {
opaModule := MustNewOPAModuleConfig("", "package policies very_composed_policy {true}")
opaModule := MustNewOPAModuleConfig([]Module{
{
Name: "",
Content: "package policies very_composed_policy {true}",
},
})
evaluator, err := newRegoInstanceBuilder("very.composed.policy", opaModule, nil)
require.NoError(t, err)

Expand All @@ -43,8 +48,13 @@ func TestGetHeaderFunction(t *testing.T) {
headerKeyMocked := "exampleKey"
headerValueMocked := "value"

opaModule := MustNewOPAModuleConfig("", `package policies
todo { get_header("ExAmPlEkEy", input.headers) == "value" }`)
opaModule := MustNewOPAModuleConfig([]Module{
{
Name: "",
Content: `package policies
todo { get_header("ExAmPlEkEy", input.headers) == "value" }`,
},
})
queryString := "todo"

t.Run("if header key exists", func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func setupService(env config.EnvironmentVariables, log *logrus.Logger) (*app, er
}).Errorf("failed rego file read")
return nil, err
}
log.WithField("opaModuleFileName", opaModuleConfig.Name).Trace("rego module successfully loaded")
log.WithField("opaModuleFileName", opaModuleConfig.Modules[0].Name).Trace("rego module successfully loaded")

rondLogger := rondlogrus.NewLogger(log)
oas, err := openapi.LoadOASFromFileOrNetwork(rondLogger, openapi.LoadOptions{
Expand Down
18 changes: 14 additions & 4 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1598,14 +1598,19 @@ func TestSetupRouterStandaloneMode(t *testing.T) {
BindingsCrudServiceURL: "http://crud:3030",
AdditionalHeadersToProxy: "miauserid",
}
opa := core.MustNewOPAModuleConfig("policies", `package policies
opa := core.MustNewOPAModuleConfig([]core.Module{
{
Name: "policies",
Content: `package policies
test_policy { true }
filter_policy {
query := data.resources[_]
query.answer = 42
}
`)
`,
},
})
oas := &openapi.OpenAPISpec{
Paths: openapi.OpenAPIPaths{
"/evalapi": openapi.PathVerbs{
Expand Down Expand Up @@ -1754,14 +1759,19 @@ func TestSetupRouterMetrics(t *testing.T) {
AdditionalHeadersToProxy: "miauserid",
ExposeMetrics: true,
}
opa := core.MustNewOPAModuleConfig("policies", `package policies
opa := core.MustNewOPAModuleConfig([]core.Module{
{
Name: "policies",
Content: `package policies
test_policy { true }
filter_policy {
query := data.resources[_]
query.answer = 42
}
`)
`,
},
})
oas := &openapi.OpenAPISpec{
Paths: openapi.OpenAPIPaths{
"/evalapi": openapi.PathVerbs{
Expand Down
23 changes: 18 additions & 5 deletions sdk/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,13 @@ func TestEvaluateRequestPolicy(t *testing.T) {
})

t.Run("with nil options", func(t *testing.T) {
opaModule := core.MustNewOPAModuleConfig("example.rego", `package policies
todo { true }`)
opaModule := core.MustNewOPAModuleConfig([]core.Module{
{
Name: "example.rego",
Content: `package policies todo { true }`,
},
})

sdk, err := NewWithConfig(context.Background(), opaModule, core.RondConfig{
RequestFlow: core.RequestFlow{PolicyName: "todo"},
}, nil)
Expand Down Expand Up @@ -588,10 +593,16 @@ func TestEvaluateResponsePolicy(t *testing.T) {
})

t.Run("with nil options", func(t *testing.T) {
opaModule := core.MustNewOPAModuleConfig("example.rego", `package policies
opaModule := core.MustNewOPAModuleConfig([]core.Module{
{
Name: "example.rego",
Content: `package policies
responsepolicy [body] {
body := input.response.body
}`)
}`,
},
})

sdk, err := NewWithConfig(context.Background(), opaModule, core.RondConfig{
RequestFlow: core.RequestFlow{PolicyName: "todo"},
ResponseFlow: core.ResponseFlow{PolicyName: "responsepolicy"},
Expand Down Expand Up @@ -983,7 +994,9 @@ func getOASSdk(t require.TestingT, options *sdkOptions) OASEvaluatorFinder {
if options.opaModuleContent != "" {
content = options.opaModuleContent
}
opaModule := core.MustNewOPAModuleConfig("example.rego", content)
opaModule := core.MustNewOPAModuleConfig([]core.Module{
{Name: "example.rego", Content: content},
})

sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &Options{
Metrics: options.metrics,
Expand Down
9 changes: 7 additions & 2 deletions sdk/openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ func TestOasSDK(t *testing.T) {

openAPISpec, err := openapi.LoadOASFile("../mocks/simplifiedMock.json")
require.Nil(t, err)
opaModule := core.MustNewOPAModuleConfig("example.rego", `package policies
very_very_composed_permission { true }`)
opaModule := core.MustNewOPAModuleConfig([]core.Module{
{
Name: "example.rego",
Content: `package policies
very_very_composed_permission { true }`,
},
})
sdk, err := NewFromOAS(context.Background(), opaModule, openAPISpec, &Options{
Metrics: metrics.NoOpMetrics(),
Logger: logger,
Expand Down
Loading

0 comments on commit d5e7385

Please sign in to comment.