Implement Policy Enforcer Plugin
- Added a new Policy Enforcer plugin to evaluate incoming messages against OPA policies. - Configurable via YAML with options for policy sources, actions, and query. - Integrated into existing configuration files for BAP and BPP. - Updated related tests and documentation for the new functionality. - Enhanced plugin manager to support Policy Enforcer instantiation.
This commit is contained in:
@@ -83,6 +83,11 @@ func (m *MockPluginManager) SchemaValidator(ctx context.Context, cfg *plugin.Con
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PolicyEnforcer returns a mock implementation of the PolicyEnforcer interface.
|
||||||
|
func (m *MockPluginManager) PolicyEnforcer(ctx context.Context, cfg *plugin.Config) (definition.PolicyEnforcer, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// mockRun is a mock implementation of the `run` function, simulating a successful run.
|
// mockRun is a mock implementation of the `run` function, simulating a successful run.
|
||||||
func mockRun(ctx context.Context, configPath string) error {
|
func mockRun(ctx context.Context, configPath string) error {
|
||||||
return nil // Simulate a successful run
|
return nil // Simulate a successful run
|
||||||
|
|||||||
@@ -86,6 +86,12 @@ modules:
|
|||||||
id: router
|
id: router
|
||||||
config:
|
config:
|
||||||
routingConfig: ./config/local-beckn-one-routing-BAPReceiver.yaml
|
routingConfig: ./config/local-beckn-one-routing-BAPReceiver.yaml
|
||||||
|
policyEnforcer:
|
||||||
|
id: policyenforcer
|
||||||
|
config:
|
||||||
|
policySources: "./policies/compliance.rego"
|
||||||
|
actions: "confirm"
|
||||||
|
query: "data.policy.violations"
|
||||||
middleware:
|
middleware:
|
||||||
- id: reqpreprocessor
|
- id: reqpreprocessor
|
||||||
config:
|
config:
|
||||||
@@ -93,6 +99,7 @@ modules:
|
|||||||
role: bap
|
role: bap
|
||||||
steps:
|
steps:
|
||||||
- validateSign
|
- validateSign
|
||||||
|
- enforcePolicy
|
||||||
- addRoute
|
- addRoute
|
||||||
- validateSchema
|
- validateSchema
|
||||||
|
|
||||||
|
|||||||
@@ -84,8 +84,15 @@ modules:
|
|||||||
id: router
|
id: router
|
||||||
config:
|
config:
|
||||||
routingConfig: ./config/local-beckn-one-routing-BPPReceiver.yaml
|
routingConfig: ./config/local-beckn-one-routing-BPPReceiver.yaml
|
||||||
|
policyEnforcer:
|
||||||
|
id: policyenforcer
|
||||||
|
config:
|
||||||
|
policySources: "./policies/compliance.rego"
|
||||||
|
actions: "confirm"
|
||||||
|
query: "data.policy.violations"
|
||||||
steps:
|
steps:
|
||||||
- validateSign
|
- validateSign
|
||||||
|
- enforcePolicy
|
||||||
- addRoute
|
- addRoute
|
||||||
- validateSchema
|
- validateSchema
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ modules:
|
|||||||
id: router
|
id: router
|
||||||
config:
|
config:
|
||||||
routingConfig: ./config/local-simple-routing.yaml
|
routingConfig: ./config/local-simple-routing.yaml
|
||||||
|
policyEnforcer:
|
||||||
|
id: policyenforcer
|
||||||
|
config:
|
||||||
|
policySources: "./policies/compliance.rego"
|
||||||
|
actions: "confirm"
|
||||||
|
query: "data.policy.violations"
|
||||||
middleware:
|
middleware:
|
||||||
- id: reqpreprocessor
|
- id: reqpreprocessor
|
||||||
config:
|
config:
|
||||||
@@ -73,6 +79,7 @@ modules:
|
|||||||
role: bap
|
role: bap
|
||||||
steps:
|
steps:
|
||||||
- validateSign
|
- validateSign
|
||||||
|
- enforcePolicy
|
||||||
- addRoute
|
- addRoute
|
||||||
|
|
||||||
- name: bapTxnCaller
|
- name: bapTxnCaller
|
||||||
@@ -162,8 +169,15 @@ modules:
|
|||||||
id: router
|
id: router
|
||||||
config:
|
config:
|
||||||
routingConfig: ./config/local-simple-routing-BPPReceiver.yaml
|
routingConfig: ./config/local-simple-routing-BPPReceiver.yaml
|
||||||
|
policyEnforcer:
|
||||||
|
id: policyenforcer
|
||||||
|
config:
|
||||||
|
policySources: "./policies/compliance.rego"
|
||||||
|
actions: "confirm"
|
||||||
|
query: "data.policy.violations"
|
||||||
steps:
|
steps:
|
||||||
- validateSign
|
- validateSign
|
||||||
|
- enforcePolicy
|
||||||
- addRoute
|
- addRoute
|
||||||
|
|
||||||
- name: bppTxnCaller
|
- name: bppTxnCaller
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type PluginManager interface {
|
|||||||
Publisher(ctx context.Context, cfg *plugin.Config) (definition.Publisher, error)
|
Publisher(ctx context.Context, cfg *plugin.Config) (definition.Publisher, error)
|
||||||
Signer(ctx context.Context, cfg *plugin.Config) (definition.Signer, error)
|
Signer(ctx context.Context, cfg *plugin.Config) (definition.Signer, error)
|
||||||
Step(ctx context.Context, cfg *plugin.Config) (definition.Step, error)
|
Step(ctx context.Context, cfg *plugin.Config) (definition.Step, error)
|
||||||
|
PolicyEnforcer(ctx context.Context, cfg *plugin.Config) (definition.PolicyEnforcer, error)
|
||||||
Cache(ctx context.Context, cfg *plugin.Config) (definition.Cache, error)
|
Cache(ctx context.Context, cfg *plugin.Config) (definition.Cache, error)
|
||||||
Registry(ctx context.Context, cfg *plugin.Config) (definition.RegistryLookup, error)
|
Registry(ctx context.Context, cfg *plugin.Config) (definition.RegistryLookup, error)
|
||||||
KeyManager(ctx context.Context, cache definition.Cache, rLookup definition.RegistryLookup, cfg *plugin.Config) (definition.KeyManager, error)
|
KeyManager(ctx context.Context, cache definition.Cache, rLookup definition.RegistryLookup, cfg *plugin.Config) (definition.KeyManager, error)
|
||||||
@@ -37,6 +38,7 @@ const (
|
|||||||
// PluginCfg holds the configuration for various plugins.
|
// PluginCfg holds the configuration for various plugins.
|
||||||
type PluginCfg struct {
|
type PluginCfg struct {
|
||||||
SchemaValidator *plugin.Config `yaml:"schemaValidator,omitempty"`
|
SchemaValidator *plugin.Config `yaml:"schemaValidator,omitempty"`
|
||||||
|
PolicyEnforcer *plugin.Config `yaml:"policyEnforcer,omitempty"`
|
||||||
SignValidator *plugin.Config `yaml:"signValidator,omitempty"`
|
SignValidator *plugin.Config `yaml:"signValidator,omitempty"`
|
||||||
Publisher *plugin.Config `yaml:"publisher,omitempty"`
|
Publisher *plugin.Config `yaml:"publisher,omitempty"`
|
||||||
Signer *plugin.Config `yaml:"signer,omitempty"`
|
Signer *plugin.Config `yaml:"signer,omitempty"`
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type stdHandler struct {
|
|||||||
registry definition.RegistryLookup
|
registry definition.RegistryLookup
|
||||||
km definition.KeyManager
|
km definition.KeyManager
|
||||||
schemaValidator definition.SchemaValidator
|
schemaValidator definition.SchemaValidator
|
||||||
|
policyEnforcer definition.PolicyEnforcer
|
||||||
router definition.Router
|
router definition.Router
|
||||||
publisher definition.Publisher
|
publisher definition.Publisher
|
||||||
transportWrapper definition.TransportWrapper
|
transportWrapper definition.TransportWrapper
|
||||||
@@ -318,6 +319,9 @@ func (h *stdHandler) initPlugins(ctx context.Context, mgr PluginManager, cfg *Pl
|
|||||||
if h.transportWrapper, err = loadPlugin(ctx, "TransportWrapper", cfg.TransportWrapper, mgr.TransportWrapper); err != nil {
|
if h.transportWrapper, err = loadPlugin(ctx, "TransportWrapper", cfg.TransportWrapper, mgr.TransportWrapper); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if h.policyEnforcer, err = loadPlugin(ctx, "PolicyEnforcer", cfg.PolicyEnforcer, mgr.PolicyEnforcer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Debugf(ctx, "All required plugins successfully loaded for stdHandler")
|
log.Debugf(ctx, "All required plugins successfully loaded for stdHandler")
|
||||||
return nil
|
return nil
|
||||||
@@ -350,6 +354,8 @@ func (h *stdHandler) initSteps(ctx context.Context, mgr PluginManager, cfg *Conf
|
|||||||
s, err = newValidateSchemaStep(h.schemaValidator)
|
s, err = newValidateSchemaStep(h.schemaValidator)
|
||||||
case "addRoute":
|
case "addRoute":
|
||||||
s, err = newAddRouteStep(h.router)
|
s, err = newAddRouteStep(h.router)
|
||||||
|
case "enforcePolicy":
|
||||||
|
s, err = newEnforcePolicyStep(h.policyEnforcer)
|
||||||
default:
|
default:
|
||||||
if customStep, exists := steps[step]; exists {
|
if customStep, exists := steps[step]; exists {
|
||||||
s = customStep
|
s = customStep
|
||||||
|
|||||||
@@ -315,3 +315,11 @@ func extractSchemaVersion(body []byte) string {
|
|||||||
}
|
}
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newEnforcePolicyStep creates and returns the enforcePolicy step after validation.
|
||||||
|
func newEnforcePolicyStep(policyEnforcer definition.PolicyEnforcer) (definition.Step, error) {
|
||||||
|
if policyEnforcer == nil {
|
||||||
|
return nil, fmt.Errorf("invalid config: PolicyEnforcer plugin not configured")
|
||||||
|
}
|
||||||
|
return policyEnforcer, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ func (m *mockPluginManager) SchemaValidator(ctx context.Context, cfg *plugin.Con
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PolicyEnforcer returns a mock policy enforcer implementation.
|
||||||
|
func (m *mockPluginManager) PolicyEnforcer(ctx context.Context, cfg *plugin.Config) (definition.PolicyEnforcer, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TestRegisterSuccess tests scenarios where the handler registration should succeed.
|
// TestRegisterSuccess tests scenarios where the handler registration should succeed.
|
||||||
func TestRegisterSuccess(t *testing.T) {
|
func TestRegisterSuccess(t *testing.T) {
|
||||||
mCfgs := []Config{
|
mCfgs := []Config{
|
||||||
|
|||||||
44
go.mod
44
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/beckn-one/beckn-onix
|
module github.com/beckn-one/beckn-onix
|
||||||
|
|
||||||
go 1.24.0
|
go 1.24.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||||
@@ -22,16 +22,20 @@ require github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03
|
|||||||
require golang.org/x/text v0.33.0 // indirect
|
require golang.org/x/text v0.33.0 // indirect
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/go-openapi/swag v0.23.0 // indirect
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
@@ -41,33 +45,52 @@ require (
|
|||||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
|
||||||
|
github.com/lestrrat-go/dsig v1.0.0 // indirect
|
||||||
|
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||||
|
github.com/lestrrat-go/httprc/v3 v3.0.2 // indirect
|
||||||
|
github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect
|
||||||
|
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||||
github.com/prometheus/client_model v0.6.0 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.45.0 // indirect
|
github.com/prometheus/common v0.66.1 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.17.0 // indirect
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
|
||||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 // indirect
|
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 // indirect
|
||||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||||
|
github.com/segmentio/asm v1.2.1 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
|
||||||
|
github.com/valyala/fastjson v1.6.7 // indirect
|
||||||
|
github.com/vektah/gqlparser/v2 v2.5.31 // indirect
|
||||||
github.com/woodsbury/decimal128 v1.3.0 // indirect
|
github.com/woodsbury/decimal128 v1.3.0 // indirect
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
|
github.com/yashtewari/glob-intersection v0.2.0 // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
|
||||||
go.opentelemetry.io/otel/log v0.16.0 // indirect
|
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||||
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/net v0.49.0 // indirect
|
golang.org/x/net v0.49.0 // indirect
|
||||||
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.1 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -76,16 +99,19 @@ require (
|
|||||||
github.com/hashicorp/go-retryablehttp v0.7.7
|
github.com/hashicorp/go-retryablehttp v0.7.7
|
||||||
github.com/hashicorp/vault/api v1.16.0
|
github.com/hashicorp/vault/api v1.16.0
|
||||||
github.com/jsonata-go/jsonata v0.0.0-20250709164031-599f35f32e5f
|
github.com/jsonata-go/jsonata v0.0.0-20250709164031-599f35f32e5f
|
||||||
github.com/prometheus/client_golang v1.18.0
|
github.com/open-policy-agent/opa v1.13.2
|
||||||
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0
|
github.com/rabbitmq/amqp091-go v1.10.0
|
||||||
github.com/redis/go-redis/extra/redisotel/v9 v9.16.0
|
github.com/redis/go-redis/extra/redisotel/v9 v9.16.0
|
||||||
github.com/redis/go-redis/v9 v9.16.0
|
github.com/redis/go-redis/v9 v9.16.0
|
||||||
github.com/rs/zerolog v1.34.0
|
github.com/rs/zerolog v1.34.0
|
||||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0
|
go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0
|
||||||
go.opentelemetry.io/otel v1.40.0
|
go.opentelemetry.io/otel v1.40.0
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.16.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0
|
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.46.0
|
go.opentelemetry.io/otel/exporters/prometheus v0.46.0
|
||||||
|
go.opentelemetry.io/otel/log v0.16.0
|
||||||
go.opentelemetry.io/otel/metric v1.40.0
|
go.opentelemetry.io/otel/metric v1.40.0
|
||||||
go.opentelemetry.io/otel/sdk v1.40.0
|
go.opentelemetry.io/otel/sdk v1.40.0
|
||||||
go.opentelemetry.io/otel/sdk/log v0.16.0
|
go.opentelemetry.io/otel/sdk/log v0.16.0
|
||||||
|
|||||||
137
go.sum
137
go.sum
@@ -1,3 +1,9 @@
|
|||||||
|
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
|
||||||
|
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
|
||||||
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||||
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||||
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
@@ -6,6 +12,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
|||||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
|
github.com/bytecodealliance/wasmtime-go/v39 v39.0.1 h1:RibaT47yiyCRxMOj/l2cvL8cWiWBSqDXHyqsa9sGcCE=
|
||||||
|
github.com/bytecodealliance/wasmtime-go/v39 v39.0.1/go.mod h1:miR4NYIEBXeDNamZIzpskhJ0z/p8al+lwMWylQ/ZJb4=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||||
@@ -17,13 +25,29 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||||
|
github.com/dgraph-io/badger/v4 v4.9.0 h1:tpqWb0NewSrCYqTvywbcXOhQdWcqephkVkbBmaaqHzc=
|
||||||
|
github.com/dgraph-io/badger/v4 v4.9.0/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0=
|
||||||
|
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
|
||||||
|
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
|
||||||
|
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||||
|
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
|
github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZF0=
|
||||||
|
github.com/foxcpp/go-mockdns v1.2.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
|
||||||
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
|
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
|
||||||
github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE=
|
github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||||
@@ -39,15 +63,19 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
|
|||||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
|
||||||
|
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
@@ -79,10 +107,28 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
|
|||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/jsonata-go/jsonata v0.0.0-20250709164031-599f35f32e5f h1:JnGon8QHtmjFPq0NcSu8OTEnQDDEgFME7gtj/NkjCUo=
|
github.com/jsonata-go/jsonata v0.0.0-20250709164031-599f35f32e5f h1:JnGon8QHtmjFPq0NcSu8OTEnQDDEgFME7gtj/NkjCUo=
|
||||||
github.com/jsonata-go/jsonata v0.0.0-20250709164031-599f35f32e5f/go.mod h1:rYUEOEiieWXHNCE/eDXV/o5s7jZ2VyUzQKbqVns9pik=
|
github.com/jsonata-go/jsonata v0.0.0-20250709164031-599f35f32e5f/go.mod h1:rYUEOEiieWXHNCE/eDXV/o5s7jZ2VyUzQKbqVns9pik=
|
||||||
|
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||||
|
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
|
||||||
|
github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38=
|
||||||
|
github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo=
|
||||||
|
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY=
|
||||||
|
github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU=
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||||
|
github.com/lestrrat-go/httprc/v3 v3.0.2 h1:7u4HUaD0NQbf2/n5+fyp+T10hNCsAnwKfqn4A4Baif0=
|
||||||
|
github.com/lestrrat-go/httprc/v3 v3.0.2/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
|
||||||
|
github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0VdSk=
|
||||||
|
github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU=
|
||||||
|
github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss=
|
||||||
|
github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
@@ -93,8 +139,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
|||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
@@ -104,10 +150,14 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
|||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
||||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
||||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
|
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
|
||||||
|
github.com/open-policy-agent/opa v1.13.2 h1:c72l7DhxP4g8DEUBOdaU9QBKyA24dZxCcIuZNRZ0yP4=
|
||||||
|
github.com/open-policy-agent/opa v1.13.2/go.mod h1:M3Asy9yp1YTusUU5VQuENDe92GLmamIuceqjw+C8PHY=
|
||||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -117,16 +167,18 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
|
|||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
|
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||||
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 h1:zAFQyFxJ3QDwpPUY/CKn22LI5+B8m/lUyffzq2+8ENs=
|
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0 h1:zAFQyFxJ3QDwpPUY/CKn22LI5+B8m/lUyffzq2+8ENs=
|
||||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0/go.mod h1:ouOc8ujB2wdUG6o0RrqaPl2tI6cenExC0KkJQ+PHXmw=
|
github.com/redis/go-redis/extra/rediscmd/v9 v9.16.0/go.mod h1:ouOc8ujB2wdUG6o0RrqaPl2tI6cenExC0KkJQ+PHXmw=
|
||||||
github.com/redis/go-redis/extra/redisotel/v9 v9.16.0 h1:+a9h9qxFXdf3gX0FXnDcz7X44ZBFUPq58Gblq7aMU4s=
|
github.com/redis/go-redis/extra/redisotel/v9 v9.16.0 h1:+a9h9qxFXdf3gX0FXnDcz7X44ZBFUPq58Gblq7aMU4s=
|
||||||
@@ -143,20 +195,45 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB
|
|||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||||
|
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
|
||||||
|
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
|
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||||
|
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
|
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||||
|
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
|
github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM=
|
||||||
|
github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||||
|
github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k=
|
||||||
|
github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
|
||||||
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
|
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
|
||||||
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
|
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
|
github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg=
|
||||||
|
github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
|
||||||
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 h1:m1h+vudopHsI67FPT9MOncyndWhTcdUoBtI1R1uajGY=
|
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 h1:m1h+vudopHsI67FPT9MOncyndWhTcdUoBtI1R1uajGY=
|
||||||
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03/go.mod h1:8sheVFH84v3PCyFY/O02mIgSQY9I6wMYPWsq7mDnEZY=
|
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03/go.mod h1:8sheVFH84v3PCyFY/O02mIgSQY9I6wMYPWsq7mDnEZY=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ=
|
||||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0 h1:/+/+UjlXjFcdDlXxKL1PouzX8Z2Vl0OxolRKeBEgYDw=
|
go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0 h1:/+/+UjlXjFcdDlXxKL1PouzX8Z2Vl0OxolRKeBEgYDw=
|
||||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0/go.mod h1:Ldm/PDuzY2DP7IypudopCR3OCOW42NJlN9+mNEroevo=
|
go.opentelemetry.io/contrib/instrumentation/runtime v0.64.0/go.mod h1:Ldm/PDuzY2DP7IypudopCR3OCOW42NJlN9+mNEroevo=
|
||||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||||
@@ -169,6 +246,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNl
|
|||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU=
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ=
|
go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ=
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs=
|
go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs=
|
||||||
go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4=
|
go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4=
|
||||||
@@ -179,6 +258,8 @@ go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ7
|
|||||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||||
go.opentelemetry.io/otel/sdk/log v0.16.0 h1:e/b4bdlQwC5fnGtG3dlXUrNOnP7c8YLVSpSfEBIkTnI=
|
go.opentelemetry.io/otel/sdk/log v0.16.0 h1:e/b4bdlQwC5fnGtG3dlXUrNOnP7c8YLVSpSfEBIkTnI=
|
||||||
go.opentelemetry.io/otel/sdk/log v0.16.0/go.mod h1:JKfP3T6ycy7QEuv3Hj8oKDy7KItrEkus8XJE6EoSzw4=
|
go.opentelemetry.io/otel/sdk/log v0.16.0/go.mod h1:JKfP3T6ycy7QEuv3Hj8oKDy7KItrEkus8XJE6EoSzw4=
|
||||||
|
go.opentelemetry.io/otel/sdk/log/logtest v0.16.0 h1:/XVkpZ41rVRTP4DfMgYv1nEtNmf65XPPyAdqV90TMy4=
|
||||||
|
go.opentelemetry.io/otel/sdk/log/logtest v0.16.0/go.mod h1:iOOPgQr5MY9oac/F5W86mXdeyWZGleIx3uXO98X2R6Y=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||||
@@ -189,47 +270,45 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
|||||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
|
||||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
|
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||||
|
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
|
||||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
|
||||||
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||||
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
|
||||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
|
||||||
|
gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
@@ -237,3 +316,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||||
|
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ plugins=(
|
|||||||
"schemav2validator"
|
"schemav2validator"
|
||||||
"signer"
|
"signer"
|
||||||
"signvalidator"
|
"signvalidator"
|
||||||
|
"policyenforcer"
|
||||||
)
|
)
|
||||||
|
|
||||||
for plugin in "${plugins[@]}"; do
|
for plugin in "${plugins[@]}"; do
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ../config:/app/config
|
- ../config:/app/config
|
||||||
- ../schemas:/app/schemas
|
- ../schemas:/app/schemas
|
||||||
|
- ../policies:/app/policies
|
||||||
command: ["./server", "--config=/app/config/local-beckn-one-bap.yaml"]
|
command: ["./server", "--config=/app/config/local-beckn-one-bap.yaml"]
|
||||||
|
|
||||||
onix-bpp:
|
onix-bpp:
|
||||||
@@ -57,6 +58,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ../config:/app/config
|
- ../config:/app/config
|
||||||
- ../schemas:/app/schemas
|
- ../schemas:/app/schemas
|
||||||
|
- ../policies:/app/policies
|
||||||
command: ["./server", "--config=/app/config/local-beckn-one-bpp.yaml"]
|
command: ["./server", "--config=/app/config/local-beckn-one-bpp.yaml"]
|
||||||
|
|
||||||
sandbox-bap:
|
sandbox-bap:
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ../config:/app/config
|
- ../config:/app/config
|
||||||
- ../schemas:/app/schemas
|
- ../schemas:/app/schemas
|
||||||
|
- ../policies:/app/policies
|
||||||
command: ["./server", "--config=/app/config/local-simple.yaml"]
|
command: ["./server", "--config=/app/config/local-simple.yaml"]
|
||||||
|
|
||||||
# Vault - Key Management Service
|
# Vault - Key Management Service
|
||||||
|
|||||||
17
pkg/plugin/definition/policyEnforcer.go
Normal file
17
pkg/plugin/definition/policyEnforcer.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package definition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/beckn-one/beckn-onix/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PolicyEnforcer interface for policy enforcement on incoming messages.
|
||||||
|
type PolicyEnforcer interface {
|
||||||
|
Run(ctx *model.StepContext) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyEnforcerProvider interface for creating policy enforcers.
|
||||||
|
type PolicyEnforcerProvider interface {
|
||||||
|
New(ctx context.Context, config map[string]string) (PolicyEnforcer, func(), error)
|
||||||
|
}
|
||||||
68
pkg/plugin/implementation/policyenforcer/README.md
Normal file
68
pkg/plugin/implementation/policyenforcer/README.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Policy Enforcer Plugin
|
||||||
|
|
||||||
|
OPA/Rego-based policy enforcement for beckn-onix adapters. Evaluates incoming beckn messages against configurable policies and NACKs non-compliant requests.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `policyenforcer` plugin is a **Step plugin** that:
|
||||||
|
- Loads `.rego` policy files from local directories, files, URLs, or local paths
|
||||||
|
- Evaluates incoming messages against compiled OPA policies
|
||||||
|
- Returns a `BadReqErr` (NACK) when policy violations are detected
|
||||||
|
- Fails closed on evaluation errors (treats as NACK)
|
||||||
|
- Is strictly **opt-in** — adapters that don't reference it are unaffected
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
All config keys are passed via `map[string]string` in the adapter YAML config.
|
||||||
|
|
||||||
|
| Key | Required | Default | Description |
|
||||||
|
|-----|----------|---------|-------------|
|
||||||
|
| `policyDir` | One of `policyDir`, `policyFile`, or `policyUrls` required | — | Local directory containing `.rego` files |
|
||||||
|
| `policyFile` | | — | Single local `.rego` file path |
|
||||||
|
| `policyUrls` | | — | Comma-separated list of URLs or local paths to `.rego` files |
|
||||||
|
| `query` | No | `data.policy.violations` | Rego query returning violation strings |
|
||||||
|
| `actions` | No | `confirm` | Comma-separated beckn actions to enforce |
|
||||||
|
| `enabled` | No | `true` | Enable/disable the plugin |
|
||||||
|
| `debugLogging` | No | `false` | Enable verbose logging |
|
||||||
|
| *any other key* | No | — | Forwarded to Rego as `data.config.<key>` |
|
||||||
|
|
||||||
|
### Policy URLs
|
||||||
|
|
||||||
|
`policyUrls` accepts both remote URLs and local file paths, separated by commas:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
config:
|
||||||
|
policyUrls: "https://policies.example.com/compliance.rego,/etc/policies/local.rego,https://policies.example.com/safety.rego"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Air-Gapped Deployments
|
||||||
|
|
||||||
|
For environments without internet access, replace any URL with a local file path or volume mount:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
config:
|
||||||
|
policyUrls: "/mounted-policies/compliance.rego,/mounted-policies/safety.rego"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Config
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
plugins:
|
||||||
|
steps:
|
||||||
|
- id: policyenforcer
|
||||||
|
config:
|
||||||
|
policyUrls: "https://policies.example.com/compliance.rego,/local/policies/safety.rego"
|
||||||
|
actions: "confirm,init"
|
||||||
|
query: "data.policy.violations"
|
||||||
|
minDeliveryLeadHours: "4"
|
||||||
|
debugLogging: "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Relationship with Schema Validator
|
||||||
|
|
||||||
|
`policyenforcer` and `schemavalidator`/`schemav2validator` are **separate plugins** with different responsibilities:
|
||||||
|
|
||||||
|
- **Schema Validator**: Validates message **structure** against OpenAPI/JSON Schema specs
|
||||||
|
- **Policy Enforcer**: Evaluates **business rules** via OPA/Rego policies
|
||||||
|
|
||||||
|
They use different plugin interfaces (`SchemaValidator` vs `Step`), different engines, and different error types. Configure them side-by-side in your adapter config as needed.
|
||||||
26
pkg/plugin/implementation/policyenforcer/cmd/plugin.go
Normal file
26
pkg/plugin/implementation/policyenforcer/cmd/plugin.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Package main provides the plugin entry point for the Policy Enforcer plugin.
|
||||||
|
// This file is compiled as a Go plugin (.so) and loaded by beckn-onix at runtime.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/beckn-one/beckn-onix/pkg/plugin/definition"
|
||||||
|
"github.com/beckn-one/beckn-onix/pkg/plugin/implementation/policyenforcer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// provider implements the PolicyEnforcerProvider interface for plugin loading.
|
||||||
|
type provider struct{}
|
||||||
|
|
||||||
|
// New creates a new PolicyEnforcer instance.
|
||||||
|
func (p provider) New(ctx context.Context, cfg map[string]string) (definition.PolicyEnforcer, func(), error) {
|
||||||
|
enforcer, err := policyenforcer.New(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return enforcer, enforcer.Close, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider is the exported symbol that beckn-onix plugin manager looks up.
|
||||||
|
var Provider = provider{}
|
||||||
129
pkg/plugin/implementation/policyenforcer/config.go
Normal file
129
pkg/plugin/implementation/policyenforcer/config.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package policyenforcer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config holds the configuration for the Policy Enforcer plugin.
|
||||||
|
type Config struct {
|
||||||
|
// PolicyDir is a local directory containing .rego policy files (all loaded).
|
||||||
|
// At least one policy source (PolicyDir, PolicyFile, or PolicyUrls) is required.
|
||||||
|
PolicyDir string
|
||||||
|
|
||||||
|
// PolicyFile is a single local .rego file path.
|
||||||
|
PolicyFile string
|
||||||
|
|
||||||
|
// PolicyUrls is a list of URLs (or local file paths) pointing to .rego files,
|
||||||
|
// fetched at startup or read from disk.
|
||||||
|
// Parsed from the comma-separated "policyUrls" config key.
|
||||||
|
PolicyUrls []string
|
||||||
|
|
||||||
|
// Query is the Rego query that returns a set of violation strings.
|
||||||
|
// Default: "data.policy.violations"
|
||||||
|
Query string
|
||||||
|
|
||||||
|
// Actions is the list of beckn actions to enforce policies on.
|
||||||
|
// Default: ["confirm"]
|
||||||
|
Actions []string
|
||||||
|
|
||||||
|
// Enabled controls whether the plugin is active.
|
||||||
|
Enabled bool
|
||||||
|
|
||||||
|
// DebugLogging enables verbose logging.
|
||||||
|
DebugLogging bool
|
||||||
|
|
||||||
|
// RuntimeConfig holds arbitrary key-value pairs passed to Rego as data.config.
|
||||||
|
// Keys like minDeliveryLeadHours are forwarded here.
|
||||||
|
RuntimeConfig map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Known config keys that are handled directly (not forwarded to RuntimeConfig).
|
||||||
|
var knownKeys = map[string]bool{
|
||||||
|
"policyDir": true,
|
||||||
|
"policyFile": true,
|
||||||
|
"policyUrls": true,
|
||||||
|
"query": true,
|
||||||
|
"actions": true,
|
||||||
|
"enabled": true,
|
||||||
|
"debugLogging": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfig returns a Config with sensible defaults.
|
||||||
|
func DefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
Query: "data.policy.violations",
|
||||||
|
Actions: []string{"confirm"},
|
||||||
|
Enabled: true,
|
||||||
|
DebugLogging: false,
|
||||||
|
RuntimeConfig: make(map[string]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseConfig parses the plugin configuration map into a Config struct.
|
||||||
|
func ParseConfig(cfg map[string]string) (*Config, error) {
|
||||||
|
config := DefaultConfig()
|
||||||
|
|
||||||
|
if dir, ok := cfg["policyDir"]; ok && dir != "" {
|
||||||
|
config.PolicyDir = dir
|
||||||
|
}
|
||||||
|
if file, ok := cfg["policyFile"]; ok && file != "" {
|
||||||
|
config.PolicyFile = file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy: comma-separated policyUrls
|
||||||
|
if urls, ok := cfg["policyUrls"]; ok && urls != "" {
|
||||||
|
for _, u := range strings.Split(urls, ",") {
|
||||||
|
u = strings.TrimSpace(u)
|
||||||
|
if u != "" {
|
||||||
|
config.PolicyUrls = append(config.PolicyUrls, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.PolicyDir == "" && config.PolicyFile == "" && len(config.PolicyUrls) == 0 {
|
||||||
|
return nil, fmt.Errorf("at least one policy source is required (policyDir, policyFile, or policyUrls)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if query, ok := cfg["query"]; ok && query != "" {
|
||||||
|
config.Query = query
|
||||||
|
}
|
||||||
|
|
||||||
|
if actions, ok := cfg["actions"]; ok && actions != "" {
|
||||||
|
actionList := strings.Split(actions, ",")
|
||||||
|
config.Actions = make([]string, 0, len(actionList))
|
||||||
|
for _, action := range actionList {
|
||||||
|
action = strings.TrimSpace(action)
|
||||||
|
if action != "" {
|
||||||
|
config.Actions = append(config.Actions, action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled, ok := cfg["enabled"]; ok {
|
||||||
|
config.Enabled = enabled == "true" || enabled == "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug, ok := cfg["debugLogging"]; ok {
|
||||||
|
config.DebugLogging = debug == "true" || debug == "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward unknown keys to RuntimeConfig (e.g., minDeliveryLeadHours)
|
||||||
|
for k, v := range cfg {
|
||||||
|
if !knownKeys[k] {
|
||||||
|
config.RuntimeConfig[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsActionEnabled checks if the given action is in the configured actions list.
|
||||||
|
func (c *Config) IsActionEnabled(action string) bool {
|
||||||
|
for _, a := range c.Actions {
|
||||||
|
if a == action {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
106
pkg/plugin/implementation/policyenforcer/enforcer.go
Normal file
106
pkg/plugin/implementation/policyenforcer/enforcer.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package policyenforcer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/beckn-one/beckn-onix/pkg/log"
|
||||||
|
"github.com/beckn-one/beckn-onix/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PolicyEnforcer is a Step plugin that evaluates beckn messages against
|
||||||
|
// OPA policies and NACKs non-compliant messages.
|
||||||
|
type PolicyEnforcer struct {
|
||||||
|
config *Config
|
||||||
|
evaluator *Evaluator
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new PolicyEnforcer instance.
|
||||||
|
func New(cfg map[string]string) (*PolicyEnforcer, error) {
|
||||||
|
config, err := ParseConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("policyenforcer: config error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluator, err := NewEvaluator(config.PolicyDir, config.PolicyFile, config.PolicyUrls, config.Query, config.RuntimeConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("policyenforcer: failed to initialize OPA evaluator: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(context.TODO(), "PolicyEnforcer initialized (actions=%v, query=%s, policies=%v, debugLogging=%v)",
|
||||||
|
config.Actions, config.Query, evaluator.ModuleNames(), config.DebugLogging)
|
||||||
|
|
||||||
|
return &PolicyEnforcer{
|
||||||
|
config: config,
|
||||||
|
evaluator: evaluator,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements the Step interface. It evaluates the message body against
|
||||||
|
// loaded OPA policies. Returns a BadReqErr (causing NACK) if violations are found.
|
||||||
|
// Returns an error on evaluation failure (fail closed).
|
||||||
|
func (e *PolicyEnforcer) Run(ctx *model.StepContext) error {
|
||||||
|
if !e.config.Enabled {
|
||||||
|
log.Debug(ctx, "PolicyEnforcer: plugin disabled, skipping")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract action from the message
|
||||||
|
action := extractAction(ctx.Request.URL.Path, ctx.Body)
|
||||||
|
|
||||||
|
if !e.config.IsActionEnabled(action) {
|
||||||
|
if e.config.DebugLogging {
|
||||||
|
log.Debugf(ctx, "PolicyEnforcer: action %q not in configured actions %v, skipping", action, e.config.Actions)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.config.DebugLogging {
|
||||||
|
log.Debugf(ctx, "PolicyEnforcer: evaluating policies for action %q (modules=%v)", action, e.evaluator.ModuleNames())
|
||||||
|
}
|
||||||
|
|
||||||
|
violations, err := e.evaluator.Evaluate(ctx, ctx.Body)
|
||||||
|
if err != nil {
|
||||||
|
// Fail closed: evaluation error → NACK
|
||||||
|
log.Errorf(ctx, err, "PolicyEnforcer: policy evaluation failed: %v", err)
|
||||||
|
return model.NewBadReqErr(fmt.Errorf("policy evaluation error: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(violations) == 0 {
|
||||||
|
if e.config.DebugLogging {
|
||||||
|
log.Debugf(ctx, "PolicyEnforcer: message compliant for action %q", action)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-compliant: NACK with all violation messages
|
||||||
|
msg := fmt.Sprintf("policy violation(s): %s", strings.Join(violations, "; "))
|
||||||
|
log.Warnf(ctx, "PolicyEnforcer: %s", msg)
|
||||||
|
return model.NewBadReqErr(fmt.Errorf("%s", msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is a no-op for the policy enforcer (no resources to release).
|
||||||
|
func (e *PolicyEnforcer) Close() {}
|
||||||
|
|
||||||
|
// extractAction gets the beckn action from the URL path or message body.
|
||||||
|
func extractAction(urlPath string, body []byte) string {
|
||||||
|
// Try URL path first: /bap/receiver/{action} or /bpp/caller/{action}
|
||||||
|
parts := strings.Split(strings.Trim(urlPath, "/"), "/")
|
||||||
|
if len(parts) >= 3 {
|
||||||
|
return parts[len(parts)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: extract from body context.action
|
||||||
|
var payload struct {
|
||||||
|
Context struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
} `json:"context"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &payload); err == nil && payload.Context.Action != "" {
|
||||||
|
return payload.Context.Action
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
518
pkg/plugin/implementation/policyenforcer/enforcer_test.go
Normal file
518
pkg/plugin/implementation/policyenforcer/enforcer_test.go
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
package policyenforcer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/beckn-one/beckn-onix/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helper: create a StepContext with the given action path and JSON body.
|
||||||
|
func makeStepCtx(action string, body string) *model.StepContext {
|
||||||
|
req, _ := http.NewRequest("POST", "/bpp/caller/"+action, nil)
|
||||||
|
return &model.StepContext{
|
||||||
|
Context: context.Background(),
|
||||||
|
Request: req,
|
||||||
|
Body: []byte(body),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: write a .rego file to a temp dir and return the dir path.
|
||||||
|
func writePolicyDir(t *testing.T, filename, content string) string {
|
||||||
|
t.Helper()
|
||||||
|
dir := t.TempDir()
|
||||||
|
err := os.WriteFile(filepath.Join(dir, filename), []byte(content), 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to write policy file: %v", err)
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Config Tests ---
|
||||||
|
|
||||||
|
func TestParseConfig_RequiresPolicySource(t *testing.T) {
|
||||||
|
_, err := ParseConfig(map[string]string{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error when no policyDir, policyFile, or policyUrls given")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseConfig_Defaults(t *testing.T) {
|
||||||
|
cfg, err := ParseConfig(map[string]string{"policyDir": "/tmp"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if cfg.Query != "data.policy.violations" {
|
||||||
|
t.Errorf("expected default query, got %q", cfg.Query)
|
||||||
|
}
|
||||||
|
if len(cfg.Actions) != 1 || cfg.Actions[0] != "confirm" {
|
||||||
|
t.Errorf("expected default actions [confirm], got %v", cfg.Actions)
|
||||||
|
}
|
||||||
|
if !cfg.Enabled {
|
||||||
|
t.Error("expected enabled=true by default")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseConfig_RuntimeConfigForwarding(t *testing.T) {
|
||||||
|
cfg, err := ParseConfig(map[string]string{
|
||||||
|
"policyDir": "/tmp",
|
||||||
|
"minDeliveryLeadHours": "6",
|
||||||
|
"customParam": "value",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if cfg.RuntimeConfig["minDeliveryLeadHours"] != "6" {
|
||||||
|
t.Errorf("expected minDeliveryLeadHours=6, got %q", cfg.RuntimeConfig["minDeliveryLeadHours"])
|
||||||
|
}
|
||||||
|
if cfg.RuntimeConfig["customParam"] != "value" {
|
||||||
|
t.Errorf("expected customParam=value, got %q", cfg.RuntimeConfig["customParam"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseConfig_CustomActions(t *testing.T) {
|
||||||
|
cfg, err := ParseConfig(map[string]string{
|
||||||
|
"policyDir": "/tmp",
|
||||||
|
"actions": "confirm, select, init",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.Actions) != 3 {
|
||||||
|
t.Fatalf("expected 3 actions, got %d: %v", len(cfg.Actions), cfg.Actions)
|
||||||
|
}
|
||||||
|
expected := []string{"confirm", "select", "init"}
|
||||||
|
for i, want := range expected {
|
||||||
|
if cfg.Actions[i] != want {
|
||||||
|
t.Errorf("action[%d] = %q, want %q", i, cfg.Actions[i], want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseConfig_PolicyUrls(t *testing.T) {
|
||||||
|
cfg, err := ParseConfig(map[string]string{
|
||||||
|
"policyUrls": "https://example.com/a.rego, https://example.com/b.rego",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.PolicyUrls) != 2 {
|
||||||
|
t.Fatalf("expected 2 URLs, got %d: %v", len(cfg.PolicyUrls), cfg.PolicyUrls)
|
||||||
|
}
|
||||||
|
if cfg.PolicyUrls[0] != "https://example.com/a.rego" {
|
||||||
|
t.Errorf("url[0] = %q", cfg.PolicyUrls[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: policySources support was removed; we intentionally only support
|
||||||
|
// comma-separated policyUrls and local paths via policyUrls entries.
|
||||||
|
|
||||||
|
// --- Evaluator Tests (with inline policies) ---
|
||||||
|
|
||||||
|
func TestEvaluator_NoViolations(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains msg if {
|
||||||
|
input.value < 0
|
||||||
|
msg := "value is negative"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "test.rego", policy)
|
||||||
|
eval, err := NewEvaluator(dir, "", nil, "data.policy.violations", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewEvaluator failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
violations, err := eval.Evaluate(context.Background(), []byte(`{"value": 10}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 0 {
|
||||||
|
t.Errorf("expected 0 violations, got %d: %v", len(violations), violations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvaluator_WithViolation(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains msg if {
|
||||||
|
input.value < 0
|
||||||
|
msg := "value is negative"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "test.rego", policy)
|
||||||
|
eval, err := NewEvaluator(dir, "", nil, "data.policy.violations", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewEvaluator failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
violations, err := eval.Evaluate(context.Background(), []byte(`{"value": -5}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 1 {
|
||||||
|
t.Fatalf("expected 1 violation, got %d: %v", len(violations), violations)
|
||||||
|
}
|
||||||
|
if violations[0] != "value is negative" {
|
||||||
|
t.Errorf("unexpected violation: %q", violations[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvaluator_RuntimeConfig(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains msg if {
|
||||||
|
input.value > to_number(data.config.maxValue)
|
||||||
|
msg := "value exceeds maximum"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "test.rego", policy)
|
||||||
|
eval, err := NewEvaluator(dir, "", nil, "data.policy.violations", map[string]string{"maxValue": "100"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewEvaluator failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Under limit
|
||||||
|
violations, err := eval.Evaluate(context.Background(), []byte(`{"value": 50}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 0 {
|
||||||
|
t.Errorf("expected 0 violations for value=50, got %v", violations)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Over limit
|
||||||
|
violations, err = eval.Evaluate(context.Background(), []byte(`{"value": 150}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 1 {
|
||||||
|
t.Errorf("expected 1 violation for value=150, got %v", violations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvaluator_SkipsTestFiles(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "always" if { true }
|
||||||
|
`
|
||||||
|
os.WriteFile(filepath.Join(dir, "policy.rego"), []byte(policy), 0644)
|
||||||
|
|
||||||
|
// Test file would cause compilation issues if loaded (different package)
|
||||||
|
testFile := `
|
||||||
|
package policy_test
|
||||||
|
import rego.v1
|
||||||
|
import data.policy
|
||||||
|
test_something if { count(policy.violations) > 0 }
|
||||||
|
`
|
||||||
|
os.WriteFile(filepath.Join(dir, "policy_test.rego"), []byte(testFile), 0644)
|
||||||
|
|
||||||
|
eval, err := NewEvaluator(dir, "", nil, "data.policy.violations", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewEvaluator should skip _test.rego files, but failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
violations, err := eval.Evaluate(context.Background(), []byte(`{}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 1 {
|
||||||
|
t.Errorf("expected 1 violation, got %d", len(violations))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvaluator_InvalidJSON(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations := set()
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "test.rego", policy)
|
||||||
|
eval, err := NewEvaluator(dir, "", nil, "data.policy.violations", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewEvaluator failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = eval.Evaluate(context.Background(), []byte(`not json`))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for invalid JSON")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Evaluator URL Fetch Tests ---
|
||||||
|
|
||||||
|
func TestEvaluator_FetchFromURL(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains msg if {
|
||||||
|
input.value < 0
|
||||||
|
msg := "value is negative"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
// Serve the policy via a local HTTP server
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.Write([]byte(policy))
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
eval, err := NewEvaluator("", "", []string{srv.URL + "/test_policy.rego"}, "data.policy.violations", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewEvaluator with URL failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compliant
|
||||||
|
violations, err := eval.Evaluate(context.Background(), []byte(`{"value": 10}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 0 {
|
||||||
|
t.Errorf("expected 0 violations, got %v", violations)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-compliant
|
||||||
|
violations, err = eval.Evaluate(context.Background(), []byte(`{"value": -1}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 1 {
|
||||||
|
t.Errorf("expected 1 violation, got %v", violations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvaluator_FetchURL_NotFound(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.NotFoundHandler())
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
_, err := NewEvaluator("", "", []string{srv.URL + "/missing.rego"}, "data.policy.violations", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for 404 URL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvaluator_FetchURL_InvalidScheme(t *testing.T) {
|
||||||
|
_, err := NewEvaluator("", "", []string{"ftp://example.com/policy.rego"}, "data.policy.violations", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for ftp:// scheme")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvaluator_MixedLocalAndURL(t *testing.T) {
|
||||||
|
// Local policy
|
||||||
|
localPolicy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "local_violation" if { input.local_bad }
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "local.rego", localPolicy)
|
||||||
|
|
||||||
|
// Remote policy (different rule, same package)
|
||||||
|
remotePolicy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "remote_violation" if { input.remote_bad }
|
||||||
|
`
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(remotePolicy))
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
eval, err := NewEvaluator(dir, "", []string{srv.URL + "/remote.rego"}, "data.policy.violations", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewEvaluator failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger both violations
|
||||||
|
violations, err := eval.Evaluate(context.Background(), []byte(`{"local_bad": true, "remote_bad": true}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 2 {
|
||||||
|
t.Errorf("expected 2 violations (local+remote), got %d: %v", len(violations), violations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Evaluator with local file path in policySources ---
|
||||||
|
|
||||||
|
func TestEvaluator_LocalFilePath(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "from_file" if { input.bad }
|
||||||
|
`
|
||||||
|
dir := t.TempDir()
|
||||||
|
policyPath := filepath.Join(dir, "local_policy.rego")
|
||||||
|
os.WriteFile(policyPath, []byte(policy), 0644)
|
||||||
|
|
||||||
|
eval, err := NewEvaluator("", "", []string{policyPath}, "data.policy.violations", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewEvaluator with local path failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
violations, err := eval.Evaluate(context.Background(), []byte(`{"bad": true}`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Evaluate failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(violations) != 1 || violations[0] != "from_file" {
|
||||||
|
t.Errorf("expected [from_file], got %v", violations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Enforcer Integration Tests ---
|
||||||
|
|
||||||
|
func TestEnforcer_Compliant(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "blocked" if { input.context.action == "confirm"; input.block }
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "test.rego", policy)
|
||||||
|
|
||||||
|
enforcer, err := New(map[string]string{
|
||||||
|
"policyDir": dir,
|
||||||
|
"actions": "confirm",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := makeStepCtx("confirm", `{"context": {"action": "confirm"}, "block": false}`)
|
||||||
|
err = enforcer.Run(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil error for compliant message, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnforcer_NonCompliant(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "blocked" if { input.context.action == "confirm" }
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "test.rego", policy)
|
||||||
|
|
||||||
|
enforcer, err := New(map[string]string{
|
||||||
|
"policyDir": dir,
|
||||||
|
"actions": "confirm",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := makeStepCtx("confirm", `{"context": {"action": "confirm"}}`)
|
||||||
|
err = enforcer.Run(ctx)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for non-compliant message, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be a BadReqErr
|
||||||
|
if _, ok := err.(*model.BadReqErr); !ok {
|
||||||
|
t.Errorf("expected *model.BadReqErr, got %T: %v", err, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnforcer_SkipsNonMatchingAction(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "blocked" if { true }
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "test.rego", policy)
|
||||||
|
|
||||||
|
enforcer, err := New(map[string]string{
|
||||||
|
"policyDir": dir,
|
||||||
|
"actions": "confirm",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-compliant body, but action is "search" — not in configured actions
|
||||||
|
ctx := makeStepCtx("search", `{"context": {"action": "search"}}`)
|
||||||
|
err = enforcer.Run(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil for non-matching action, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnforcer_DisabledPlugin(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "blocked" if { true }
|
||||||
|
`
|
||||||
|
dir := writePolicyDir(t, "test.rego", policy)
|
||||||
|
|
||||||
|
enforcer, err := New(map[string]string{
|
||||||
|
"policyDir": dir,
|
||||||
|
"enabled": "false",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := makeStepCtx("confirm", `{"context": {"action": "confirm"}}`)
|
||||||
|
err = enforcer.Run(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected nil for disabled plugin, got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Enforcer with URL-sourced policy ---
|
||||||
|
|
||||||
|
func TestEnforcer_PolicyFromURL(t *testing.T) {
|
||||||
|
policy := `
|
||||||
|
package policy
|
||||||
|
import rego.v1
|
||||||
|
violations contains "blocked" if { input.context.action == "confirm" }
|
||||||
|
`
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(policy))
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
enforcer, err := New(map[string]string{
|
||||||
|
"policyUrls": srv.URL + "/block_confirm.rego",
|
||||||
|
"actions": "confirm",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := makeStepCtx("confirm", `{"context": {"action": "confirm"}}`)
|
||||||
|
err = enforcer.Run(ctx)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error from URL-sourced policy, got nil")
|
||||||
|
}
|
||||||
|
if _, ok := err.(*model.BadReqErr); !ok {
|
||||||
|
t.Errorf("expected *model.BadReqErr, got %T", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- extractAction Tests ---
|
||||||
|
|
||||||
|
func TestExtractAction_FromURL(t *testing.T) {
|
||||||
|
action := extractAction("/bpp/caller/confirm", nil)
|
||||||
|
if action != "confirm" {
|
||||||
|
t.Errorf("expected 'confirm', got %q", action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractAction_FromBody(t *testing.T) {
|
||||||
|
body := []byte(`{"context": {"action": "select"}}`)
|
||||||
|
action := extractAction("/x", body)
|
||||||
|
if action != "select" {
|
||||||
|
t.Errorf("expected 'select', got %q", action)
|
||||||
|
}
|
||||||
|
}
|
||||||
238
pkg/plugin/implementation/policyenforcer/evaluator.go
Normal file
238
pkg/plugin/implementation/policyenforcer/evaluator.go
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
package policyenforcer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/open-policy-agent/opa/v1/ast"
|
||||||
|
"github.com/open-policy-agent/opa/v1/rego"
|
||||||
|
"github.com/open-policy-agent/opa/v1/storage/inmem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Evaluator wraps the OPA engine: loads and compiles .rego files at startup,
|
||||||
|
// then evaluates messages against the compiled policy set.
|
||||||
|
type Evaluator struct {
|
||||||
|
preparedQuery rego.PreparedEvalQuery
|
||||||
|
query string
|
||||||
|
runtimeConfig map[string]string
|
||||||
|
moduleNames []string // names of loaded .rego modules
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModuleNames returns the names of the loaded .rego policy modules.
|
||||||
|
func (e *Evaluator) ModuleNames() []string {
|
||||||
|
return e.moduleNames
|
||||||
|
}
|
||||||
|
|
||||||
|
// policyFetchTimeout is the HTTP timeout for fetching remote .rego files.
|
||||||
|
const policyFetchTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
// maxPolicySize is the maximum size of a single .rego file fetched from a URL (1 MB).
|
||||||
|
const maxPolicySize = 1 << 20
|
||||||
|
|
||||||
|
// NewEvaluator creates an Evaluator by loading .rego files from local paths
|
||||||
|
// and/or URLs, then compiling them. runtimeConfig is passed to Rego as data.config.
|
||||||
|
func NewEvaluator(policyDir, policyFile string, policyUrls []string, query string, runtimeConfig map[string]string) (*Evaluator, error) {
|
||||||
|
modules := make(map[string]string)
|
||||||
|
|
||||||
|
// Load from local directory
|
||||||
|
if policyDir != "" {
|
||||||
|
entries, err := os.ReadDir(policyDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read policy directory %s: %w", policyDir, err)
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(entry.Name(), ".rego") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Skip test files — they shouldn't be compiled into the runtime evaluator
|
||||||
|
if strings.HasSuffix(entry.Name(), "_test.rego") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fpath := filepath.Join(policyDir, entry.Name())
|
||||||
|
data, err := os.ReadFile(fpath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read policy file %s: %w", fpath, err)
|
||||||
|
}
|
||||||
|
modules[entry.Name()] = string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load single local file
|
||||||
|
if policyFile != "" {
|
||||||
|
data, err := os.ReadFile(policyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read policy file %s: %w", policyFile, err)
|
||||||
|
}
|
||||||
|
modules[filepath.Base(policyFile)] = string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load from URLs and local file paths (policyUrls)
|
||||||
|
for _, rawSource := range policyUrls {
|
||||||
|
if isURL(rawSource) {
|
||||||
|
name, content, err := fetchPolicy(rawSource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch policy from %s: %w", rawSource, err)
|
||||||
|
}
|
||||||
|
modules[name] = content
|
||||||
|
} else {
|
||||||
|
// Treat as local file path
|
||||||
|
data, err := os.ReadFile(rawSource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read local policy source %s: %w", rawSource, err)
|
||||||
|
}
|
||||||
|
modules[filepath.Base(rawSource)] = string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(modules) == 0 {
|
||||||
|
return nil, fmt.Errorf("no .rego policy files found from any configured source")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile modules to catch syntax errors early
|
||||||
|
compiler, err := ast.CompileModulesWithOpt(modules, ast.CompileOpts{ParserOptions: ast.ParserOptions{RegoVersion: ast.RegoV1}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to compile rego modules: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build data.config from runtime config
|
||||||
|
store := map[string]interface{}{
|
||||||
|
"config": toInterfaceMap(runtimeConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
pq, err := rego.New(
|
||||||
|
rego.Query(query),
|
||||||
|
rego.Compiler(compiler),
|
||||||
|
rego.Store(inmem.NewFromObject(store)),
|
||||||
|
).PrepareForEval(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to prepare rego query %q: %w", query, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
names := make([]string, 0, len(modules))
|
||||||
|
for name := range modules {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Evaluator{
|
||||||
|
preparedQuery: pq,
|
||||||
|
query: query,
|
||||||
|
runtimeConfig: runtimeConfig,
|
||||||
|
moduleNames: names,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isURL checks if a source string looks like a remote URL.
|
||||||
|
func isURL(source string) bool {
|
||||||
|
return strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchPolicy downloads a .rego file from a URL and returns (filename, content, error).
|
||||||
|
func fetchPolicy(rawURL string) (string, string, error) {
|
||||||
|
parsed, err := url.Parse(rawURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("invalid URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed.Scheme != "http" && parsed.Scheme != "https" {
|
||||||
|
return "", "", fmt.Errorf("unsupported URL scheme %q (only http and https are supported)", parsed.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: policyFetchTimeout}
|
||||||
|
resp, err := client.Get(rawURL)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("HTTP request failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return "", "", fmt.Errorf("HTTP %d from %s", resp.StatusCode, rawURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read with size limit
|
||||||
|
limited := io.LimitReader(resp.Body, maxPolicySize+1)
|
||||||
|
body, err := io.ReadAll(limited)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("failed to read response body: %w", err)
|
||||||
|
}
|
||||||
|
if len(body) > maxPolicySize {
|
||||||
|
return "", "", fmt.Errorf("policy file exceeds maximum size of %d bytes", maxPolicySize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive filename from URL path
|
||||||
|
name := path.Base(parsed.Path)
|
||||||
|
if name == "" || name == "." || name == "/" {
|
||||||
|
name = "policy.rego"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(name, ".rego") {
|
||||||
|
name += ".rego"
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, string(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate runs the compiled policy against a JSON message body.
|
||||||
|
// Returns a list of violation strings (empty = compliant).
|
||||||
|
func (e *Evaluator) Evaluate(ctx context.Context, body []byte) ([]string, error) {
|
||||||
|
var input interface{}
|
||||||
|
if err := json.Unmarshal(body, &input); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse message body as JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rs, err := e.preparedQuery.Eval(ctx, rego.EvalInput(input))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("rego evaluation failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractViolations(rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractViolations pulls string violations from the OPA result set.
|
||||||
|
// The query is expected to return a set of strings.
|
||||||
|
func extractViolations(rs rego.ResultSet) ([]string, error) {
|
||||||
|
if len(rs) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var violations []string
|
||||||
|
for _, result := range rs {
|
||||||
|
for _, expr := range result.Expressions {
|
||||||
|
switch v := expr.Value.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
// Result is a list (from set)
|
||||||
|
for _, item := range v {
|
||||||
|
if s, ok := item.(string); ok {
|
||||||
|
violations = append(violations, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case map[string]interface{}:
|
||||||
|
// OPA sometimes returns sets as maps with string keys
|
||||||
|
for key := range v {
|
||||||
|
violations = append(violations, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return violations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toInterfaceMap converts map[string]string to map[string]interface{} for OPA store.
|
||||||
|
func toInterfaceMap(m map[string]string) map[string]interface{} {
|
||||||
|
result := make(map[string]interface{}, len(m))
|
||||||
|
for k, v := range m {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -257,6 +257,23 @@ func (m *Manager) Step(ctx context.Context, cfg *Config) (definition.Step, error
|
|||||||
return step, error
|
return step, error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PolicyEnforcer returns a PolicyEnforcer instance based on the provided configuration.
|
||||||
|
// It registers a cleanup function for resource management.
|
||||||
|
func (m *Manager) PolicyEnforcer(ctx context.Context, cfg *Config) (definition.PolicyEnforcer, error) {
|
||||||
|
pp, err := provider[definition.PolicyEnforcerProvider](m.plugins, cfg.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load provider for %s: %w", cfg.ID, err)
|
||||||
|
}
|
||||||
|
enforcer, closer, err := pp.New(ctx, cfg.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if closer != nil {
|
||||||
|
m.closers = append(m.closers, closer)
|
||||||
|
}
|
||||||
|
return enforcer, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Cache returns a Cache instance based on the provided configuration.
|
// Cache returns a Cache instance based on the provided configuration.
|
||||||
// It registers a cleanup function for resource management.
|
// It registers a cleanup function for resource management.
|
||||||
func (m *Manager) Cache(ctx context.Context, cfg *Config) (definition.Cache, error) {
|
func (m *Manager) Cache(ctx context.Context, cfg *Config) (definition.Cache, error) {
|
||||||
|
|||||||
18
policies/compliance.rego
Normal file
18
policies/compliance.rego
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package policy
|
||||||
|
|
||||||
|
import rego.v1
|
||||||
|
|
||||||
|
# Example policy: validate confirm action messages.
|
||||||
|
# This is a sample policy file. Replace with your actual business rules.
|
||||||
|
#
|
||||||
|
# The policy evaluates incoming beckn messages and produces a set of
|
||||||
|
# violation strings. If any violations exist, the adapter will NACK
|
||||||
|
# the request.
|
||||||
|
#
|
||||||
|
# Available inputs:
|
||||||
|
# - input: the full JSON message body
|
||||||
|
# - data.config: runtime config from the adapter config (e.g., minDeliveryLeadHours)
|
||||||
|
|
||||||
|
# violations is the set of policy violation messages.
|
||||||
|
# An empty set means the message is compliant.
|
||||||
|
violations := set()
|
||||||
Reference in New Issue
Block a user