Skip to content
Snippets Groups Projects
Commit c3820989 authored by Lyuben Penkovski's avatar Lyuben Penkovski
Browse files

Generic policy evaluation with hardcoded query

This is an experiment to produce a generic evaluation
function for different polices, which is able to return
arbitrary JSON result after policy execution. For the
moment a lot of conventions have to be followed in order
for this to work, which is not so good. More experiments
will follow to reduce the conventions to minimum (it would
be best to remove all of them).
parent 826fe358
No related branches found
No related tags found
1 merge request!2HTTP endpoint for policy evaluation (execution)
Pipeline #49689 passed with stage
in 25 seconds
......@@ -25,6 +25,7 @@ import (
"code.vereign.com/gaiax/tsa/policy/internal/service"
"code.vereign.com/gaiax/tsa/policy/internal/service/health"
"code.vereign.com/gaiax/tsa/policy/internal/service/policy"
"code.vereign.com/gaiax/tsa/policy/internal/storage"
)
var Version = "0.0.0+development"
......@@ -41,7 +42,9 @@ func main() {
}
defer logger.Sync() //nolint:errcheck
logger.Info("staring policy service", zap.String("version", Version), zap.String("goa", goa.Version()))
logger.Info("policy service started", zap.String("version", Version), zap.String("goa", goa.Version()))
storage := storage.New()
// create services
var (
......@@ -49,7 +52,7 @@ func main() {
healthSvc goahealth.Service
)
{
policySvc = policy.New()
policySvc = policy.New(storage, logger)
healthSvc = health.New()
}
......
......@@ -3,16 +3,69 @@ package policy
import (
"context"
"github.com/open-policy-agent/opa/rego"
"go.uber.org/zap"
"code.vereign.com/gaiax/tsa/golib/errors"
"code.vereign.com/gaiax/tsa/policy/gen/policy"
"code.vereign.com/gaiax/tsa/policy/internal/storage"
)
type Service struct{}
type Storage interface {
Policy(ctx context.Context, name, group, version string) (*storage.Policy, error)
}
func New() *Service {
return &Service{}
type Service struct {
storage Storage
logger *zap.Logger
}
func (s *Service) Evaluate(context.Context, *policy.EvaluateRequest) (*policy.EvaluateResult, error) {
return nil, errors.New("not implemented")
func New(storage Storage, logger *zap.Logger) *Service {
return &Service{
storage: storage,
logger: logger,
}
}
func (s *Service) Evaluate(ctx context.Context, req *policy.EvaluateRequest) (*policy.EvaluateResult, error) {
pol, err := s.storage.Policy(ctx, req.PolicyName, req.Group, req.Version)
if err != nil {
s.logger.Error("error getting policy from storage", zap.Error(err))
if errors.Is(errors.NotFound, err) {
return nil, err
}
return nil, errors.New("error evaluating policy", err)
}
if pol.Locked {
return nil, errors.New(errors.Forbidden, "policy is locked")
}
query, err := rego.New(
rego.Module(pol.Filename, pol.Rego),
rego.Query("result = data.gaiax.result"),
).PrepareForEval(ctx)
if err != nil {
s.logger.Error("error preparing rego query", zap.Error(err))
return nil, errors.New("error preparing rego query", err)
}
resultSet, err := query.Eval(ctx, rego.EvalInput(req.Data))
if err != nil {
s.logger.Error("error evaluating rego query", zap.Error(err))
return nil, errors.New("error evaluating rego query", err)
}
if len(resultSet) == 0 {
s.logger.Error("policy evaluation result set is empty")
return nil, errors.New("policy evaluation result set is empty")
}
result, ok := resultSet[0].Bindings["result"]
if !ok {
s.logger.Error("policy result bindings not found")
return nil, errors.New("policy result bindings not found")
}
return &policy.EvaluateResult{Result: result}, nil
}
package storage
import "time"
// Temporary hardcoded policy storage as a simple map.
// When we finalize with Steffen how we're going to store
// and synchronize policy files, this will be replaced with
// real policy store.
var policies = map[string]*Policy{
"example:example:1.0": {
Filename: "example_1.0.rego",
Name: "example",
Group: "example",
Version: "1.0",
Locked: false,
LastUpdated: time.Now(),
Rego: `
package gaiax
default result = {}
result = {"taskID":123}
`,
},
}
package storage
import (
"context"
"fmt"
"time"
"code.vereign.com/gaiax/tsa/golib/errors"
)
type Policy struct {
Filename string
Name string
Group string
Version string
Rego string
Locked bool
LastUpdated time.Time
}
type Storage struct{}
func New() *Storage {
return &Storage{}
}
func (s *Storage) Policy(ctx context.Context, name, group, version string) (*Policy, error) {
key := fmt.Sprintf("%s:%s:%s", name, group, version)
policy, ok := policies[key]
if !ok {
return nil, errors.New(errors.NotFound, "policy not found in storage")
}
return policy, nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment