diff --git a/cmd/policy/main.go b/cmd/policy/main.go index 05675f9928cbcb56dbf042b5056e7ad6ea8daa4d..d84be447017b61b5d62381c335093d058767f998 100644 --- a/cmd/policy/main.go +++ b/cmd/policy/main.go @@ -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() } diff --git a/internal/service/policy/service.go b/internal/service/policy/service.go index ed8edcb5878852a986a628cc9c50f237e733de10..6ccad263e5d282dbc84160f25a2775b63f910775 100644 --- a/internal/service/policy/service.go +++ b/internal/service/policy/service.go @@ -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 } diff --git a/internal/storage/policies_tmp_store.go b/internal/storage/policies_tmp_store.go new file mode 100644 index 0000000000000000000000000000000000000000..0bbba0d6782768cf134ae029ab18cf6fc2ee1a9f --- /dev/null +++ b/internal/storage/policies_tmp_store.go @@ -0,0 +1,25 @@ +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} + `, + }, +} diff --git a/internal/storage/storage.go b/internal/storage/storage.go new file mode 100644 index 0000000000000000000000000000000000000000..0a43d6ae09bd80ed824f25362a7fcddde15ea05b --- /dev/null +++ b/internal/storage/storage.go @@ -0,0 +1,36 @@ +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 +}