diff --git a/pkg/plugin/definition/signVerifier.go b/pkg/plugin/definition/signVerifier.go deleted file mode 100644 index fe36358..0000000 --- a/pkg/plugin/definition/signVerifier.go +++ /dev/null @@ -1,22 +0,0 @@ -package definition - -import "context" - -// Verifier defines the method for verifying signatures. -type Verifier interface { - // Verify checks the validity of the signature for the given body. - Verify(ctx context.Context, body []byte, header []byte, publicKeyBase64 string) (bool, error) - Close() error // Close for releasing resources -} - -// VerifierProvider initializes a new Verifier instance with the given config. -type VerifierProvider interface { - // New creates a new Verifier instance based on the provided config. - New(ctx context.Context, config map[string]string) (Verifier, func() error, error) -} - -// PublicKeyManager is the interface for key management plugin. -type PublicKeyManager interface { - // PublicKey retrieves the public key for the given subscriberID and keyID. - PublicKey(ctx context.Context, subscriberID string, keyID string) (string, error) -} diff --git a/pkg/plugin/implementation/requestPreProcessor/cmd/plugin.go b/pkg/plugin/implementation/requestPreProcessor/cmd/plugin.go deleted file mode 100644 index 4d8d3a8..0000000 --- a/pkg/plugin/implementation/requestPreProcessor/cmd/plugin.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "context" - "net/http" - "strings" - - "github.com/beckn/beckn-onix/pkg/plugin/implementation/requestpreprocessor" -) - -type provider struct{} - -func (p provider) New(ctx context.Context, c map[string]string) (func(http.Handler) http.Handler, error) { - config := &requestpreprocessor.Config{} - if contextKeysStr, ok := c["ContextKeys"]; ok { - config.ContextKeys = strings.Split(contextKeysStr, ",") - } - return requestpreprocessor.NewUUIDSetter(config) -} - -var Provider = provider{} diff --git a/pkg/plugin/implementation/requestPreProcessor/cmd/plugin_test.go b/pkg/plugin/implementation/requestPreProcessor/cmd/plugin_test.go deleted file mode 100644 index 0890dbc..0000000 --- a/pkg/plugin/implementation/requestPreProcessor/cmd/plugin_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "context" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// TODO: Will Split this into success and fail (two test cases) -func TestProviderNew(t *testing.T) { - testCases := []struct { - name string - config map[string]string - expectedError bool - expectedStatus int - prepareRequest func(req *http.Request) - }{ - { - name: "No Config", - config: map[string]string{}, - expectedError: true, - expectedStatus: http.StatusOK, - prepareRequest: func(req *http.Request) { - // Add minimal required headers. - req.Header.Set("context", "test-context") - req.Header.Set("transaction_id", "test-transaction") - }, - }, - { - name: "With Check Keys", - config: map[string]string{ - "ContextKeys": "message_id,transaction_id", - }, - expectedError: false, - expectedStatus: http.StatusOK, - prepareRequest: func(req *http.Request) { - // Add headers matching the check keys. - req.Header.Set("context", "test-context") - req.Header.Set("transaction_id", "test-transaction") - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - requestBody := `{ - "context": { - "transaction_id": "abc" - } - }` - - p := provider{} - middleware, err := p.New(context.Background(), tc.config) - if tc.expectedError { - assert.Error(t, err) - return - } - require.NoError(t, err) - assert.NotNil(t, middleware) - - testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }) - - req := httptest.NewRequest("POST", "/", strings.NewReader(requestBody)) - req.Header.Set("Content-Type", "application/json") - if tc.prepareRequest != nil { - tc.prepareRequest(req) - } - - w := httptest.NewRecorder() - middlewaredHandler := middleware(testHandler) - middlewaredHandler.ServeHTTP(w, req) - assert.Equal(t, tc.expectedStatus, w.Code, "Unexpected response status") - responseBody := w.Body.String() - t.Logf("Response Body: %s", responseBody) - - }) - } -} diff --git a/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor.go b/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor.go deleted file mode 100644 index 13d4da0..0000000 --- a/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor.go +++ /dev/null @@ -1,105 +0,0 @@ -package requestpreprocessor - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - - "github.com/google/uuid" -) - -type Config struct { - ContextKeys []string - Role string -} - -type becknRequest struct { - Context map[string]any `json:"context"` -} - -type contextKeyType string - -const contextKey = "context" -const subscriberIDKey contextKeyType = "subscriber_id" - -func NewUUIDSetter(cfg *Config) (func(http.Handler) http.Handler, error) { - if err := validateConfig(cfg); err != nil { - return nil, err - } - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - body, _ := io.ReadAll(r.Body) - var req becknRequest - if err := json.Unmarshal(body, &req); err != nil { - http.Error(w, "Failed to decode request body", http.StatusBadRequest) - return - } - if req.Context == nil { - http.Error(w, fmt.Sprintf("%s field not found.", contextKey), http.StatusBadRequest) - return - } - var subID any - switch cfg.Role { - case "bap": - subID = req.Context["bap_id"] - case "bpp": - subID = req.Context["bpp_id"] - } - ctx := context.WithValue(r.Context(), subscriberIDKey, subID) - for _, key := range cfg.ContextKeys { - value := uuid.NewString() - updatedValue := update(req.Context, key, value) - ctx = context.WithValue(ctx, contextKeyType(key), updatedValue) - } - reqData := map[string]any{"context": req.Context} - updatedBody, _ := json.Marshal(reqData) - r.Body = io.NopCloser(bytes.NewBuffer(updatedBody)) - r.ContentLength = int64(len(updatedBody)) - r = r.WithContext(ctx) - next.ServeHTTP(w, r) - }) - }, nil -} - -func update(wrapper map[string]any, key string, value any) any { - field, exists := wrapper[key] - if !exists || isEmpty(field) { - wrapper[key] = value - return value - } - - return field -} -func isEmpty(v any) bool { - switch v := v.(type) { - case string: - return v == "" - case nil: - return true - default: - return false - } -} - -func validateConfig(cfg *Config) error { - if cfg == nil { - return errors.New("config cannot be nil") - } - - // Check if ContextKeys is empty. - if len(cfg.ContextKeys) == 0 { - return errors.New("ContextKeys cannot be empty") - } - - // Validate that ContextKeys does not contain empty strings. - for _, key := range cfg.ContextKeys { - if key == "" { - return errors.New("ContextKeys cannot contain empty strings") - } - } - return nil -} diff --git a/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor_test.go b/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor_test.go deleted file mode 100644 index 307a7e7..0000000 --- a/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package requestpreprocessor - -import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" -) - -func TestNewUUIDSetterSuccessCases(t *testing.T) { - tests := []struct { - name string - config *Config - requestBody map[string]any - expectedKeys []string - role string - }{ - { - name: "Valid keys, update missing keys with bap role", - config: &Config{ - ContextKeys: []string{"transaction_id", "message_id"}, - Role: "bap", - }, - requestBody: map[string]any{ - "context": map[string]any{ - "transaction_id": "", - "message_id": nil, - "bap_id": "bap-123", - }, - }, - expectedKeys: []string{"transaction_id", "message_id", "bap_id"}, - role: "bap", - }, - { - name: "Valid keys, do not update existing keys with bpp role", - config: &Config{ - ContextKeys: []string{"transaction_id", "message_id"}, - Role: "bpp", - }, - requestBody: map[string]any{ - "context": map[string]any{ - "transaction_id": "existing-transaction", - "message_id": "existing-message", - "bpp_id": "bpp-456", - }, - }, - expectedKeys: []string{"transaction_id", "message_id", "bpp_id"}, - role: "bpp", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - middleware, err := NewUUIDSetter(tt.config) - if err != nil { - t.Fatalf("Unexpected error while creating middleware: %v", err) - } - - bodyBytes, _ := json.Marshal(tt.requestBody) - req := httptest.NewRequest(http.MethodPost, "/test", bytes.NewReader(bodyBytes)) - req.Header.Set("Content-Type", "application/json") - - rec := httptest.NewRecorder() - - dummyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - w.WriteHeader(http.StatusOK) - - subID, ok := ctx.Value(subscriberIDKey).(string) - if !ok { - http.Error(w, "Subscriber ID not found", http.StatusInternalServerError) - return - } - - response := map[string]any{"subscriber_id": subID} - if err := json.NewEncoder(w).Encode(response); err != nil { - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - }) - - middleware(dummyHandler).ServeHTTP(rec, req) - - if rec.Code != http.StatusOK { - t.Errorf("Expected status code 200, but got %d", rec.Code) - return - } - - var responseBody map[string]any - if err := json.Unmarshal(rec.Body.Bytes(), &responseBody); err != nil { - t.Fatal("Failed to unmarshal response body:", err) - } - - expectedSubIDKey := "bap_id" - if tt.role == "bpp" { - expectedSubIDKey = "bpp_id" - } - - subID, ok := responseBody["subscriber_id"].(string) - if !ok { - t.Error("subscriber_id not found in response") - return - } - - expectedSubID := tt.requestBody["context"].(map[string]any)[expectedSubIDKey] - if subID != expectedSubID { - t.Errorf("Expected subscriber_id %v, but got %v", expectedSubID, subID) - } - }) - } -} - -func TestNewUUIDSetterErrorCases(t *testing.T) { - tests := []struct { - name string - config *Config - requestBody map[string]any - expectedCode int - }{ - { - name: "Missing context key", - config: &Config{ - ContextKeys: []string{"transaction_id"}, - }, - requestBody: map[string]any{ - "otherKey": "value", - }, - expectedCode: http.StatusBadRequest, - }, - { - name: "Invalid context type", - config: &Config{ - ContextKeys: []string{"transaction_id"}, - }, - requestBody: map[string]any{ - "context": "not-a-map", - }, - expectedCode: http.StatusBadRequest, - }, - { - name: "Nil config", - config: nil, - requestBody: map[string]any{}, - expectedCode: http.StatusInternalServerError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - middleware, err := NewUUIDSetter(tt.config) - if tt.config == nil { - if err == nil { - t.Error("Expected an error for nil config, but got none") - } - return - } - if err != nil { - t.Fatalf("Unexpected error while creating middleware: %v", err) - } - - bodyBytes, _ := json.Marshal(tt.requestBody) - req := httptest.NewRequest(http.MethodPost, "/test", bytes.NewReader(bodyBytes)) - req.Header.Set("Content-Type", "application/json") - - rec := httptest.NewRecorder() - dummyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }) - - middleware(dummyHandler).ServeHTTP(rec, req) - - if rec.Code != tt.expectedCode { - t.Errorf("Expected status code %d, but got %d", tt.expectedCode, rec.Code) - } - }) - } -} diff --git a/pkg/plugin/implementation/router/router.go b/pkg/plugin/implementation/router/router.go index 9d16767..ba36715 100644 --- a/pkg/plugin/implementation/router/router.go +++ b/pkg/plugin/implementation/router/router.go @@ -9,7 +9,8 @@ import ( "path" "strings" - definition "github.com/beckn/beckn-onix/pkg/plugin/definition" + "github.com/beckn/beckn-onix/pkg/log" + "github.com/beckn/beckn-onix/pkg/model" "gopkg.in/yaml.v3" ) @@ -26,7 +27,7 @@ type routingConfig struct { // Router implements Router interface. type Router struct { - rules map[string]map[string]map[string]*definition.Route // domain -> version -> endpoint -> route + rules map[string]map[string]map[string]*model.Route // domain -> version -> endpoint -> route } // RoutingRule represents a single routing rule. @@ -61,7 +62,7 @@ func New(ctx context.Context, config *Config) (*Router, func() error, error) { return nil, nil, fmt.Errorf("config cannot be nil") } router := &Router{ - rules: make(map[string]map[string]map[string]*definition.Route), + rules: make(map[string]map[string]map[string]*model.Route), } // Load rules at bootup @@ -117,20 +118,20 @@ func (r *Router) loadRules(configPath string) error { for _, rule := range config.RoutingRules { // Initialize domain map if not exists if _, ok := r.rules[rule.Domain]; !ok { - r.rules[rule.Domain] = make(map[string]map[string]*definition.Route) + r.rules[rule.Domain] = make(map[string]map[string]*model.Route) } // Initialize version map if not exists if _, ok := r.rules[rule.Domain][rule.Version]; !ok { - r.rules[rule.Domain][rule.Version] = make(map[string]*definition.Route) + r.rules[rule.Domain][rule.Version] = make(map[string]*model.Route) } // Add all endpoints for this rule for _, endpoint := range rule.Endpoints { - var route *definition.Route + var route *model.Route switch rule.TargetType { case targetTypePublisher: - route = &definition.Route{ + route = &model.Route{ TargetType: rule.TargetType, PublisherID: rule.Target.PublisherID, } @@ -139,7 +140,7 @@ func (r *Router) loadRules(configPath string) error { if err != nil { return fmt.Errorf("invalid URL in rule: %w", err) } - route = &definition.Route{ + route = &model.Route{ TargetType: rule.TargetType, URL: parsedURL, } @@ -151,7 +152,7 @@ func (r *Router) loadRules(configPath string) error { return fmt.Errorf("invalid URL in rule: %w", err) } } - route = &definition.Route{ + route = &model.Route{ TargetType: rule.TargetType, URL: parsedURL, } @@ -199,8 +200,12 @@ func validateRules(rules []routingRule) error { } // Route determines the routing destination based on the request context. -func (r *Router) Route(ctx context.Context, url *url.URL, body []byte) (*definition.Route, error) { +func (r *Router) Route(ctx context.Context, url *url.URL, body []byte) (*model.Route, error) { + if r == nil { + log.Debug(ctx, "In Router :Router not set") + } + log.Debugf(ctx, "In Router: Routing request with url %v and body: %s", url, string(body)) // Parse the body to extract domain and version var requestBody struct { Context struct { @@ -213,10 +218,17 @@ func (r *Router) Route(ctx context.Context, url *url.URL, body []byte) (*definit if err := json.Unmarshal(body, &requestBody); err != nil { return nil, fmt.Errorf("error parsing request body: %w", err) } + log.Debugf(ctx, "In Router: Routing request with %v and body: %#s", url, requestBody) // Extract the endpoint from the URL endpoint := path.Base(url.Path) + if r.rules == nil { + + log.Debug(ctx, "In Router :Routing rules not set") + } + log.Debugf(ctx, "In Router :Routing rules len :%d", len(r.rules)) + // Lookup route in the optimized map domainRules, ok := r.rules[requestBody.Context.Domain] if !ok { @@ -244,7 +256,7 @@ func (r *Router) Route(ctx context.Context, url *url.URL, body []byte) (*definit } // handleProtocolMapping handles both BPP and BAP routing with proper URL construction -func handleProtocolMapping(route *definition.Route, requestURI, endpoint string) (*definition.Route, error) { +func handleProtocolMapping(route *model.Route, requestURI, endpoint string) (*model.Route, error) { uri := strings.TrimSpace(requestURI) var targetURL *url.URL if len(uri) != 0 { @@ -268,7 +280,7 @@ func handleProtocolMapping(route *definition.Route, requestURI, endpoint string) } } - return &definition.Route{ + return &model.Route{ TargetType: targetTypeURL, URL: targetURL, }, nil diff --git a/pkg/plugin/implementation/schemaValidator/cmd/plugin.go b/pkg/plugin/implementation/schemaValidator/cmd/plugin.go deleted file mode 100644 index fb257c9..0000000 --- a/pkg/plugin/implementation/schemaValidator/cmd/plugin.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "context" - "errors" - - "github.com/beckn/beckn-onix/pkg/plugin/definition" - "github.com/beckn/beckn-onix/pkg/plugin/implementation/schemavalidator" -) - -// schemaValidatorProvider provides instances of schemaValidator. -type schemaValidatorProvider struct{} - -// New initializes a new Verifier instance. -func (vp schemaValidatorProvider) New(ctx context.Context, config map[string]string) (definition.SchemaValidator, func() error, error) { - if ctx == nil { - return nil, nil, errors.New("context cannot be nil") - } - - // Extract schemaDir from the config map - schemaDir, ok := config["schemaDir"] - if !ok || schemaDir == "" { - return nil, nil, errors.New("config must contain 'schemaDir'") - } - - // Create a new schemaValidator instance with the provided configuration - return schemavalidator.New(ctx, &schemavalidator.Config{ - SchemaDir: schemaDir, - }) -} - -// Provider is the exported symbol that the plugin manager will look for. -var Provider definition.SchemaValidatorProvider = schemaValidatorProvider{} diff --git a/pkg/plugin/implementation/schemaValidator/cmd/plugin_test.go b/pkg/plugin/implementation/schemaValidator/cmd/plugin_test.go deleted file mode 100644 index 75fdce0..0000000 --- a/pkg/plugin/implementation/schemaValidator/cmd/plugin_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package main - -import ( - "context" - "os" - "path/filepath" - "strings" - "testing" -) - -// setupTestSchema creates a temporary directory and writes a sample schema file. -func setupTestSchema(t *testing.T) string { - t.Helper() - - // Create a temporary directory for the schema - schemaDir, err := os.MkdirTemp("", "schemas") - if err != nil { - t.Fatalf("Failed to create temp directory: %v", err) - } - - // Create the directory structure for the schema file - schemaFilePath := filepath.Join(schemaDir, "example", "1.0", "test_schema.json") - if err := os.MkdirAll(filepath.Dir(schemaFilePath), 0755); err != nil { - t.Fatalf("Failed to create schema directory structure: %v", err) - } - - // Define a sample schema - schemaContent := `{ - "type": "object", - "properties": { - "context": { - "type": "object", - "properties": { - "domain": {"type": "string"}, - "version": {"type": "string"} - }, - "required": ["domain", "version"] - } - }, - "required": ["context"] - }` - - // Write the schema to the file - if err := os.WriteFile(schemaFilePath, []byte(schemaContent), 0644); err != nil { - t.Fatalf("Failed to write schema file: %v", err) - } - - return schemaDir -} - -// TestValidatorProviderSuccess tests successful ValidatorProvider implementation. -func TestValidatorProviderSuccess(t *testing.T) { - schemaDir := setupTestSchema(t) - defer os.RemoveAll(schemaDir) - - // Define test cases. - tests := []struct { - name string - ctx context.Context - config map[string]string - expectedError string - }{ - { - name: "Valid schema directory", - ctx: context.Background(), // Valid context - config: map[string]string{"schemaDir": schemaDir}, - expectedError: "", - }, - } - - // Test using table-driven tests - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - vp := schemaValidatorProvider{} - schemaValidator, _, err := vp.New(tt.ctx, tt.config) - - // Ensure no error occurred - if err != nil { - t.Errorf("unexpected error: %v", err) - return - } - - // Ensure the schemaValidator is not nil - if schemaValidator == nil { - t.Error("expected a non-nil schemaValidator, got nil") - } - }) - } -} - -// TestValidatorProviderSuccess tests cases where ValidatorProvider creation should fail. -func TestValidatorProviderFailure(t *testing.T) { - schemaDir := setupTestSchema(t) - defer os.RemoveAll(schemaDir) - - // Define test cases. - tests := []struct { - name string - ctx context.Context - config map[string]string - expectedError string - }{ - { - name: "Config is empty", - ctx: context.Background(), - config: map[string]string{}, - expectedError: "config must contain 'schemaDir'", - }, - { - name: "schemaDir is empty", - ctx: context.Background(), - config: map[string]string{"schemaDir": ""}, - expectedError: "config must contain 'schemaDir'", - }, - { - name: "Invalid schema directory", - ctx: context.Background(), // Valid context - config: map[string]string{"schemaDir": "/invalid/dir"}, - expectedError: "failed to initialise schemaValidator: schema directory does not exist: /invalid/dir", - }, - { - name: "Nil context", - ctx: nil, // Nil context - config: map[string]string{"schemaDir": schemaDir}, - expectedError: "context cannot be nil", - }, - } - - // Test using table-driven tests - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - vp := schemaValidatorProvider{} - _, _, err := vp.New(tt.ctx, tt.config) - - // Check for expected error - if tt.expectedError != "" { - if err == nil || !strings.Contains(err.Error(), tt.expectedError) { - t.Errorf("expected error %q, got %v", tt.expectedError, err) - } - return - } - - // Ensure no error occurred - if err != nil { - t.Errorf("unexpected error: %v", err) - return - } - }) - } -} diff --git a/pkg/plugin/implementation/schemaValidator/schemaValidator.go b/pkg/plugin/implementation/schemaValidator/schemaValidator.go deleted file mode 100644 index 85f2bf7..0000000 --- a/pkg/plugin/implementation/schemaValidator/schemaValidator.go +++ /dev/null @@ -1,197 +0,0 @@ -package schemavalidator - -import ( - "context" - "encoding/json" - "fmt" - "net/url" - "os" - "path" - "path/filepath" - "strings" - - response "github.com/beckn/beckn-onix/pkg/response" - - "github.com/santhosh-tekuri/jsonschema/v6" -) - -// Payload represents the structure of the data payload with context information. -type payload struct { - Context struct { - Domain string `json:"domain"` - Version string `json:"version"` - } `json:"context"` -} - -// SchemaValidator implements the Validator interface. -type SchemaValidator struct { - config *Config - schemaCache map[string]*jsonschema.Schema -} - -// Config struct for SchemaValidator. -type Config struct { - SchemaDir string -} - -// New creates a new ValidatorProvider instance. -func New(ctx context.Context, config *Config) (*SchemaValidator, func() error, error) { - // Check if config is nil - if config == nil { - return nil, nil, fmt.Errorf("config cannot be nil") - } - v := &SchemaValidator{ - config: config, - schemaCache: make(map[string]*jsonschema.Schema), - } - - // Call Initialise function to load schemas and get validators - if err := v.initialise(); err != nil { - return nil, nil, fmt.Errorf("failed to initialise schemaValidator: %v", err) - } - return v, nil, nil -} - -// Validate validates the given data against the schema. -func (v *SchemaValidator) Validate(ctx context.Context, url *url.URL, data []byte) error { - var payloadData payload - err := json.Unmarshal(data, &payloadData) - if err != nil { - return fmt.Errorf("failed to parse JSON payload: %v", err) - } - - // Extract domain, version, and endpoint from the payload and uri. - cxtDomain := payloadData.Context.Domain - version := payloadData.Context.Version - version = fmt.Sprintf("v%s", version) - - endpoint := path.Base(url.String()) - // ToDo Add debug log here - fmt.Println("Handling request for endpoint:", endpoint) - domain := strings.ToLower(cxtDomain) - domain = strings.ReplaceAll(domain, ":", "_") - - // Construct the schema file name. - schemaFileName := fmt.Sprintf("%s_%s_%s", domain, version, endpoint) - - // Retrieve the schema from the cache. - schema, exists := v.schemaCache[schemaFileName] - if !exists { - return fmt.Errorf("schema not found for domain: %s", schemaFileName) - } - - var jsonData any - if err := json.Unmarshal(data, &jsonData); err != nil { - return fmt.Errorf("failed to parse JSON data: %v", err) - } - err = schema.Validate(jsonData) - if err != nil { - // Handle schema validation errors - if validationErr, ok := err.(*jsonschema.ValidationError); ok { - // Convert validation errors into an array of SchemaValError - var schemaErrors []response.Error - for _, cause := range validationErr.Causes { - // Extract the path and message from the validation error - path := strings.Join(cause.InstanceLocation, ".") // JSON path to the invalid field - message := cause.Error() // Validation error message - - // Append the error to the schemaErrors array - schemaErrors = append(schemaErrors, response.Error{ - Paths: path, - Message: message, - }) - } - // Return the array of schema validation errors - return &response.SchemaValidationErr{Errors: schemaErrors} - } - // Return a generic error for non-validation errors - return fmt.Errorf("validation failed: %v", err) - } - - // Return nil if validation succeeds - return nil -} - -// ValidatorProvider provides instances of Validator. -type ValidatorProvider struct{} - -// Initialise initialises the validator provider by compiling all the JSON schema files -// from the specified directory and storing them in a cache indexed by their schema filenames. -func (v *SchemaValidator) initialise() error { - schemaDir := v.config.SchemaDir - // Check if the directory exists and is accessible. - info, err := os.Stat(schemaDir) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("schema directory does not exist: %s", schemaDir) - } - return fmt.Errorf("failed to access schema directory: %v", err) - } - if !info.IsDir() { - return fmt.Errorf("provided schema path is not a directory: %s", schemaDir) - } - - compiler := jsonschema.NewCompiler() - - // Helper function to process directories recursively. - var processDir func(dir string) error - processDir = func(dir string) error { - entries, err := os.ReadDir(dir) - if err != nil { - return fmt.Errorf("failed to read directory: %v", err) - } - - for _, entry := range entries { - path := filepath.Join(dir, entry.Name()) - if entry.IsDir() { - // Recursively process subdirectories. - if err := processDir(path); err != nil { - return err - } - } else if filepath.Ext(entry.Name()) == ".json" { - // Process JSON files. - compiledSchema, err := compiler.Compile(path) - if err != nil { - return fmt.Errorf("failed to compile JSON schema from file %s: %v", entry.Name(), err) - } - - // Use relative path from schemaDir to avoid absolute paths and make schema keys domain/version specific. - relativePath, err := filepath.Rel(schemaDir, path) - if err != nil { - return fmt.Errorf("failed to get relative path for file %s: %v", entry.Name(), err) - } - // Split the relative path to get domain, version, and schema. - parts := strings.Split(relativePath, string(os.PathSeparator)) - - // Ensure that the file path has at least 3 parts: domain, version, and schema file. - if len(parts) < 3 { - return fmt.Errorf("invalid schema file structure, expected domain/version/schema.json but got: %s", relativePath) - } - - // Extract domain, version, and schema filename from the parts. - // Validate that the extracted parts are non-empty. - domain := strings.TrimSpace(parts[0]) - version := strings.TrimSpace(parts[1]) - schemaFileName := strings.TrimSpace(parts[2]) - schemaFileName = strings.TrimSuffix(schemaFileName, ".json") - - if domain == "" || version == "" || schemaFileName == "" { - return fmt.Errorf("invalid schema file structure, one or more components are empty. Relative path: %s", relativePath) - } - - // Construct a unique key combining domain, version, and schema name (e.g., ondc_trv10_v2.0.0_schema). - uniqueKey := fmt.Sprintf("%s_%s_%s", domain, version, schemaFileName) - // Store the compiled schema in the SchemaCache using the unique key. - v.schemaCache[uniqueKey] = compiledSchema - } - } - return nil - } - - // Start processing from the root schema directory. - if err := processDir(schemaDir); err != nil { - return fmt.Errorf("failed to read schema directory: %v", err) - } - - return nil -} diff --git a/pkg/plugin/implementation/schemaValidator/schemaValidator_test.go b/pkg/plugin/implementation/schemaValidator/schemaValidator_test.go deleted file mode 100644 index 7264e2e..0000000 --- a/pkg/plugin/implementation/schemaValidator/schemaValidator_test.go +++ /dev/null @@ -1,353 +0,0 @@ -package schemavalidator - -import ( - "context" - "net/url" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/santhosh-tekuri/jsonschema/v6" -) - -// setupTestSchema creates a temporary directory and writes a sample schema file. -func setupTestSchema(t *testing.T) string { - t.Helper() - - // Create a temporary directory for the schema - schemaDir, err := os.MkdirTemp("", "schemas") - if err != nil { - t.Fatalf("Failed to create temp directory: %v", err) - } - - // Create the directory structure for the schema file - schemaFilePath := filepath.Join(schemaDir, "example", "v1.0", "endpoint.json") - if err := os.MkdirAll(filepath.Dir(schemaFilePath), 0755); err != nil { - t.Fatalf("Failed to create schema directory structure: %v", err) - } - - // Define a sample schema - schemaContent := `{ - "type": "object", - "properties": { - "context": { - "type": "object", - "properties": { - "domain": {"type": "string"}, - "version": {"type": "string"}, - "action": {"type": "string"} - }, - "required": ["domain", "version", "action"] - } - }, - "required": ["context"] - }` - - // Write the schema to the file - if err := os.WriteFile(schemaFilePath, []byte(schemaContent), 0644); err != nil { - t.Fatalf("Failed to write schema file: %v", err) - } - - return schemaDir -} - -func TestValidator_Validate_Success(t *testing.T) { - tests := []struct { - name string - url string - payload string - wantErr bool - }{ - { - name: "Valid payload", - url: "http://example.com/endpoint", - payload: `{"context": {"domain": "example", "version": "1.0", "action": "endpoint"}}`, - wantErr: false, - }, - } - - // Setup a temporary schema directory for testing - schemaDir := setupTestSchema(t) - defer os.RemoveAll(schemaDir) - - config := &Config{SchemaDir: schemaDir} - v, _, err := New(context.Background(), config) - if err != nil { - t.Fatalf("Failed to create validator: %v", err) - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - u, _ := url.Parse(tt.url) - err := v.Validate(context.Background(), u, []byte(tt.payload)) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } else { - t.Logf("Test %s passed with no errors", tt.name) - } - }) - } -} - -func TestValidator_Validate_Failure(t *testing.T) { - tests := []struct { - name string - url string - payload string - wantErr string - }{ - { - name: "Invalid JSON payload", - url: "http://example.com/endpoint", - payload: `{"context": {"domain": "example", "version": "1.0"`, - wantErr: "failed to parse JSON payload", - }, - { - name: "Schema validation failure", - url: "http://example.com/endpoint", - payload: `{"context": {"domain": "example", "version": "1.0"}}`, - wantErr: "context: at '/context': missing property 'action'", - }, - { - name: "Schema not found", - url: "http://example.com/unknown_endpoint", - payload: `{"context": {"domain": "example", "version": "1.0"}}`, - wantErr: "schema not found for domain", - }, - } - - // Setup a temporary schema directory for testing - schemaDir := setupTestSchema(t) - defer os.RemoveAll(schemaDir) - - config := &Config{SchemaDir: schemaDir} - v, _, err := New(context.Background(), config) - if err != nil { - t.Fatalf("Failed to create validator: %v", err) - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - u, _ := url.Parse(tt.url) - err := v.Validate(context.Background(), u, []byte(tt.payload)) - if tt.wantErr != "" { - if err == nil { - t.Errorf("Expected error containing '%s', but got nil", tt.wantErr) - } else if !strings.Contains(err.Error(), tt.wantErr) { - t.Errorf("Expected error containing '%s', but got '%v'", tt.wantErr, err) - } else { - t.Logf("Test %s passed with expected error: %v", tt.name, err) - } - } else { - if err != nil { - t.Errorf("Unexpected error: %v", err) - } else { - t.Logf("Test %s passed with no errors", tt.name) - } - } - }) - } -} - -func TestValidator_Initialise(t *testing.T) { - tests := []struct { - name string - setupFunc func(schemaDir string) error - wantErr string - }{ - { - name: "Schema directory does not exist", - setupFunc: func(schemaDir string) error { - // Do not create the schema directory - return nil - - }, - wantErr: "schema directory does not exist", - }, - { - name: "Schema path is not a directory", - setupFunc: func(schemaDir string) error { - // Create a file instead of a directory - return os.WriteFile(schemaDir, []byte{}, 0644) - }, - wantErr: "provided schema path is not a directory", - }, - { - name: "Invalid schema file structure", - setupFunc: func(schemaDir string) error { - // Create an invalid schema file structure - invalidSchemaFile := filepath.Join(schemaDir, "invalid_schema.json") - if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil { - t.Fatalf("Failed to create directory: %v", err) - } - return os.WriteFile(invalidSchemaFile, []byte(`{}`), 0644) - }, - wantErr: "invalid schema file structure", - }, - { - name: "Failed to compile JSON schema", - setupFunc: func(schemaDir string) error { - // Create a schema file with invalid JSON - invalidSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json") - if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil { - t.Fatalf("Failed to create directory: %v", err) - } - return os.WriteFile(invalidSchemaFile, []byte(`{invalid json}`), 0644) - }, - wantErr: "failed to compile JSON schema", - }, - { - name: "Invalid schema file structure with empty components", - setupFunc: func(schemaDir string) error { - // Create a schema file with empty domain, version, or schema name - invalidSchemaFile := filepath.Join(schemaDir, "", "1.0", "endpoint.json") - if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil { - t.Fatalf("Failed to create directory: %v", err) - } - return os.WriteFile(invalidSchemaFile, []byte(`{ - "type": "object", - "properties": { - "context": { - "type": "object", - "properties": { - "domain": {"type": "string"}, - "version": {"type": "string"} - }, - "required": ["domain", "version"] - } - }, - "required": ["context"] - }`), 0644) - }, - wantErr: "failed to read schema directory: invalid schema file structure, expected domain/version/schema.json but got: 1.0/endpoint.json", - }, - { - name: "Failed to read directory", - setupFunc: func(schemaDir string) error { - // Create a directory and remove read permissions - if err := os.MkdirAll(schemaDir, 0000); err != nil { - t.Fatalf("Failed to create directory: %v", err) - } - return nil - }, - wantErr: "failed to read directory", - }, - { - name: "Valid schema directory", - setupFunc: func(schemaDir string) error { - // Create a valid schema file - validSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json") - if err := os.MkdirAll(filepath.Dir(validSchemaFile), 0755); err != nil { - t.Fatalf("Failed to create directory: %v", err) - } - return os.WriteFile(validSchemaFile, []byte(`{ - "type": "object", - "properties": { - "context": { - "type": "object", - "properties": { - "domain": {"type": "string"}, - "version": {"type": "string"} - }, - "required": ["domain", "version"] - } - }, - "required": ["context"] - }`), 0644) - }, - wantErr: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Setup a temporary schema directory for testing - schemaDir := filepath.Join(os.TempDir(), "schemas") - defer os.RemoveAll(schemaDir) - - // Run the setup function to prepare the test case - if err := tt.setupFunc(schemaDir); err != nil { - t.Fatalf("setupFunc() error = %v", err) - } - - config := &Config{SchemaDir: schemaDir} - v := &SchemaValidator{ - config: config, - schemaCache: make(map[string]*jsonschema.Schema), - } - - err := v.initialise() - if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") { - t.Errorf("Error: initialise() returned error = %v, expected error = %v", err, tt.wantErr) - } else if err == nil { - t.Logf("Test %s passed: validator initialized successfully", tt.name) - } else { - t.Logf("Test %s passed with expected error: %v", tt.name, err) - } - }) - } -} - -func TestValidatorNew_Success(t *testing.T) { - schemaDir := setupTestSchema(t) - defer os.RemoveAll(schemaDir) - - config := &Config{SchemaDir: schemaDir} - _, _, err := New(context.Background(), config) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } -} - -func TestValidatorNewFailure(t *testing.T) { - tests := []struct { - name string - config *Config - setupFunc func(schemaDir string) error - wantErr string - }{ - { - name: "Config is nil", - config: nil, - setupFunc: func(schemaDir string) error { - return nil - }, - wantErr: "config cannot be nil", - }, - { - name: "Failed to initialise validators", - config: &Config{ - SchemaDir: "/invalid/path", - }, - setupFunc: func(schemaDir string) error { - // Do not create the schema directory - return nil - }, - wantErr: "ailed to initialise schemaValidator: schema directory does not exist: /invalid/path", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Run the setup function if provided - if tt.setupFunc != nil { - schemaDir := "" - if tt.config != nil { - schemaDir = tt.config.SchemaDir - } - if err := tt.setupFunc(schemaDir); err != nil { - t.Fatalf("Setup function failed: %v", err) - } - } - - // Call the New function with the test config - _, _, err := New(context.Background(), tt.config) - if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") { - t.Errorf("Error: New() returned error = %v, expected error = %v", err, tt.wantErr) - } else { - t.Logf("Test %s passed with expected error: %v", tt.name, err) - } - }) - } -} diff --git a/pkg/plugin/implementation/signVerifier/cmd/plugin.go b/pkg/plugin/implementation/signVerifier/cmd/plugin.go deleted file mode 100644 index d260057..0000000 --- a/pkg/plugin/implementation/signVerifier/cmd/plugin.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "context" - "errors" - - "github.com/beckn/beckn-onix/pkg/plugin/definition" - "github.com/beckn/beckn-onix/pkg/plugin/implementation/signverifier" -) - -// VerifierProvider provides instances of Verifier. -type VerifierProvider struct{} - -// New initializes a new Verifier instance. -func (vp VerifierProvider) New(ctx context.Context, config map[string]string) (definition.Verifier, func() error, error) { - if ctx == nil { - return nil, nil, errors.New("context cannot be nil") - } - - return signverifier.New(ctx, &signverifier.Config{}) -} - -// Provider is the exported symbol that the plugin manager will look for. -var Provider definition.VerifierProvider = VerifierProvider{} diff --git a/pkg/plugin/implementation/signVerifier/cmd/plugin_test.go b/pkg/plugin/implementation/signVerifier/cmd/plugin_test.go deleted file mode 100644 index 85caee5..0000000 --- a/pkg/plugin/implementation/signVerifier/cmd/plugin_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package main - -import ( - "context" - "testing" -) - -// TestVerifierProviderSuccess tests successful creation of a verifier. -func TestVerifierProviderSuccess(t *testing.T) { - provider := VerifierProvider{} - - tests := []struct { - name string - ctx context.Context - config map[string]string - }{ - { - name: "Successful creation", - ctx: context.Background(), - config: map[string]string{}, - }, - { - name: "Nil context", - ctx: context.TODO(), - config: map[string]string{}, - }, - { - name: "Empty config", - ctx: context.Background(), - config: map[string]string{}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - verifier, close, err := provider.New(tt.ctx, tt.config) - - if err != nil { - t.Fatalf("Expected no error, but got: %v", err) - } - if verifier == nil { - t.Fatal("Expected verifier instance to be non-nil") - } - if close != nil { - if err := close(); err != nil { - t.Fatalf("Test %q failed: cleanup function returned an error: %v", tt.name, err) - } - } - }) - } -} - -// TestVerifierProviderFailure tests cases where verifier creation should fail. -func TestVerifierProviderFailure(t *testing.T) { - provider := VerifierProvider{} - - tests := []struct { - name string - ctx context.Context - config map[string]string - wantErr bool - }{ - { - name: "Nil context failure", - ctx: nil, - config: map[string]string{}, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - verifierInstance, close, err := provider.New(tt.ctx, tt.config) - - if (err != nil) != tt.wantErr { - t.Fatalf("Expected error: %v, but got: %v", tt.wantErr, err) - } - if verifierInstance != nil { - t.Fatal("Expected verifier instance to be nil") - } - if close != nil { - if err := close(); err != nil { - t.Fatalf("Test %q failed: cleanup function returned an error: %v", tt.name, err) - } - } - - }) - } -} diff --git a/pkg/plugin/implementation/signVerifier/signVerifier.go b/pkg/plugin/implementation/signVerifier/signVerifier.go deleted file mode 100644 index b76dd6d..0000000 --- a/pkg/plugin/implementation/signVerifier/signVerifier.go +++ /dev/null @@ -1,120 +0,0 @@ -package signverifier - -import ( - "context" - "crypto/ed25519" - "encoding/base64" - "fmt" - "strconv" - "strings" - "time" - - "golang.org/x/crypto/blake2b" -) - -// Config struct for Verifier. -type Config struct { -} - -// Verifier implements the Verifier interface. -type Verifier struct { - config *Config -} - -// New creates a new Verifier instance. -func New(ctx context.Context, config *Config) (*Verifier, func() error, error) { - v := &Verifier{config: config} - - return v, v.Close, nil -} - -// Verify checks the signature for the given payload and public key. -func (v *Verifier) Verify(ctx context.Context, body []byte, header []byte, publicKeyBase64 string) (bool, error) { - createdTimestamp, expiredTimestamp, signature, err := parseAuthHeader(string(header)) - if err != nil { - // TODO: Return appropriate error code when Error Code Handling Module is ready - return false, fmt.Errorf("error parsing header: %w", err) - } - - signatureBytes, err := base64.StdEncoding.DecodeString(signature) - if err != nil { - // TODO: Return appropriate error code when Error Code Handling Module is ready - return false, fmt.Errorf("error decoding signature: %w", err) - } - - currentTime := time.Now().Unix() - if createdTimestamp > currentTime || currentTime > expiredTimestamp { - // TODO: Return appropriate error code when Error Code Handling Module is ready - return false, fmt.Errorf("signature is expired or not yet valid") - } - - createdTime := time.Unix(createdTimestamp, 0) - expiredTime := time.Unix(expiredTimestamp, 0) - - signingString := hash(body, createdTime.Unix(), expiredTime.Unix()) - - decodedPublicKey, err := base64.StdEncoding.DecodeString(publicKeyBase64) - if err != nil { - // TODO: Return appropriate error code when Error Code Handling Module is ready - return false, fmt.Errorf("error decoding public key: %w", err) - } - - if !ed25519.Verify(ed25519.PublicKey(decodedPublicKey), []byte(signingString), signatureBytes) { - // TODO: Return appropriate error code when Error Code Handling Module is ready - return false, fmt.Errorf("signature verification failed") - } - - return true, nil -} - -// parseAuthHeader extracts signature values from the Authorization header. -func parseAuthHeader(header string) (int64, int64, string, error) { - header = strings.TrimPrefix(header, "Signature ") - - parts := strings.Split(header, ",") - signatureMap := make(map[string]string) - - for _, part := range parts { - keyValue := strings.SplitN(strings.TrimSpace(part), "=", 2) - if len(keyValue) == 2 { - key := strings.TrimSpace(keyValue[0]) - value := strings.Trim(keyValue[1], "\"") - signatureMap[key] = value - } - } - - createdTimestamp, err := strconv.ParseInt(signatureMap["created"], 10, 64) - if err != nil { - // TODO: Return appropriate error code when Error Code Handling Module is ready - return 0, 0, "", fmt.Errorf("invalid created timestamp: %w", err) - } - - expiredTimestamp, err := strconv.ParseInt(signatureMap["expires"], 10, 64) - if err != nil { - // TODO: Return appropriate error code when Error Code Handling Module is ready - return 0, 0, "", fmt.Errorf("invalid expires timestamp: %w", err) - } - - signature := signatureMap["signature"] - if signature == "" { - // TODO: Return appropriate error code when Error Code Handling Module is ready - return 0, 0, "", fmt.Errorf("signature missing in header") - } - - return createdTimestamp, expiredTimestamp, signature, nil -} - -// hash constructs a signing string for verification. -func hash(payload []byte, createdTimestamp, expiredTimestamp int64) string { - hasher, _ := blake2b.New512(nil) - hasher.Write(payload) - hashSum := hasher.Sum(nil) - digestB64 := base64.StdEncoding.EncodeToString(hashSum) - - return fmt.Sprintf("(created): %d\n(expires): %d\ndigest: BLAKE-512=%s", createdTimestamp, expiredTimestamp, digestB64) -} - -// Close releases resources (mock implementation returning nil). -func (v *Verifier) Close() error { - return nil -} diff --git a/pkg/plugin/implementation/signVerifier/signVerifier_test.go b/pkg/plugin/implementation/signVerifier/signVerifier_test.go deleted file mode 100644 index 0e22eb8..0000000 --- a/pkg/plugin/implementation/signVerifier/signVerifier_test.go +++ /dev/null @@ -1,153 +0,0 @@ -package signverifier - -import ( - "context" - "crypto/ed25519" - "encoding/base64" - "strconv" - "testing" - "time" -) - -// generateTestKeyPair generates a new ED25519 key pair for testing. -func generateTestKeyPair() (string, string) { - publicKey, privateKey, _ := ed25519.GenerateKey(nil) - return base64.StdEncoding.EncodeToString(privateKey), base64.StdEncoding.EncodeToString(publicKey) -} - -// signTestData creates a valid signature for test cases. -func signTestData(privateKeyBase64 string, body []byte, createdAt, expiresAt int64) string { - privateKeyBytes, _ := base64.StdEncoding.DecodeString(privateKeyBase64) - privateKey := ed25519.PrivateKey(privateKeyBytes) - - signingString := hash(body, createdAt, expiresAt) - signature := ed25519.Sign(privateKey, []byte(signingString)) - - return base64.StdEncoding.EncodeToString(signature) -} - -// TestVerifySuccessCases tests all valid signature verification cases. -func TestVerifySuccess(t *testing.T) { - privateKeyBase64, publicKeyBase64 := generateTestKeyPair() - - tests := []struct { - name string - body []byte - createdAt int64 - expiresAt int64 - }{ - { - name: "Valid Signature", - body: []byte("Test Payload"), - createdAt: time.Now().Unix(), - expiresAt: time.Now().Unix() + 3600, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - signature := signTestData(privateKeyBase64, tt.body, tt.createdAt, tt.expiresAt) - header := `Signature created="` + strconv.FormatInt(tt.createdAt, 10) + - `", expires="` + strconv.FormatInt(tt.expiresAt, 10) + - `", signature="` + signature + `"` - - verifier, close, _ := New(context.Background(), &Config{}) - valid, err := verifier.Verify(context.Background(), tt.body, []byte(header), publicKeyBase64) - - if err != nil { - t.Fatalf("Expected no error, but got: %v", err) - } - if !valid { - t.Fatal("Expected signature verification to succeed") - } - if close != nil { - if err := close(); err != nil { - t.Fatalf("Test %q failed: cleanup function returned an error: %v", tt.name, err) - } - } - }) - } -} - -// TestVerifyFailureCases tests all invalid signature verification cases. -func TestVerifyFailure(t *testing.T) { - privateKeyBase64, publicKeyBase64 := generateTestKeyPair() - _, wrongPublicKeyBase64 := generateTestKeyPair() - - tests := []struct { - name string - body []byte - header string - pubKey string - }{ - { - name: "Missing Authorization Header", - body: []byte("Test Payload"), - header: "", - pubKey: publicKeyBase64, - }, - { - name: "Malformed Header", - body: []byte("Test Payload"), - header: `InvalidSignature created="wrong"`, - pubKey: publicKeyBase64, - }, - { - name: "Invalid Base64 Signature", - body: []byte("Test Payload"), - header: `Signature created="` + strconv.FormatInt(time.Now().Unix(), 10) + - `", expires="` + strconv.FormatInt(time.Now().Unix()+3600, 10) + - `", signature="!!INVALIDBASE64!!"`, - pubKey: publicKeyBase64, - }, - { - name: "Expired Signature", - body: []byte("Test Payload"), - header: `Signature created="` + strconv.FormatInt(time.Now().Unix()-7200, 10) + - `", expires="` + strconv.FormatInt(time.Now().Unix()-3600, 10) + - `", signature="` + signTestData(privateKeyBase64, []byte("Test Payload"), time.Now().Unix()-7200, time.Now().Unix()-3600) + `"`, - pubKey: publicKeyBase64, - }, - { - name: "Invalid Public Key", - body: []byte("Test Payload"), - header: `Signature created="` + strconv.FormatInt(time.Now().Unix(), 10) + - `", expires="` + strconv.FormatInt(time.Now().Unix()+3600, 10) + - `", signature="` + signTestData(privateKeyBase64, []byte("Test Payload"), time.Now().Unix(), time.Now().Unix()+3600) + `"`, - pubKey: wrongPublicKeyBase64, - }, - { - name: "Invalid Expires Timestamp", - body: []byte("Test Payload"), - header: `Signature created="` + strconv.FormatInt(time.Now().Unix(), 10) + - `", expires="invalid_timestamp"`, - pubKey: publicKeyBase64, - }, - { - name: "Signature Missing in Headers", - body: []byte("Test Payload"), - header: `Signature created="` + strconv.FormatInt(time.Now().Unix(), 10) + - `", expires="` + strconv.FormatInt(time.Now().Unix()+3600, 10) + `"`, - pubKey: publicKeyBase64, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - verifier, close, _ := New(context.Background(), &Config{}) - valid, err := verifier.Verify(context.Background(), tt.body, []byte(tt.header), tt.pubKey) - - if err == nil { - t.Fatal("Expected an error but got none") - } - if valid { - t.Fatal("Expected verification to fail") - } - if close != nil { - if err := close(); err != nil { - t.Fatalf("Test %q failed: cleanup function returned an error: %v", tt.name, err) - } - } - }) - } -}