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 5a545b8..d557016 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,41 @@ require golang.org/x/crypto v0.36.0 require github.com/google/uuid v1.6.0 // indirect require ( + github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 + cloud.google.com/go v0.119.0 // indirect + cloud.google.com/go/auth v0.15.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect + cloud.google.com/go/iam v1.4.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.5 // indirect + github.com/googleapis/gax-go/v2 v2.14.1 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/oauth2 v0.28.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/text v0.23.0 // indirect + golang.org/x/time v0.11.0 // indirect + google.golang.org/api v0.224.0 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e // indirect + google.golang.org/grpc v1.71.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect +) + +require ( + cloud.google.com/go/pubsub v1.48.0 golang.org/x/sys v0.31.0 // indirect gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 54090b4..2bc3958 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,162 @@ +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= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.119.0 h1:tw7OjErMzJKbbjaEHkrt60KQrK5Wus/boCZ7tm5/RNE= +cloud.google.com/go v0.119.0/go.mod h1:fwB8QLzTcNevxqi8dcpR+hoMIs3jBherGS9VUBDAW08= +cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= +cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM= +cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM= +cloud.google.com/go/pubsub v1.48.0 h1:ntFpQVrr10Wj/GXSOpxGmexGynldv/bFp25H0jy8aOs= +cloud.google.com/go/pubsub v1.48.0/go.mod h1:AAtyjyIT/+zaY1ERKFJbefOvkUxRDNp3nD6TdfdqUZk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.5 h1:VgzTY2jogw3xt39CusEnFJWm7rlsq5yL5q9XdLOuP5g= +github.com/googleapis/enterprise-certificate-proxy v0.3.5/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.224.0 h1:Ir4UPtDsNiwIOHdExr3fAj4xZ42QjK7uQte3lORLJwU= +google.golang.org/api v0.224.0/go.mod h1:3V39my2xAGkodXy0vEqcEtkqgw2GtrFL5WuBZlCTCOQ= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e h1:YA5lmSs3zc/5w+xsRcHqpETkaYyK63ivEPzNTcUUlSA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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/pkg/plugin/implementation/requestPreProcessor/cmd/plugin.go b/pkg/plugin/implementation/requestPreProcessor/cmd/plugin.go new file mode 100644 index 0000000..e8280d9 --- /dev/null +++ b/pkg/plugin/implementation/requestPreProcessor/cmd/plugin.go @@ -0,0 +1,21 @@ +package main + +import ( + "context" + "net/http" + "strings" + + requestpreprocessor "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 checkKeysStr, ok := c["CheckKeys"]; ok { + config.CheckKeys = strings.Split(checkKeysStr, ",") + } + return requestpreprocessor.NewUUIDSetter(config) +} + +var Provider = provider{} diff --git a/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor.go b/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor.go new file mode 100644 index 0000000..427393e --- /dev/null +++ b/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor.go @@ -0,0 +1,108 @@ +package requestpreprocessor + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + + "github.com/google/uuid" +) + +type Config struct { + CheckKeys []string + Role string +} + +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) { + + var data map[string]any + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Failed to read request body", http.StatusInternalServerError) + return + } + if err := json.Unmarshal(body, &data); err != nil { + http.Error(w, "Failed to decode request body", http.StatusBadRequest) + return + } + contextRaw := data[contextKey] + if contextRaw == nil { + http.Error(w, fmt.Sprintf("%s field not found.", contextKey), http.StatusBadRequest) + return + } + contextData, ok := contextRaw.(map[string]any) + if !ok { + http.Error(w, fmt.Sprintf("%s field is not a map.", contextKey), http.StatusBadRequest) + return + } + var subID any + switch cfg.Role { + case "bap": + subID = contextData["bap_id"] + case "bpp": + subID = contextData["bpp_id"] + } + ctx := context.WithValue(r.Context(), subscriberIDKey, subID) + for _, key := range cfg.CheckKeys { + value := uuid.NewString() + updatedValue := update(contextData, key, value) + ctx = context.WithValue(ctx, contextKeyType(key), updatedValue) + } + data[contextKey] = contextData + updatedBody, err := json.Marshal(data) + if err != nil { + http.Error(w, "Failed to marshal updated JSON", http.StatusInternalServerError) + return + } + 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") + } + for _, key := range cfg.CheckKeys { + if key == "" { + return errors.New("checkKeys cannot contain empty strings") + } + } + return nil +} diff --git a/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor_test.go b/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor_test.go new file mode 100644 index 0000000..ad11606 --- /dev/null +++ b/pkg/plugin/implementation/requestPreProcessor/reqpreprocessor_test.go @@ -0,0 +1,139 @@ +package requestpreprocessor + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" +) + +func TestNewUUIDSetter(t *testing.T) { + tests := []struct { + name string + config *Config + requestBody map[string]any + expectedCode int + expectedKeys []string + role string + }{ + { + name: "Valid keys, update missing keys with bap role", + config: &Config{ + CheckKeys: []string{"transaction_id", "message_id"}, + Role: "bap", + }, + requestBody: map[string]any{ + "context": map[string]any{ + "transaction_id": "", + "message_id": nil, + "bap_id": "bap-123", + }, + }, + expectedCode: http.StatusOK, + expectedKeys: []string{"transaction_id", "message_id", "bap_id"}, + role: "bap", + }, + { + name: "Valid keys, do not update existing keys with bpp role", + config: &Config{ + CheckKeys: []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", + }, + }, + expectedCode: http.StatusOK, + expectedKeys: []string{"transaction_id", "message_id", "bpp_id"}, + role: "bpp", + }, + { + name: "Missing context key", + config: &Config{ + CheckKeys: []string{"transaction_id"}, + }, + requestBody: map[string]any{ + "otherKey": "value", + }, + expectedCode: http.StatusBadRequest, + }, + { + name: "Invalid context type", + config: &Config{ + CheckKeys: []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 || len(tt.config.CheckKeys) == 0 { + if err == nil { + t.Fatal("Expected an error, 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) { + ctx := r.Context() + w.WriteHeader(http.StatusOK) + if subID, ok := ctx.Value(subscriberIDKey).(string); ok { + 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 + } + } else { + http.Error(w, "Subscriber ID not found", http.StatusInternalServerError) + return + } + }) + middleware(dummyHandler).ServeHTTP(rec, req) + if rec.Code != tt.expectedCode { + t.Errorf("Expected status code %d, but got %d", tt.expectedCode, rec.Code) + } + if rec.Code == http.StatusOK { + 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" + } + + if subID, ok := responseBody["subscriber_id"].(string); ok { + expectedSubID := tt.requestBody["context"].(map[string]any)[expectedSubIDKey] + if subID != expectedSubID { + t.Errorf("Expected subscriber_id %v, but got %v", expectedSubID, subID) + } + } else { + t.Error("subscriber_id not found in response") + } + } + }) + } +} 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/shared/plugin/manager.go b/pkg/plugin/manager.go similarity index 52% rename from shared/plugin/manager.go rename to pkg/plugin/manager.go index e31fc98..86a0b02 100644 --- a/shared/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -7,14 +7,17 @@ import ( "plugin" "strings" - "github.com/beckn/beckn-onix/shared/plugin/definition" + "github.com/beckn/beckn-onix/pkg/plugin/definition" ) // Config represents the plugin manager configuration. type Config struct { - Root string `yaml:"root"` - Signer PluginConfig `yaml:"signer"` - Verifier PluginConfig `yaml:"verifier"` + Root string `yaml:"root"` + Signer PluginConfig `yaml:"signer"` + Verifier PluginConfig `yaml:"verifier"` + Decrypter PluginConfig `yaml:"decrypter"` + Encrypter PluginConfig `yaml:"encrypter"` + Publisher PluginConfig `yaml:"publisher"` } // PluginConfig represents configuration details for a plugin. @@ -27,6 +30,9 @@ type PluginConfig struct { type Manager struct { sp definition.SignerProvider vp definition.VerifierProvider + dp definition.DecrypterProvider + ep definition.EncrypterProvider + pb definition.PublisherProvider cfg *Config } @@ -36,19 +42,37 @@ func NewManager(ctx context.Context, cfg *Config) (*Manager, error) { return nil, fmt.Errorf("configuration cannot be nil") } - // Load signer plugin + // 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 + // 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) + } + + // 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 + // 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. @@ -106,3 +130,42 @@ func (m *Manager) Verifier(ctx context.Context) (definition.Verifier, func() err } return Verifier, close, nil } + +// 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, nil, fmt.Errorf("failed to initialize Decrypter: %w", err) + } + 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 +}