diff --git a/.github/workflows/beckn_ci.yml b/.github/workflows/beckn_ci.yml index c2fe961..df6c105 100644 --- a/.github/workflows/beckn_ci.yml +++ b/.github/workflows/beckn_ci.yml @@ -69,9 +69,9 @@ jobs: coverage=$(go tool cover -func=$coverage_file | grep total | awk '{print $3}' | sed 's/%//') echo "Coverage for $coverage_file: $coverage%" - # If coverage is below threshold (80%), fail the job + # If coverage is below threshold (90%), fail the job if (( $(echo "$coverage < 80" | bc -l) )); then - echo "Coverage for $coverage_file is below 80%. Failing the job." + echo "Coverage for $coverage_file is below 90%. Failing the job." exit 1 fi done diff --git a/go.mod b/go.mod index e82ed85..c2692fb 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,20 @@ toolchain go1.23.7 require ( github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 golang.org/x/crypto v0.36.0 - gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/kr/pretty v0.3.1 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect +) + +require ( + github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 + golang.org/x/text v0.23.0 // indirect ) require ( golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index f8880bd..896f459 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,29 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 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/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 h1:m1h+vudopHsI67FPT9MOncyndWhTcdUoBtI1R1uajGY= +github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03/go.mod h1:8sheVFH84v3PCyFY/O02mIgSQY9I6wMYPWsq7mDnEZY= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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/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= \ No newline at end of file diff --git a/pkg/plugin/definition/decrypter.go b/pkg/plugin/definition/decrypter.go new file mode 100644 index 0000000..8bd0b6a --- /dev/null +++ b/pkg/plugin/definition/decrypter.go @@ -0,0 +1,15 @@ +package definition + +import "context" + +// Decrypter defines the methods for decryption. +type Decrypter interface { + // Decrypt decrypts the given body using the provided privateKeyBase64 and publicKeyBase64. + Decrypt(ctx context.Context, encryptedData string, privateKeyBase64, publicKeyBase64 string) (string, error) +} + +// DecrypterProvider initializes a new decrypter instance with the given config. +type DecrypterProvider interface { + // New creates a new decrypter instance based on the provided config. + New(ctx context.Context, config map[string]string) (Decrypter, func() error, error) +} diff --git a/pkg/plugin/definition/encrypter.go b/pkg/plugin/definition/encrypter.go new file mode 100644 index 0000000..08db8a6 --- /dev/null +++ b/pkg/plugin/definition/encrypter.go @@ -0,0 +1,15 @@ +package definition + +import "context" + +// Encrypter defines the methods for encryption. +type Encrypter interface { + // Encrypt encrypts the given body using the provided privateKeyBase64 and publicKeyBase64. + Encrypt(ctx context.Context, data string, privateKeyBase64, publicKeyBase64 string) (string, error) +} + +// EncrypterProvider initializes a new encrypter instance with the given config. +type EncrypterProvider interface { + // New creates a new encrypter instance based on the provided config. + New(ctx context.Context, config map[string]string) (Encrypter, func() error, error) +} diff --git a/pkg/plugin/definition/publisher.go b/pkg/plugin/definition/publisher.go new file mode 100644 index 0000000..93f9e21 --- /dev/null +++ b/pkg/plugin/definition/publisher.go @@ -0,0 +1,16 @@ +package definition + +import "context" + +// Publisher defines the general publisher interface for messaging plugins. +type Publisher interface { + // Publish sends a message (as a byte slice) using the underlying messaging system. + Publish(ctx context.Context, msg []byte) error + + Close() error // Important for releasing resources. +} + +type PublisherProvider interface { + // New initializes a new publisher instance with the given configuration. + New(ctx context.Context, config map[string]string) (Publisher, error) +} diff --git a/shared/plugin/definition/signVerifier.go b/pkg/plugin/definition/signVerifier.go similarity index 100% rename from shared/plugin/definition/signVerifier.go rename to pkg/plugin/definition/signVerifier.go diff --git a/shared/plugin/definition/signer.go b/pkg/plugin/definition/signer.go similarity index 100% rename from shared/plugin/definition/signer.go rename to pkg/plugin/definition/signer.go diff --git a/pkg/plugin/implementation/decrypter/cmd/plugin.go b/pkg/plugin/implementation/decrypter/cmd/plugin.go new file mode 100644 index 0000000..cb988a9 --- /dev/null +++ b/pkg/plugin/implementation/decrypter/cmd/plugin.go @@ -0,0 +1,19 @@ +package main + +import ( + "context" + + "github.com/beckn/beckn-onix/pkg/plugin/definition" + decrypter "github.com/beckn/beckn-onix/pkg/plugin/implementation/decrypter" +) + +// DecrypterProvider implements the definition.DecrypterProvider interface. +type DecrypterProvider struct{} + +// New creates a new Decrypter instance using the provided configuration. +func (dp DecrypterProvider) New(ctx context.Context, config map[string]string) (definition.Decrypter, func() error, error) { + return decrypter.New(ctx) +} + +// Provider is the exported symbol that the plugin manager will look for. +var Provider definition.DecrypterProvider = DecrypterProvider{} diff --git a/pkg/plugin/implementation/decrypter/cmd/plugin_test.go b/pkg/plugin/implementation/decrypter/cmd/plugin_test.go new file mode 100644 index 0000000..6a4f168 --- /dev/null +++ b/pkg/plugin/implementation/decrypter/cmd/plugin_test.go @@ -0,0 +1,49 @@ +package main + +import ( + "context" + "testing" +) + +func TestDecrypterProviderSuccess(t *testing.T) { + tests := []struct { + name string + ctx context.Context + config map[string]string + }{ + { + name: "Valid context with empty config", + ctx: context.Background(), + config: map[string]string{}, + }, + { + name: "Valid context with non-empty config", + ctx: context.Background(), + config: map[string]string{"key": "value"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + provider := DecrypterProvider{} + decrypter, cleanup, err := provider.New(tt.ctx, tt.config) + + // Check error. + if err != nil { + t.Errorf("New() error = %v, want no error", err) + } + + // Check decrypter. + if decrypter == nil { + t.Error("New() decrypter is nil, want non-nil") + } + + // Test cleanup function if it exists. + if cleanup != nil { + if err := cleanup(); err != nil { + t.Errorf("cleanup() error = %v", err) + } + } + }) + } +} diff --git a/pkg/plugin/implementation/decrypter/decrypter.go b/pkg/plugin/implementation/decrypter/decrypter.go new file mode 100644 index 0000000..f312f16 --- /dev/null +++ b/pkg/plugin/implementation/decrypter/decrypter.go @@ -0,0 +1,85 @@ +package decryption + +import ( + "context" + "crypto/aes" + "crypto/cipher" + "crypto/ecdh" + "encoding/base64" + "fmt" + + "github.com/zenazn/pkcs7pad" +) + +// decrypter implements the Decrypter interface and handles the decryption process. +type decrypter struct { +} + +// New creates a new decrypter instance with the given configuration. +func New(ctx context.Context) (*decrypter, func() error, error) { + return &decrypter{}, nil, nil +} + +// Decrypt decrypts the given encryptedData using the provided privateKeyBase64 and publicKeyBase64. +func (d *decrypter) Decrypt(ctx context.Context, encryptedData, privateKeyBase64, publicKeyBase64 string) (string, error) { + privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + return "", fmt.Errorf("invalid private key: %w", err) + } + + publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + return "", fmt.Errorf("invalid public key: %w", err) + } + + // Decode the Base64 encoded encrypted data. + messageByte, err := base64.StdEncoding.DecodeString(encryptedData) + if err != nil { + return "", fmt.Errorf("failed to decode encrypted data: %w", err) + } + + aesCipher, err := createAESCipher(privateKeyBytes, publicKeyBytes) + if err != nil { + return "", fmt.Errorf("failed to create AES cipher: %w", err) + } + + blocksize := aesCipher.BlockSize() + if len(messageByte)%blocksize != 0 { + return "", fmt.Errorf("ciphertext is not a multiple of the blocksize") + } + + for i := 0; i < len(messageByte); i += aesCipher.BlockSize() { + executionSlice := messageByte[i : i+aesCipher.BlockSize()] + aesCipher.Decrypt(executionSlice, executionSlice) + } + + messageByte, err = pkcs7pad.Unpad(messageByte) + if err != nil { + return "", fmt.Errorf("failed to unpad data: %w", err) + } + + return string(messageByte), nil +} + +func createAESCipher(privateKey, publicKey []byte) (cipher.Block, error) { + x25519Curve := ecdh.X25519() + x25519PrivateKey, err := x25519Curve.NewPrivateKey(privateKey) + if err != nil { + return nil, fmt.Errorf("failed to create private key: %w", err) + } + x25519PublicKey, err := x25519Curve.NewPublicKey(publicKey) + if err != nil { + return nil, fmt.Errorf("failed to create public key: %w", err) + } + sharedSecret, err := x25519PrivateKey.ECDH(x25519PublicKey) + if err != nil { + return nil, fmt.Errorf("failed to derive shared secret: %w", err) + } + + aesCipher, err := aes.NewCipher(sharedSecret) + if err != nil { + return nil, fmt.Errorf("failed to create AES cipher: %w", err) + } + + return aesCipher, nil +} diff --git a/pkg/plugin/implementation/decrypter/decrypter_test.go b/pkg/plugin/implementation/decrypter/decrypter_test.go new file mode 100644 index 0000000..a2bbe11 --- /dev/null +++ b/pkg/plugin/implementation/decrypter/decrypter_test.go @@ -0,0 +1,251 @@ +package decryption + +import ( + "context" + "crypto/aes" + "crypto/ecdh" + "crypto/rand" + "encoding/base64" + "strings" + "testing" + + "github.com/zenazn/pkcs7pad" +) + +// Helper function to generate valid test keys. +func generateTestKeys(t *testing.T) (privateKeyB64, publicKeyB64 string) { + curve := ecdh.X25519() + privateKey, err := curve.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("Failed to generate private key: %v", err) + } + + publicKey := privateKey.PublicKey() + privateKeyB64 = base64.StdEncoding.EncodeToString(privateKey.Bytes()) + publicKeyB64 = base64.StdEncoding.EncodeToString(publicKey.Bytes()) + + return privateKeyB64, publicKeyB64 +} + +// Helper function to encrypt test data. +func encryptTestData(t *testing.T, data []byte, privateKeyBase64, publicKeyBase64 string) string { + privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + t.Fatalf("Invalid private key: %v", err) + } + + publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + t.Fatalf("Invalid public key: %v", err) + } + + x25519Curve := ecdh.X25519() + x25519PrivateKey, err := x25519Curve.NewPrivateKey(privateKeyBytes) + if err != nil { + t.Fatalf("Failed to create private key: %v", err) + } + x25519PublicKey, err := x25519Curve.NewPublicKey(publicKeyBytes) + if err != nil { + t.Fatalf("Failed to create public key: %v", err) + } + + // Generate shared secret for encryption. + sharedSecret, err := x25519PrivateKey.ECDH(x25519PublicKey) + if err != nil { + t.Fatalf("Failed to create shared secret: %v", err) + } + + // Create AES cipher. + block, err := aes.NewCipher(sharedSecret) + if err != nil { + t.Fatalf("Failed to create AES cipher: %v", err) + } + + // Pad the data. + paddedData := pkcs7pad.Pad(data, block.BlockSize()) + + // Encrypt the data. + ciphertext := make([]byte, len(paddedData)) + for i := 0; i < len(paddedData); i += block.BlockSize() { + block.Encrypt(ciphertext[i:i+block.BlockSize()], paddedData[i:i+block.BlockSize()]) + } + + return base64.StdEncoding.EncodeToString(ciphertext) +} + +// TestDecrypterSuccess tests successful decryption scenarios. +func TestDecrypterSuccess(t *testing.T) { + senderPrivateKeyB64, senderPublicKeyB64 := generateTestKeys(t) + receiverPrivateKeyB64, receiverPublicKeyB64 := generateTestKeys(t) + + tests := []struct { + name string + data []byte + }{ + { + name: "Valid decryption with small data", + data: []byte("test"), + }, + { + name: "Valid decryption with medium data", + data: []byte("medium length test data that spans multiple blocks"), + }, + { + name: "Valid decryption with empty data", + data: []byte{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Encrypt the test data. + encryptedData := encryptTestData(t, tt.data, senderPrivateKeyB64, receiverPublicKeyB64) + + decrypter, _, err := New(context.Background()) + if err != nil { + t.Fatalf("Failed to create decrypter: %v", err) + } + + result, err := decrypter.Decrypt(context.Background(), encryptedData, receiverPrivateKeyB64, senderPublicKeyB64) + if err != nil { + t.Errorf("Decrypt() error = %v", err) + } + + if err == nil { + if result != string(tt.data) { + t.Errorf("Decrypt() = %v, want %v", result, string(tt.data)) + } + } + }) + } +} + +// TestDecrypterFailure tests various failure scenarios. +func TestDecrypterFailure(t *testing.T) { + _, senderPublicKeyB64 := generateTestKeys(t) + receiverPrivateKeyB64, _ := generateTestKeys(t) + + tests := []struct { + name string + encryptedData string + privateKey string + publicKey string + expectedErr string + }{ + { + name: "Invalid private key format", + encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)), + privateKey: "invalid-base64!@#$", + publicKey: senderPublicKeyB64, + expectedErr: "invalid private key", + }, + { + name: "Invalid public key format", + encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)), + privateKey: receiverPrivateKeyB64, + publicKey: "invalid-base64!@#$", + expectedErr: "invalid public key", + }, + { + name: "Invalid encrypted data format", + encryptedData: "invalid-base64!@#$", + privateKey: receiverPrivateKeyB64, + publicKey: senderPublicKeyB64, + expectedErr: "failed to decode encrypted data", + }, + { + name: "Empty private key", + encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)), + privateKey: "", + publicKey: senderPublicKeyB64, + expectedErr: "invalid private key", + }, + { + name: "Empty public key", + encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)), + privateKey: receiverPrivateKeyB64, + publicKey: "", + expectedErr: "invalid public key", + }, + { + name: "Invalid base64 data", + encryptedData: "=invalid-base64", // Invalid encrypted data. + privateKey: receiverPrivateKeyB64, + publicKey: senderPublicKeyB64, + expectedErr: "failed to decode encrypted data", + }, + { + name: "Invalid private key size", + encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)), + privateKey: base64.StdEncoding.EncodeToString([]byte("short")), + publicKey: senderPublicKeyB64, + expectedErr: "failed to create private key", + }, + { + name: "Invalid public key size", + encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)), + privateKey: receiverPrivateKeyB64, + publicKey: base64.StdEncoding.EncodeToString([]byte("short")), + expectedErr: "failed to create public key", + }, + { + name: "Invalid block size", + encryptedData: base64.StdEncoding.EncodeToString([]byte("not-block-size")), + privateKey: receiverPrivateKeyB64, + publicKey: senderPublicKeyB64, + expectedErr: "ciphertext is not a multiple of the blocksize", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + decrypter, _, err := New(context.Background()) + if err != nil { + t.Fatalf("Failed to create decrypter: %v", err) + } + + _, err = decrypter.Decrypt(context.Background(), tt.encryptedData, tt.privateKey, tt.publicKey) + if err == nil { + t.Error("Expected error but got none") + } + + if err != nil { + if !strings.Contains(err.Error(), tt.expectedErr) { + t.Errorf("Expected error containing %q, got %q", tt.expectedErr, err.Error()) + } + } + }) + } +} + +// TestNewDecrypter tests the creation of new Decrypter instances. +func TestNewDecrypter(t *testing.T) { + tests := []struct { + name string + ctx context.Context + }{ + { + name: "Valid context", + ctx: context.Background(), + }, + { + name: "Nil context", + ctx: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + decrypter, _, err := New(tt.ctx) + if err != nil { + t.Errorf("New() error = %v", err) + } + + if err == nil { + if decrypter == nil { + t.Error("Expected non-nil decrypter") + } + } + }) + } +} diff --git a/pkg/plugin/implementation/encrypter/cmd/plugin.go b/pkg/plugin/implementation/encrypter/cmd/plugin.go new file mode 100644 index 0000000..aad52ef --- /dev/null +++ b/pkg/plugin/implementation/encrypter/cmd/plugin.go @@ -0,0 +1,18 @@ +package main + +import ( + "context" + + "github.com/beckn/beckn-onix/pkg/plugin/definition" + "github.com/beckn/beckn-onix/pkg/plugin/implementation/encrypter" +) + +// EncrypterProvider implements the definition.EncrypterProvider interface. +type EncrypterProvider struct{} + +func (ep EncrypterProvider) New(ctx context.Context, config map[string]string) (definition.Encrypter, func() error, error) { + return encrypter.New(ctx) +} + +// Provider is the exported symbol that the plugin manager will look for. +var Provider definition.EncrypterProvider = EncrypterProvider{} diff --git a/pkg/plugin/implementation/encrypter/cmd/plugin_test.go b/pkg/plugin/implementation/encrypter/cmd/plugin_test.go new file mode 100644 index 0000000..cbb469e --- /dev/null +++ b/pkg/plugin/implementation/encrypter/cmd/plugin_test.go @@ -0,0 +1,49 @@ +package main + +import ( + "context" + "testing" +) + +func TestEncrypterProviderSuccess(t *testing.T) { + tests := []struct { + name string + ctx context.Context + config map[string]string + }{ + { + name: "Valid empty config", + ctx: context.Background(), + config: map[string]string{}, + }, + { + name: "Valid config with algorithm", + ctx: context.Background(), + config: map[string]string{ + "algorithm": "AES", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create provider and encrypter. + provider := EncrypterProvider{} + encrypter, cleanup, err := provider.New(tt.ctx, tt.config) + if err != nil { + t.Fatalf("EncrypterProvider.New() error = %v", err) + } + if encrypter == nil { + t.Fatal("EncrypterProvider.New() returned nil encrypter") + } + defer func() { + if cleanup != nil { + if err := cleanup(); err != nil { + t.Errorf("Cleanup() error = %v", err) + } + } + }() + + }) + } +} diff --git a/pkg/plugin/implementation/encrypter/encrypter.go b/pkg/plugin/implementation/encrypter/encrypter.go new file mode 100644 index 0000000..f0a8663 --- /dev/null +++ b/pkg/plugin/implementation/encrypter/encrypter.go @@ -0,0 +1,70 @@ +package encrypter + +import ( + "context" + "crypto/aes" + "crypto/cipher" + "crypto/ecdh" + "encoding/base64" + "fmt" + + "github.com/zenazn/pkcs7pad" +) + +// encrypter implements the Encrypter interface and handles the encryption process. +type encrypter struct { +} + +// New creates a new encrypter instance with the given configuration. +func New(ctx context.Context) (*encrypter, func() error, error) { + return &encrypter{}, nil, nil +} + +func (e *encrypter) Encrypt(ctx context.Context, data string, privateKeyBase64, publicKeyBase64 string) (string, error) { + privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + return "", fmt.Errorf("invalid private key: %w", err) + } + + publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + return "", fmt.Errorf("invalid public key: %w", err) + } + + // Convert the input string to a byte slice. + dataByte := []byte(data) + aesCipher, err := createAESCipher(privateKeyBytes, publicKeyBytes) + if err != nil { + return "", fmt.Errorf("failed to create AES cipher: %w", err) + } + + dataByte = pkcs7pad.Pad(dataByte, aesCipher.BlockSize()) + for i := 0; i < len(dataByte); i += aesCipher.BlockSize() { + aesCipher.Encrypt(dataByte[i:i+aesCipher.BlockSize()], dataByte[i:i+aesCipher.BlockSize()]) + } + + return base64.StdEncoding.EncodeToString(dataByte), nil +} + +func createAESCipher(privateKey, publicKey []byte) (cipher.Block, error) { + x25519Curve := ecdh.X25519() + x25519PrivateKey, err := x25519Curve.NewPrivateKey(privateKey) + if err != nil { + return nil, fmt.Errorf("failed to create private key: %w", err) + } + x25519PublicKey, err := x25519Curve.NewPublicKey(publicKey) + if err != nil { + return nil, fmt.Errorf("failed to create public key: %w", err) + } + sharedSecret, err := x25519PrivateKey.ECDH(x25519PublicKey) + if err != nil { + return nil, fmt.Errorf("failed to derive shared secret: %w", err) + } + + aesCipher, err := aes.NewCipher(sharedSecret) + if err != nil { + return nil, fmt.Errorf("failed to create AES cipher: %w", err) + } + + return aesCipher, nil +} diff --git a/pkg/plugin/implementation/encrypter/encrypter_test.go b/pkg/plugin/implementation/encrypter/encrypter_test.go new file mode 100644 index 0000000..917bdc4 --- /dev/null +++ b/pkg/plugin/implementation/encrypter/encrypter_test.go @@ -0,0 +1,183 @@ +package encrypter + +import ( + "context" + "crypto/ecdh" + "crypto/rand" + "encoding/base64" + "strings" + "testing" +) + +// Helper function to generate a test X25519 key pair. +func generateTestKeyPair(t *testing.T) (string, string) { + curve := ecdh.X25519() + privateKey, err := curve.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("Failed to generate private key: %v", err) + } + + publicKeyBytes := privateKey.PublicKey().Bytes() + // Encode public and private key to base64. + publicKeyBase64 := base64.StdEncoding.EncodeToString(publicKeyBytes) + privateKeyBase64 := base64.StdEncoding.EncodeToString(privateKey.Bytes()) + + return publicKeyBase64, privateKeyBase64 +} + +// TestEncryptSuccess tests successful encryption scenarios. +func TestEncryptSuccess(t *testing.T) { + _, privateKey := generateTestKeyPair(t) + peerpublicKey, _ := generateTestKeyPair(t) + + tests := []struct { + name string + data string + pubKey string + privKey string + }{ + { + name: "Valid short message", + data: "Hello, World!", + pubKey: peerpublicKey, + privKey: privateKey, + }, + { + name: "Valid JSON message", + data: `{"key":"value"}`, + pubKey: peerpublicKey, + privKey: privateKey, + }, + { + name: "Valid empty message", + data: "", + pubKey: peerpublicKey, + privKey: privateKey, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + encrypter := &encrypter{} + encrypted, err := encrypter.Encrypt(context.Background(), tt.data, tt.privKey, tt.pubKey) + if err != nil { + t.Errorf("Encrypt() expected no error, but got: %v", err) + } + + // Verify the encrypted data is valid base64. + _, err = base64.StdEncoding.DecodeString(encrypted) + if err != nil { + t.Errorf("Encrypt() output is not valid base64: %v", err) + } + + // Since we can't decrypt without the ephemeral private key, + // we can only verify that encryption doesn't return empty data. + if encrypted == "" { + t.Error("Encrypt() returned empty string") + } + + // Verify the output is different from input (basic encryption check). + if encrypted == tt.data { + t.Error("Encrypt() output matches input, suggesting no encryption occurred") + } + + }) + } +} + +// TestEncryptFailure tests encryption failure scenarios. +func TestEncryptFailure(t *testing.T) { + // Generate a valid key pair for testing. + _, privateKey := generateTestKeyPair(t) + peerpublicKey, _ := generateTestKeyPair(t) + + tests := []struct { + name string + data string + publicKey string + privKey string + errorContains string + }{ + { + name: "Invalid public key format", + data: "test data", + publicKey: "invalid-base64!@#$", + privKey: privateKey, + errorContains: "invalid public key", + }, + { + name: "Invalid key bytes(public key)", + data: "test data", + publicKey: base64.StdEncoding.EncodeToString([]byte("invalid-key-bytes")), + privKey: privateKey, + errorContains: "failed to create public key", + }, + { + name: "Invalid key bytes(private key)", + data: "test data", + publicKey: peerpublicKey, + privKey: base64.StdEncoding.EncodeToString([]byte("invalid-key-bytes")), + errorContains: "failed to create private key", + }, + { + name: "Empty public key", + data: "test data", + publicKey: "", + privKey: privateKey, + errorContains: "invalid public key", + }, + { + name: "Too short key", + data: "test data", + publicKey: base64.StdEncoding.EncodeToString([]byte{1, 2, 3, 4}), + privKey: privateKey, + errorContains: "failed to create public key", + }, + { + name: "Invalid private key", + data: "test data", + publicKey: peerpublicKey, + privKey: "invalid-base64!@#$", + errorContains: "invalid private key", + }, + { + name: "Empty private key", + data: "test data", + publicKey: peerpublicKey, + privKey: "", + errorContains: "invalid private key", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + encrypter := &encrypter{} + _, err := encrypter.Encrypt(context.Background(), tt.data, tt.privKey, tt.publicKey) + if err != nil && !strings.Contains(err.Error(), tt.errorContains) { + t.Errorf("Encrypt() error = %v, want error containing %q", err, tt.errorContains) + } + }) + } +} + +// TestNew tests the creation of new encrypter instances. +func TestNew(t *testing.T) { + tests := []struct { + name string + ctx context.Context + }{ + { + name: "Success", + ctx: context.Background(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + encrypter, _, err := New(tt.ctx) + if err == nil && encrypter == nil { + t.Error("New() returned nil encrypter") + } + }) + } +} diff --git a/shared/plugin/implementation/signVerifier/cmd/plugin.go b/pkg/plugin/implementation/signVerifier/cmd/plugin.go similarity index 66% rename from shared/plugin/implementation/signVerifier/cmd/plugin.go rename to pkg/plugin/implementation/signVerifier/cmd/plugin.go index 1e4fb06..35c1287 100644 --- a/shared/plugin/implementation/signVerifier/cmd/plugin.go +++ b/pkg/plugin/implementation/signVerifier/cmd/plugin.go @@ -4,17 +4,16 @@ import ( "context" "errors" - "github.com/beckn/beckn-onix/shared/plugin/definition" + "github.com/beckn/beckn-onix/pkg/plugin/definition" - plugin "github.com/beckn/beckn-onix/shared/plugin/definition" - verifier "github.com/beckn/beckn-onix/shared/plugin/implementation/signVerifier" + verifier "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) (plugin.Verifier, func() error, error) { +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") } diff --git a/shared/plugin/implementation/signVerifier/cmd/plugin_test.go b/pkg/plugin/implementation/signVerifier/cmd/plugin_test.go similarity index 100% rename from shared/plugin/implementation/signVerifier/cmd/plugin_test.go rename to pkg/plugin/implementation/signVerifier/cmd/plugin_test.go diff --git a/shared/plugin/implementation/signVerifier/signVerifier.go b/pkg/plugin/implementation/signVerifier/signVerifier.go similarity index 100% rename from shared/plugin/implementation/signVerifier/signVerifier.go rename to pkg/plugin/implementation/signVerifier/signVerifier.go diff --git a/shared/plugin/implementation/signVerifier/signVerifier_test.go b/pkg/plugin/implementation/signVerifier/signVerifier_test.go similarity index 100% rename from shared/plugin/implementation/signVerifier/signVerifier_test.go rename to pkg/plugin/implementation/signVerifier/signVerifier_test.go diff --git a/shared/plugin/implementation/signer/cmd/plugin.go b/pkg/plugin/implementation/signer/cmd/plugin.go similarity index 82% rename from shared/plugin/implementation/signer/cmd/plugin.go rename to pkg/plugin/implementation/signer/cmd/plugin.go index 854ecbe..2d78d98 100644 --- a/shared/plugin/implementation/signer/cmd/plugin.go +++ b/pkg/plugin/implementation/signer/cmd/plugin.go @@ -4,8 +4,8 @@ import ( "context" "errors" - "github.com/beckn/beckn-onix/shared/plugin/definition" - "github.com/beckn/beckn-onix/shared/plugin/implementation/signer" + "github.com/beckn/beckn-onix/pkg/plugin/definition" + "github.com/beckn/beckn-onix/pkg/plugin/implementation/signer" ) // SignerProvider implements the definition.SignerProvider interface. diff --git a/shared/plugin/implementation/signer/cmd/plugin_test.go b/pkg/plugin/implementation/signer/cmd/plugin_test.go similarity index 100% rename from shared/plugin/implementation/signer/cmd/plugin_test.go rename to pkg/plugin/implementation/signer/cmd/plugin_test.go diff --git a/shared/plugin/implementation/signer/signer.go b/pkg/plugin/implementation/signer/signer.go similarity index 100% rename from shared/plugin/implementation/signer/signer.go rename to pkg/plugin/implementation/signer/signer.go diff --git a/shared/plugin/implementation/signer/signer_test.go b/pkg/plugin/implementation/signer/signer_test.go similarity index 100% rename from shared/plugin/implementation/signer/signer_test.go rename to pkg/plugin/implementation/signer/signer_test.go diff --git a/pkg/plugin/manager.go b/pkg/plugin/manager.go index 2cc76bb..209e0e6 100644 --- a/pkg/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -3,20 +3,22 @@ package plugin import ( "context" "fmt" - "os" "path/filepath" "plugin" "strings" "github.com/beckn/beckn-onix/pkg/plugin/definition" - - "gopkg.in/yaml.v2" ) // Config represents the plugin manager configuration. type Config struct { Root string `yaml:"root"` - SchemaValidator PluginConfig `yaml:"schema_validator"` + Signer PluginConfig `yaml:"signer"` + Verifier PluginConfig `yaml:"verifier"` + Decrypter PluginConfig `yaml:"decrypter"` + Encrypter PluginConfig `yaml:"encrypter"` + Publisher PluginConfig `yaml:"publisher"` + SchemaValidator PluginConfig `yaml:"schemaValidator"` } // PluginConfig represents configuration details for a plugin. @@ -25,28 +27,19 @@ type PluginConfig struct { Config map[string]string `yaml:"config"` } -// // ValidationPluginConfig represents configuration details for a plugin. -// type ValidationPluginConfig struct { -// ID string `yaml:"id"` -// Schema SchemaDetails `yaml:"config"` -// PluginPath string `yaml:"plugin_path"` -// } - // SchemaDetails contains information about the plugin schema directory. type SchemaDetails struct { - SchemaDir string `yaml:"schema_dir"` + SchemaDir string `yaml:"schemaDir"` } -// // Config represents the configuration for the application, including plugin configurations. -// type Config struct { -// Plugins struct { -// ValidationPlugin ValidationPluginConfig `yaml:"validation_plugin"` -// } `yaml:"plugins"` -// } - // Manager handles dynamic plugin loading and management. type Manager struct { - vp definition.SchemaValidatorProvider + sp definition.SignerProvider + vp definition.VerifierProvider + dp definition.DecrypterProvider + ep definition.EncrypterProvider + pb definition.PublisherProvider + svp definition.SchemaValidatorProvider cfg *Config } @@ -56,40 +49,47 @@ func NewManager(ctx context.Context, cfg *Config) (*Manager, error) { return nil, fmt.Errorf("configuration cannot be nil") } - // Load schema validator plugin - vp, err := provider[definition.SchemaValidatorProvider](cfg.Root, cfg.SchemaValidator.ID) + // Load signer plugin. + sp, err := provider[definition.SignerProvider](cfg.Root, cfg.Signer.ID) if err != nil { - return nil, fmt.Errorf("failed to load validator plugin: %w", err) - } - if vp == nil { - return nil, fmt.Errorf("validator provider is nil") + return nil, fmt.Errorf("failed to load signer plugin: %w", err) } - // // Initialize validator - // validatorMap, defErr := vp.New(ctx, map[string]string{ - // "schema_dir": cfg.Plugins.ValidationPlugin.Schema.SchemaDir, - // }) - // if defErr != nil { - // return nil, fmt.Errorf("failed to initialize validator: %v", defErr) - // } + // Load publisher plugin. + pb, err := provider[definition.PublisherProvider](cfg.Root, cfg.Publisher.ID) + if err != nil { + return nil, fmt.Errorf("failed to load publisher plugin: %w", err) + } - // // Initialize the validators map - // validators := make(map[string]definition.Validator) - // for key, validator := range validatorMap { - // validators[key] = validator - // } + // Load verifier plugin. + vp, err := provider[definition.VerifierProvider](cfg.Root, cfg.Verifier.ID) + if err != nil { + return nil, fmt.Errorf("failed to load Verifier plugin: %w", err) + } - return &Manager{vp: vp, cfg: cfg}, nil + // Load decrypter plugin. + dp, err := provider[definition.DecrypterProvider](cfg.Root, cfg.Decrypter.ID) + if err != nil { + return nil, fmt.Errorf("failed to load Decrypter plugin: %w", err) + } + + // Load encryption plugin. + ep, err := provider[definition.EncrypterProvider](cfg.Root, cfg.Encrypter.ID) + if err != nil { + return nil, fmt.Errorf("failed to load encryption plugin: %w", err) + } + + return &Manager{sp: sp, vp: vp, pb: pb, ep: ep, dp: dp, cfg: cfg}, nil } // provider loads a plugin dynamically and retrieves its provider instance. -func provider[T any](path string, id string) (T, error) { +func provider[T any](root, id string) (T, error) { var zero T if len(strings.TrimSpace(id)) == 0 { return zero, nil } - p, err := plugin.Open(pluginPath(path, id)) + p, err := plugin.Open(pluginPath(root, id)) if err != nil { return zero, fmt.Errorf("failed to open plugin %s: %w", id, err) } @@ -99,7 +99,6 @@ func provider[T any](path string, id string) (T, error) { return zero, fmt.Errorf("failed to find Provider symbol in plugin %s: %w", id, err) } - // Ensure the symbol is of the correct type prov, ok := symbol.(*T) if !ok { return zero, fmt.Errorf("failed to cast Provider for %s", id) @@ -108,38 +107,72 @@ func provider[T any](path string, id string) (T, error) { return *prov, nil } -// pluginPath constructs the path to the plugin pkg object file. -func pluginPath(path, id string) string { - return filepath.Join(path, id+".so") +// pluginPath constructs the path to the plugin shared object file. +func pluginPath(root, id string) string { + return filepath.Join(root, id+".so") } -// Validators retrieves the validation plugin instances. -func (m *Manager) SchemaValidator(ctx context.Context) (definition.SchemaValidator, func() error, error) { +// Signer retrieves the signing plugin instance. +func (m *Manager) Signer(ctx context.Context) (definition.Signer, func() error, error) { + if m.sp == nil { + return nil, nil, fmt.Errorf("signing plugin provider not loaded") + } + + signer, close, err := m.sp.New(ctx, m.cfg.Signer.Config) + if err != nil { + return nil, nil, fmt.Errorf("failed to initialize signer: %w", err) + } + return signer, close, nil +} + +// Verifier retrieves the verification plugin instance. +func (m *Manager) Verifier(ctx context.Context) (definition.Verifier, func() error, error) { if m.vp == nil { - return nil, nil, fmt.Errorf("schema validator plugin provider not loaded") - + return nil, nil, fmt.Errorf("Verifier plugin provider not loaded") } - schemaValidator, close, err := m.vp.New(ctx, m.cfg.SchemaValidator.Config) + + Verifier, close, err := m.vp.New(ctx, m.cfg.Verifier.Config) if err != nil { - - return nil, nil, fmt.Errorf("failed to initialize schema validator: %v", err) + return nil, nil, fmt.Errorf("failed to initialize Verifier: %w", err) } - return schemaValidator, close, nil + return Verifier, close, nil } -// LoadConfig loads the configuration from a YAML file. -func LoadConfig(path string) (*Config, error) { - file, err := os.Open(path) +// Decrypter retrieves the decryption plugin instance. +func (m *Manager) Decrypter(ctx context.Context) (definition.Decrypter, func() error, error) { + if m.dp == nil { + return nil, nil, fmt.Errorf("decrypter plugin provider not loaded") + } + + decrypter, close, err := m.dp.New(ctx, m.cfg.Decrypter.Config) if err != nil { - return nil, fmt.Errorf("failed to open config file: %w", err) + return nil, nil, fmt.Errorf("failed to initialize Decrypter: %w", err) } - defer file.Close() - - var cfg Config - decoder := yaml.NewDecoder(file) - if err := decoder.Decode(&cfg); err != nil { - return nil, fmt.Errorf("failed to decode config file: %w", err) - } - - return &cfg, nil + return decrypter, close, nil +} + +// Encrypter retrieves the encryption plugin instance. +func (m *Manager) Encrypter(ctx context.Context) (definition.Encrypter, func() error, error) { + if m.ep == nil { + return nil, nil, fmt.Errorf("encryption plugin provider not loaded") + } + + encrypter, close, err := m.ep.New(ctx, m.cfg.Encrypter.Config) + if err != nil { + return nil, nil, fmt.Errorf("failed to initialize encrypter: %w", err) + } + return encrypter, close, nil +} + +// Publisher retrieves the publisher plugin instance. +func (m *Manager) Publisher(ctx context.Context) (definition.Publisher, error) { + if m.pb == nil { + return nil, fmt.Errorf("publisher plugin provider not loaded") + } + + publisher, err := m.pb.New(ctx, m.cfg.Publisher.Config) + if err != nil { + return nil, fmt.Errorf("failed to initialize publisher: %w", err) + } + return publisher, nil } diff --git a/shared/plugin/manager.go b/shared/plugin/manager.go deleted file mode 100644 index e31fc98..0000000 --- a/shared/plugin/manager.go +++ /dev/null @@ -1,108 +0,0 @@ -package plugin - -import ( - "context" - "fmt" - "path/filepath" - "plugin" - "strings" - - "github.com/beckn/beckn-onix/shared/plugin/definition" -) - -// Config represents the plugin manager configuration. -type Config struct { - Root string `yaml:"root"` - Signer PluginConfig `yaml:"signer"` - Verifier PluginConfig `yaml:"verifier"` -} - -// PluginConfig represents configuration details for a plugin. -type PluginConfig struct { - ID string `yaml:"id"` - Config map[string]string `yaml:"config"` -} - -// Manager handles dynamic plugin loading and management. -type Manager struct { - sp definition.SignerProvider - vp definition.VerifierProvider - cfg *Config -} - -// NewManager initializes a new Manager with the given configuration file. -func NewManager(ctx context.Context, cfg *Config) (*Manager, error) { - if cfg == nil { - return nil, fmt.Errorf("configuration cannot be nil") - } - - // Load signer plugin - sp, err := provider[definition.SignerProvider](cfg.Root, cfg.Signer.ID) - if err != nil { - return nil, fmt.Errorf("failed to load signer plugin: %w", err) - } - - // Load verifier plugin - vp, err := provider[definition.VerifierProvider](cfg.Root, cfg.Verifier.ID) - if err != nil { - return nil, fmt.Errorf("failed to load Verifier plugin: %w", err) - } - - return &Manager{sp: sp, vp: vp, cfg: cfg}, nil -} - -// provider loads a plugin dynamically and retrieves its provider instance. -func provider[T any](root, id string) (T, error) { - var zero T - if len(strings.TrimSpace(id)) == 0 { - return zero, nil - } - - p, err := plugin.Open(pluginPath(root, id)) - if err != nil { - return zero, fmt.Errorf("failed to open plugin %s: %w", id, err) - } - - symbol, err := p.Lookup("Provider") - if err != nil { - return zero, fmt.Errorf("failed to find Provider symbol in plugin %s: %w", id, err) - } - - prov, ok := symbol.(*T) - if !ok { - return zero, fmt.Errorf("failed to cast Provider for %s", id) - } - - return *prov, nil -} - -// pluginPath constructs the path to the plugin shared object file. -func pluginPath(root, id string) string { - return filepath.Join(root, id+".so") -} - -// Signer retrieves the signing plugin instance. -func (m *Manager) Signer(ctx context.Context) (definition.Signer, func() error, error) { - if m.sp == nil { - return nil, nil, fmt.Errorf("signing plugin provider not loaded") - } - - signer, close, err := m.sp.New(ctx, m.cfg.Signer.Config) - if err != nil { - return nil, nil, fmt.Errorf("failed to initialize signer: %w", err) - } - return signer, close, nil -} - -// Verifier retrieves the verification plugin instance. -func (m *Manager) Verifier(ctx context.Context) (definition.Verifier, func() error, error) { - if m.vp == nil { - return nil, nil, fmt.Errorf("Verifier plugin provider not loaded") - } - - Verifier, close, err := m.vp.New(ctx, m.cfg.Verifier.Config) - if err != nil { - return nil, nil, fmt.Errorf("failed to initialize Verifier: %w", err) - } - return Verifier, close, nil -} diff --git a/test.go b/test.go deleted file mode 100644 index 4239aa4..0000000 --- a/test.go +++ /dev/null @@ -1,125 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io" - "log" - "net/http" - "net/url" - - "github.com/beckn/beckn-onix/pkg/plugin/definition" - - "github.com/beckn/beckn-onix/pkg/plugin" -) - -var ( - manager *plugin.Manager -) - -// Payload represents the structure of the payload with context information. -// type Payload struct { -// Context struct { -// Action string `json:"action"` -// BapID string `json:"bap_id"` -// BapURI string `json:"bap_uri"` -// BppID string `json:"bpp_id"` -// BppURI string `json:"bpp_uri"` -// Domain string `json:"domain"` -// Location struct { -// City struct { -// Code string `json:"code"` -// } `json:"city"` -// Country struct { -// Code string `json:"code"` -// } `json:"country"` -// } `json:"location"` -// MessageID string `json:"message_id"` -// Timestamp string `json:"timestamp"` -// TransactionID string `json:"transaction_id"` -// TTL string `json:"ttl"` -// Version string `json:"version"` -// } `json:"context"` -// Message struct { -// CancellationReasonID string `json:"cancellation_reason_id"` -// Descriptor struct { -// Code string `json:"code"` -// Name string `json:"name"` -// } `json:"descriptor"` -// OrderID string `json:"order_id"` -// } `json:"message"` -// } - -func main() { - var err error - // Load the configuration. - config, err := plugin.LoadConfig("pkg/plugin/plugin.yaml") - if err != nil { - log.Fatalf("Failed to load plugins configuration: %v", err) - } - - // Initialize the plugin manager. - manager, err = plugin.NewManager(context.Background(), config) - if err != nil { - log.Fatalf("Failed to create PluginManager: %v", err) - } - - // Get the validator. - validator, _, defErr := manager.SchemaValidator(context.Background()) - if defErr != nil { - log.Fatalf("Failed to get validators: %v", defErr) - } - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - validateHandler(w, r, validator) - }) - fmt.Println("Starting server on port 8084...") - err = http.ListenAndServe(":8084", nil) - if err != nil { - log.Fatalf("Server failed to start: %v", err) - } -} - -func validateHandler(w http.ResponseWriter, r *http.Request, validators definition.SchemaValidator) { - if r.Method != http.MethodPost { - http.Error(w, "Invalid request method", http.StatusMethodNotAllowed) - return - } - - // Extract endpoint from request URL. - requestURL := r.RequestURI - u, err := url.ParseRequestURI(requestURL) - if err != nil { - http.Error(w, "Failed to parse request URL", http.StatusBadRequest) - return - } - - payloadData, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, "Failed to read payload data", http.StatusInternalServerError) - return - } - - ctx := context.Background() - // validationErr := validators.Validate(ctx, u, payloadData) - // if validationErr != (definition.SchemaValError{}) { - // http.Error(w, fmt.Sprintf("Document validation failed: %v", validationErr), http.StatusBadRequest) - // } else if !valid { - // http.Error(w, "Document validation failed", http.StatusBadRequest) - // } else { - // w.WriteHeader(http.StatusOK) - // if _, err := w.Write([]byte("Document validation succeeded!")); err != nil { - // log.Fatalf("Failed to write response: %v", err) - // } - // } - validationErr := validators.Validate(ctx, u, payloadData) - if validationErr != nil { - // Handle other types of errors - http.Error(w, fmt.Sprintf("Schema validation failed: %v", validationErr), http.StatusBadRequest) - } else { - w.WriteHeader(http.StatusOK) - if _, err := w.Write([]byte("Schema validation succeeded!")); err != nil { - log.Fatalf("Failed to write response: %v", err) - } - } -}