diff --git a/.golangci.yml b/.golangci.yml index ea581db..6dc2333 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -205,21 +205,19 @@ linters: - legacy - std-error-handling rules: - - path: audit/api/api_common.go + - path: internal/audit/api/api_common.go text: context-as-argument - linters: - gochecknoglobals - path: audit/api/api.go|log/log.go|audit/api/model.go|telemetry/telemetry.go + path: pkg/audit/common/api.go|pkg/log/log.go|internal/telemetry/telemetry.go - linters: - dupl - path: audit/api/api_.*.go - - path: audit/api/model.go - text: 'exported: type name will be used as api.ApiRequest by other packages' - - path: audit/api/model_test.go|audit/api/model.go + path: pkg/audit/api/api_.*.go + - path: internal/audit/api/model_test.go|internal/audit/api/model.go text: G115 - linters: - gosec - path: audit/api/test_data.go + path: internal/audit/api/test_data.go - linters: - dogsled - dupl @@ -268,7 +266,7 @@ linters: - unparam - wastedassign - wsl - path: test_.*\.go|audit/messaging/solace.go + path: test_.*\.go|pkg/messaging/test/solace.go paths: - third_party$ - builtin$ diff --git a/go.mod b/go.mod index 0ff4ab8..d8a02aa 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,23 @@ module dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git go 1.23.4 require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1 + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 + buf.build/go/protovalidate v0.12.0 github.com/Azure/go-amqp v1.4.0 - github.com/bufbuild/protovalidate-go v0.9.3 github.com/docker/docker v28.1.1+incompatible github.com/google/uuid v1.6.0 - github.com/lestrrat-go/jwx/v2 v2.1.5 + github.com/lestrrat-go/jwx/v2 v2.1.6 github.com/rs/zerolog v1.34.0 github.com/stretchr/testify v1.10.0 - github.com/testcontainers/testcontainers-go v0.36.0 + github.com/testcontainers/testcontainers-go v0.37.0 go.opentelemetry.io/otel v1.35.0 go.opentelemetry.io/otel/trace v1.35.0 google.golang.org/protobuf v1.36.6 ) require ( - cel.dev/expr v0.23.1 // indirect - dario.cat/mergo v1.0.1 // indirect + cel.dev/expr v0.24.0 // indirect + dario.cat/mergo v1.0.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect @@ -32,7 +32,7 @@ require ( github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/ebitengine/purego v0.8.2 // indirect + github.com/ebitengine/purego v0.8.3 // 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 @@ -66,7 +66,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/shirou/gopsutil/v4 v4.25.3 // indirect + github.com/shirou/gopsutil/v4 v4.25.4 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -77,12 +77,12 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e67d207..e362c3c 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,11 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1 h1:zgJPqo17m28+Lf5BW4xv3PvU20BnrmTcGYrog22lLIU= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U= -cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg= -cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 h1:YhMSc48s25kr7kv31Z8vf7sPUIq5YJva9z1mn/hAt0M= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U= +buf.build/go/protovalidate v0.12.0 h1:4GKJotbspQjRCcqZMGVSuC8SjwZ/FmgtSuKDpKUTZew= +buf.build/go/protovalidate v0.12.0/go.mod h1:q3PFfbzI05LeqxSwq+begW2syjy2Z6hLxZSkP1OH/D0= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-amqp v1.4.0 h1:Xj3caqi4comOF/L1Uc5iuBxR/pB6KumejC01YQOqOR4= @@ -14,8 +16,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= -github.com/bufbuild/protovalidate-go v0.9.3 h1:XvdtwQuppS3wjzGfpOirsqwN5ExH2+PiIuA/XZd3MTM= -github.com/bufbuild/protovalidate-go v0.9.3/go.mod h1:2lUDP6fNd3wxznRNH3Nj64VB07+PySeslamkerwP6tE= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -40,10 +40,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= -github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc= +github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -85,8 +83,8 @@ github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCG github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.1.5 h1:PQI5gzadLfJ22ckLrejPVX6eGXKM4M4eGi5fW2jjA3o= -github.com/lestrrat-go/jwx/v2 v2.1.5/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU= +github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA= +github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= @@ -135,8 +133,8 @@ github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE= -github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= +github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw= +github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= @@ -153,8 +151,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= -github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= +github.com/testcontainers/testcontainers-go v0.37.0 h1:L2Qc0vkTw2EHWQ08djon0D2uw7Z/PtHS/QzZZ5Ra/hg= +github.com/testcontainers/testcontainers-go v0.37.0/go.mod h1:QPzbxZhQ6Bclip9igjLFj6z0hs01bU8lrl2dHQmgFGM= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= @@ -186,10 +184,10 @@ go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -212,14 +210,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= 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.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 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= @@ -230,10 +228,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0= -google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9 h1:WvBuA5rjZx9SNIzgcU53OohgZy6lKSus++uY4xLaWKc= +google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:W3S/3np0/dPWsWLi1h/UymYctGXaGBM2StwzD0y140U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= 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 v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= diff --git a/audit/api/api_common.go b/internal/audit/api/api_common.go similarity index 55% rename from audit/api/api_common.go rename to internal/audit/api/api_common.go index 73c4011..dfb99e5 100644 --- a/audit/api/api_common.go +++ b/internal/audit/api/api_common.go @@ -2,75 +2,27 @@ package api import ( "context" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/telemetry" "errors" "fmt" "regexp" "strings" "github.com/google/uuid" - - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - "google.golang.org/protobuf/proto" -) -// ContentTypeCloudEventsProtobuf the cloudevents protobuf content-type sent in metadata of messages -const ContentTypeCloudEventsProtobuf = "application/cloudevents+protobuf" -const ContentTypeCloudEventsJson = "application/cloudevents+json; charset=UTF-8" + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalTelemetry "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/telemetry" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api" +) var TopicNamePattern = regexp.MustCompile(`^topic://stackit-platform/t/swz/audit-log/(?:conway|eu01|eu02|sx-stoi01)/[Vv][1-9](?:\.\d)?/[A-Za-z0-9-]+/[A-Za-z0-9-/]+`) -// ErrAttributeIdentifierInvalid indicates that the object identifier -// and the identifier in the checked attribute do not match -var ErrAttributeIdentifierInvalid = errors.New("attribute identifier invalid") - -// ErrAttributeTypeInvalid indicates that an invalid type has been provided. -var ErrAttributeTypeInvalid = errors.New("attribute type invalid") - -// ErrCloudEventNil states that the given cloud event is nil -var ErrCloudEventNil = errors.New("cloud event nil") - -// ErrEventNil indicates that the event was nil -var ErrEventNil = errors.New("event is nil") - -// ErrInvalidRoutableIdentifierForSystemEvent states that the routable identifier is not valid for a system event -var ErrInvalidRoutableIdentifierForSystemEvent = errors.New("invalid identifier for system event") - -// ErrMessagingApiNil states that the messaging api is nil -var ErrMessagingApiNil = errors.New("messaging api nil") - -// ErrObjectIdentifierNil indicates that the object identifier was nil -var ErrObjectIdentifierNil = errors.New("object identifier is nil") - -// ErrObjectIdentifierVisibilityMismatch indicates that a reference mismatch was detected. -// -// Valid combinations are: -// * Visibility: Public, ObjectIdentifier: -// * Visibility: Private, ObjectIdentifier: -var ErrObjectIdentifierVisibilityMismatch = errors.New("object reference visibility mismatch") - -// ErrTopicNameResolverNil states that the topic name resolve is nil -var ErrTopicNameResolverNil = errors.New("topic name resolver nil") - -// ErrUnknownObjectType indicates that the given input is an unknown object type -var ErrUnknownObjectType = errors.New("unknown object type") - -// ErrUnsupportedEventTypeDataAccess states that the event type "data-access" is currently not supported -var ErrUnsupportedEventTypeDataAccess = errors.New("unsupported event type data access") - -// ErrUnsupportedObjectIdentifierType indicates that an unsupported object identifier type has been provided -var ErrUnsupportedObjectIdentifierType = errors.New("unsupported object identifier type") - -// ErrUnsupportedRoutableType indicates that the given input is an unsupported routable type -var ErrUnsupportedRoutableType = errors.New("unsupported routable type") - -func validateAndSerializePartially( - validator ProtobufValidator, +func ValidateAndSerializePartially( + validator pkgAuditCommon.ProtobufValidator, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, ) (*auditV1.RoutableAuditEvent, error) { // Check preconditions @@ -89,10 +41,10 @@ func validateAndSerializePartially( } func newValidatedRoutableAuditEvent( - validator ProtobufValidator, + validator pkgAuditCommon.ProtobufValidator, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier) (*auditV1.RoutableAuditEvent, error) { + routableIdentifier *pkgAuditCommon.RoutableIdentifier) (*auditV1.RoutableAuditEvent, error) { // Test serialization even if the data is dropped later when logging to the legacy solution auditEventBytes, err := proto.Marshal(event) @@ -120,18 +72,18 @@ func newValidatedRoutableAuditEvent( } func validateAuditLogEntry( - validator ProtobufValidator, + validator pkgAuditCommon.ProtobufValidator, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, ) error { // Return error if the given event or object identifier is nil if event == nil { - return ErrEventNil + return pkgAuditCommon.ErrEventNil } if routableIdentifier == nil { - return ErrObjectIdentifierNil + return pkgAuditCommon.ErrObjectIdentifierNil } // Validate the actual event @@ -142,21 +94,21 @@ func validateAuditLogEntry( // Ensure that a valid object identifier is set if the event is public if isSystemIdentifier(routableIdentifier) && visibility == auditV1.Visibility_VISIBILITY_PUBLIC { - return ErrObjectIdentifierVisibilityMismatch + return pkgAuditCommon.ErrObjectIdentifierVisibilityMismatch } // Check that provided identifier type is supported if err := routableIdentifier.Type.IsSupportedType(); err != nil { - if errors.Is(err, ErrUnknownObjectType) { - return ErrUnsupportedRoutableType + if errors.Is(err, pkgAuditCommon.ErrUnknownObjectType) { + return pkgAuditCommon.ErrUnsupportedRoutableType } return err } // Check identifier consistency across event attributes - if strings.HasSuffix(event.LogName, string(EventTypeSystemEvent)) { - if routableIdentifier.Identifier != SystemIdentifier.Identifier || routableIdentifier.Type != ObjectTypeSystem { - return ErrInvalidRoutableIdentifierForSystemEvent + if strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeSystemEvent)) { + if routableIdentifier.Identifier != pkgAuditCommon.SystemIdentifier.Identifier || routableIdentifier.Type != pkgAuditCommon.ObjectTypeSystem { + return pkgAuditCommon.ErrInvalidRoutableIdentifierForSystemEvent } // The resource name can either contain the system identifier or another resource identifier } else { @@ -171,32 +123,32 @@ func validateAuditLogEntry( } // Send implements AuditApi.Send -func send( - topicNameResolver TopicNameResolver, - messagingApi messaging.Api, +func Send( + topicNameResolver pkgAuditCommon.TopicNameResolver, + messagingApi pkgMessagingApi.Api, ctx context.Context, - routableIdentifier *RoutableIdentifier, - cloudEvent *CloudEvent, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, + cloudEvent *pkgAuditCommon.CloudEvent, ) error { // Check that given objects are not nil if topicNameResolver == nil { - return ErrTopicNameResolverNil + return pkgAuditCommon.ErrTopicNameResolverNil } if messagingApi == nil { - return ErrMessagingApiNil + return pkgAuditCommon.ErrMessagingApiNil } if cloudEvent == nil { - return ErrCloudEventNil + return pkgAuditCommon.ErrCloudEventNil } if routableIdentifier == nil { - return ErrObjectIdentifierNil + return pkgAuditCommon.ErrObjectIdentifierNil } // Check that provided identifier type is supported if err := routableIdentifier.Type.IsSupportedType(); err != nil { - if errors.Is(err, ErrUnknownObjectType) { - return ErrUnsupportedRoutableType + if errors.Is(err, pkgAuditCommon.ErrUnknownObjectType) { + return pkgAuditCommon.ErrUnsupportedRoutableType } return err } @@ -225,15 +177,15 @@ func send( // Telemetry applicationAttributes["cloudEvents:sdklanguage"] = "go" - auditGoVersion := telemetry.AuditGoVersion + auditGoVersion := internalTelemetry.AuditGoVersion if auditGoVersion != "" { applicationAttributes["cloudEvents:sdkversion"] = auditGoVersion } - auditGoGrpcVersion := telemetry.AuditGoGrpcVersion + auditGoGrpcVersion := internalTelemetry.AuditGoGrpcVersion if auditGoGrpcVersion != "" { applicationAttributes["cloudEvents:sdkgrpcversion"] = auditGoGrpcVersion } - auditGoHttpVersion := telemetry.AuditGoHttpVersion + auditGoHttpVersion := internalTelemetry.AuditGoHttpVersion if auditGoHttpVersion != "" { applicationAttributes["cloudEvents:sdkhttpversion"] = auditGoHttpVersion } @@ -246,16 +198,16 @@ func send( applicationAttributes) } -func isSystemIdentifier(identifier *RoutableIdentifier) bool { - if identifier.Identifier == uuid.Nil.String() && identifier.Type == ObjectTypeSystem { +func isSystemIdentifier(identifier *pkgAuditCommon.RoutableIdentifier) bool { + if identifier.Identifier == uuid.Nil.String() && identifier.Type == pkgAuditCommon.ObjectTypeSystem { return true } return false } -func areIdentifiersIdentical(routableIdentifier *RoutableIdentifier, logName string) error { +func areIdentifiersIdentical(routableIdentifier *pkgAuditCommon.RoutableIdentifier, logName string) error { dataType, identifier := getTypeAndIdentifierFromString(logName) - objectType := ObjectTypeFromPluralString(dataType) + objectType := pkgAuditCommon.ObjectTypeFromPluralString(dataType) err := objectType.IsSupportedType() if err != nil { return err @@ -263,12 +215,12 @@ func areIdentifiersIdentical(routableIdentifier *RoutableIdentifier, logName str return areTypeAndIdentifierIdentical(routableIdentifier, objectType, identifier) } -func areTypeAndIdentifierIdentical(routableIdentifier *RoutableIdentifier, dataType ObjectType, identifier string) error { +func areTypeAndIdentifierIdentical(routableIdentifier *pkgAuditCommon.RoutableIdentifier, dataType pkgAuditCommon.ObjectType, identifier string) error { if routableIdentifier.Identifier != identifier { - return ErrAttributeIdentifierInvalid + return pkgAuditCommon.ErrAttributeIdentifierInvalid } if routableIdentifier.Type != dataType { - return ErrAttributeTypeInvalid + return pkgAuditCommon.ErrAttributeTypeInvalid } return nil } diff --git a/audit/api/api_common_test.go b/internal/audit/api/api_common_test.go similarity index 58% rename from audit/api/api_common_test.go rename to internal/audit/api/api_common_test.go index c48169e..0a5a787 100644 --- a/audit/api/api_common_test.go +++ b/internal/audit/api/api_common_test.go @@ -8,14 +8,15 @@ import ( "testing" "time" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - - "github.com/bufbuild/protovalidate-go" + "buf.build/go/protovalidate" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/protobuf/proto" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api" ) type MessagingApiMock struct { @@ -38,32 +39,18 @@ func (m *MessagingApiMock) Close(_ context.Context) error { return nil } -type ProtobufValidatorMock struct { - mock.Mock -} - -func (m *ProtobufValidatorMock) Validate(msg proto.Message, options ...protovalidate.ValidationOption) error { - var args mock.Arguments - if len(options) > 0 { - args = m.Called(msg, options) - } else { - args = m.Called(msg) - } - return args.Error(0) -} - type TopicNameResolverMock struct { mock.Mock } -func (m *TopicNameResolverMock) Resolve(routableIdentifier *RoutableIdentifier) (string, error) { +func (m *TopicNameResolverMock) Resolve(routableIdentifier *pkgAuditCommon.RoutableIdentifier) (string, error) { args := m.Called(routableIdentifier) return args.String(0), args.Error(1) } -func NewValidator(t *testing.T) ProtobufValidator { +func NewValidator(t *testing.T) pkgAuditCommon.ProtobufValidator { validator, err := protovalidate.New() - var protoValidator ProtobufValidator = validator + var protoValidator pkgAuditCommon.ProtobufValidator = validator assert.NoError(t, err) return protoValidator @@ -72,20 +59,20 @@ func NewValidator(t *testing.T) ProtobufValidator { func Test_ValidateAndSerializePartially_EventNil(t *testing.T) { validator := NewValidator(t) - _, err := validateAndSerializePartially( + _, err := ValidateAndSerializePartially( validator, nil, auditV1.Visibility_VISIBILITY_PUBLIC, nil) - assert.ErrorIs(t, err, ErrEventNil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrEventNil) } func Test_ValidateAndSerializePartially_AuditEventValidationFailed(t *testing.T) { validator := NewValidator(t) - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := NewOrganizationAuditEvent(nil) event.LogName = "" - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) assert.EqualError(t, err, "validation error:\n - log_name: value is required [required]") } @@ -93,8 +80,8 @@ func Test_ValidateAndSerializePartially_AuditEventValidationFailed(t *testing.T) func Test_ValidateAndSerializePartially_RoutableEventValidationFailed(t *testing.T) { validator := NewValidator(t) - event, objectIdentifier := newOrganizationAuditEvent(nil) - _, err := validateAndSerializePartially(validator, event, 3, NewRoutableIdentifier(objectIdentifier)) + event, objectIdentifier := NewOrganizationAuditEvent(nil) + _, err := ValidateAndSerializePartially(validator, event, 3, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) assert.EqualError(t, err, "validation error:\n - visibility: value must be one of the defined enum values [enum.defined_only]") } @@ -102,47 +89,47 @@ func Test_ValidateAndSerializePartially_RoutableEventValidationFailed(t *testing func Test_ValidateAndSerializePartially_CheckVisibility_Event(t *testing.T) { validator := NewValidator(t) - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := NewOrganizationAuditEvent(nil) t.Run("Visibility public - object identifier nil", func(t *testing.T) { - _, err := validateAndSerializePartially( + _, err := ValidateAndSerializePartially( validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil) - assert.ErrorIs(t, err, ErrObjectIdentifierNil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierNil) }) t.Run("Visibility private - object identifier nil", func(t *testing.T) { - _, err := validateAndSerializePartially( + _, err := ValidateAndSerializePartially( validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil) - assert.ErrorIs(t, err, ErrObjectIdentifierNil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierNil) }) t.Run("Visibility public - object identifier system", func(t *testing.T) { - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) - assert.ErrorIs(t, err, ErrObjectIdentifierVisibilityMismatch) + assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierVisibilityMismatch) }) t.Run("Visibility public - object identifier set", func(t *testing.T) { - routableEvent, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) + routableEvent, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) assert.NoError(t, err) assert.NotNil(t, routableEvent) }) t.Run("Visibility private - object identifier system", func(t *testing.T) { - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, RoutableSystemIdentifier) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, pkgAuditCommon.RoutableSystemIdentifier) - assert.ErrorIs(t, err, ErrAttributeIdentifierInvalid) + assert.ErrorIs(t, err, pkgAuditCommon.ErrAttributeIdentifierInvalid) }) t.Run("Visibility private - object identifier set", func(t *testing.T) { - routableEvent, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, NewRoutableIdentifier(objectIdentifier)) + routableEvent, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) assert.NoError(t, err) assert.NotNil(t, routableEvent) @@ -152,130 +139,130 @@ func Test_ValidateAndSerializePartially_CheckVisibility_Event(t *testing.T) { func Test_ValidateAndSerializePartially_CheckVisibility_SystemEvent(t *testing.T) { validator := NewValidator(t) - event := newSystemAuditEvent(nil) + event := NewSystemAuditEvent(nil) t.Run("Visibility public - object identifier nil", func(t *testing.T) { - _, err := validateAndSerializePartially( + _, err := ValidateAndSerializePartially( validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil) - assert.ErrorIs(t, err, ErrObjectIdentifierNil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierNil) }) t.Run("Visibility private - object identifier nil", func(t *testing.T) { - _, err := validateAndSerializePartially( + _, err := ValidateAndSerializePartially( validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil) - assert.ErrorIs(t, err, ErrObjectIdentifierNil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierNil) }) t.Run("Visibility public - object identifier system", func(t *testing.T) { - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) - assert.ErrorIs(t, err, ErrObjectIdentifierVisibilityMismatch) + assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierVisibilityMismatch) }) t.Run("Visibility public - object identifier set", func(t *testing.T) { - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier( - &auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(ObjectTypeOrganization)})) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier( + &auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(pkgAuditCommon.ObjectTypeOrganization)})) - assert.ErrorIs(t, err, ErrInvalidRoutableIdentifierForSystemEvent) + assert.ErrorIs(t, err, pkgAuditCommon.ErrInvalidRoutableIdentifierForSystemEvent) }) t.Run("Visibility private - object identifier system", func(t *testing.T) { - routableEvent, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, RoutableSystemIdentifier) + routableEvent, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, pkgAuditCommon.RoutableSystemIdentifier) assert.NoError(t, err) assert.NotNil(t, routableEvent) }) t.Run("Visibility private - object identifier set", func(t *testing.T) { - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, NewRoutableIdentifier( - &auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(ObjectTypeOrganization)})) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, pkgAuditCommon.NewRoutableIdentifier( + &auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(pkgAuditCommon.ObjectTypeOrganization)})) - assert.ErrorIs(t, err, ErrInvalidRoutableIdentifierForSystemEvent) + assert.ErrorIs(t, err, pkgAuditCommon.ErrInvalidRoutableIdentifierForSystemEvent) }) } func Test_ValidateAndSerializePartially_UnsupportedIdentifierType(t *testing.T) { validator := NewValidator(t) - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := NewFolderAuditEvent(nil) objectIdentifier.Type = "invalid" - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) - assert.ErrorIs(t, err, ErrUnsupportedRoutableType) + assert.ErrorIs(t, err, pkgAuditCommon.ErrUnsupportedRoutableType) } func Test_ValidateAndSerializePartially_LogNameIdentifierMismatch(t *testing.T) { validator := NewValidator(t) - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := NewFolderAuditEvent(nil) parts := strings.Split(event.LogName, "/") identifier := parts[1] t.Run("LogName type mismatch", func(t *testing.T) { event.LogName = fmt.Sprintf("projects/%s/logs/admin-activity", identifier) - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) - assert.ErrorIs(t, err, ErrAttributeTypeInvalid) + assert.ErrorIs(t, err, pkgAuditCommon.ErrAttributeTypeInvalid) }) t.Run("LogName identifier mismatch", func(t *testing.T) { event.LogName = fmt.Sprintf("folders/%s/logs/admin-activity", uuid.NewString()) - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) - assert.ErrorIs(t, err, ErrAttributeIdentifierInvalid) + assert.ErrorIs(t, err, pkgAuditCommon.ErrAttributeIdentifierInvalid) }) } func Test_ValidateAndSerializePartially_ResourceNameIdentifierMismatch(t *testing.T) { validator := NewValidator(t) - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := NewFolderAuditEvent(nil) parts := strings.Split(event.ProtoPayload.ResourceName, "/") identifier := parts[1] t.Run("ResourceName type mismatch", func(t *testing.T) { event.ProtoPayload.ResourceName = fmt.Sprintf("projects/%s", identifier) - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) - assert.ErrorIs(t, err, ErrAttributeTypeInvalid) + assert.ErrorIs(t, err, pkgAuditCommon.ErrAttributeTypeInvalid) }) t.Run("ResourceName identifier mismatch", func(t *testing.T) { event.ProtoPayload.ResourceName = fmt.Sprintf("folders/%s", uuid.NewString()) - _, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) + _, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) - assert.ErrorIs(t, err, ErrAttributeIdentifierInvalid) + assert.ErrorIs(t, err, pkgAuditCommon.ErrAttributeIdentifierInvalid) }) } func Test_ValidateAndSerializePartially_SystemEvent(t *testing.T) { validator := NewValidator(t) - event := newSystemAuditEvent(nil) + event := NewSystemAuditEvent(nil) - routableEvent, err := validateAndSerializePartially( - validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, RoutableSystemIdentifier) + routableEvent, err := ValidateAndSerializePartially( + validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, pkgAuditCommon.RoutableSystemIdentifier) assert.NoError(t, err) - assert.Equal(t, event.LogName, fmt.Sprintf("system/%s/logs/%s", SystemIdentifier.Identifier, EventTypeSystemEvent)) - assert.True(t, proto.Equal(routableEvent.ObjectIdentifier, SystemIdentifier)) + assert.Equal(t, event.LogName, fmt.Sprintf("system/%s/logs/%s", pkgAuditCommon.SystemIdentifier.Identifier, pkgAuditCommon.EventTypeSystemEvent)) + assert.True(t, proto.Equal(routableEvent.ObjectIdentifier, pkgAuditCommon.SystemIdentifier)) } func Test_Send_TopicNameResolverNil(t *testing.T) { - err := send(nil, nil, context.Background(), nil, nil) - assert.ErrorIs(t, err, ErrTopicNameResolverNil) + err := Send(nil, nil, context.Background(), nil, nil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrTopicNameResolverNil) } func Test_Send_TopicNameResolutionError(t *testing.T) { @@ -283,86 +270,86 @@ func Test_Send_TopicNameResolutionError(t *testing.T) { topicNameResolverMock := TopicNameResolverMock{} topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", expectedError) - var topicNameResolver TopicNameResolver = &topicNameResolverMock + var topicNameResolver pkgAuditCommon.TopicNameResolver = &topicNameResolverMock - var cloudEvent = CloudEvent{} + var cloudEvent = pkgAuditCommon.CloudEvent{} - var messagingApi messaging.Api = &messaging.AmqpApi{} - err := send(topicNameResolver, messagingApi, context.Background(), RoutableSystemIdentifier, &cloudEvent) + var messagingApi pkgMessagingApi.Api = &pkgMessagingApi.AmqpApi{} + err := Send(topicNameResolver, messagingApi, context.Background(), pkgAuditCommon.RoutableSystemIdentifier, &cloudEvent) assert.ErrorIs(t, err, expectedError) } func Test_Send_MessagingApiNil(t *testing.T) { - var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: "test"} - err := send(topicNameResolver, nil, context.Background(), nil, nil) - assert.ErrorIs(t, err, ErrMessagingApiNil) + var topicNameResolver pkgAuditCommon.TopicNameResolver = &pkgAuditCommon.StaticTopicNameTestResolver{TopicName: "test"} + err := Send(topicNameResolver, nil, context.Background(), nil, nil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrMessagingApiNil) } func Test_Send_CloudEventNil(t *testing.T) { - var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: "test"} - var messagingApi messaging.Api = &messaging.AmqpApi{} + var topicNameResolver pkgAuditCommon.TopicNameResolver = &pkgAuditCommon.StaticTopicNameTestResolver{TopicName: "test"} + var messagingApi pkgMessagingApi.Api = &pkgMessagingApi.AmqpApi{} - err := send(topicNameResolver, messagingApi, context.Background(), nil, nil) - assert.ErrorIs(t, err, ErrCloudEventNil) + err := Send(topicNameResolver, messagingApi, context.Background(), nil, nil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrCloudEventNil) } func Test_Send_ObjectIdentifierNil(t *testing.T) { - var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: "test"} - var messagingApi messaging.Api = &messaging.AmqpApi{} - var cloudEvent = CloudEvent{} + var topicNameResolver pkgAuditCommon.TopicNameResolver = &pkgAuditCommon.StaticTopicNameTestResolver{TopicName: "test"} + var messagingApi pkgMessagingApi.Api = &pkgMessagingApi.AmqpApi{} + var cloudEvent = pkgAuditCommon.CloudEvent{} - err := send(topicNameResolver, messagingApi, context.Background(), nil, &cloudEvent) - assert.ErrorIs(t, err, ErrObjectIdentifierNil) + err := Send(topicNameResolver, messagingApi, context.Background(), nil, &cloudEvent) + assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierNil) } func Test_Send_UnsupportedObjectIdentifierType(t *testing.T) { - var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: "test"} - var messagingApi messaging.Api = &messaging.AmqpApi{} - var cloudEvent = CloudEvent{} + var topicNameResolver pkgAuditCommon.TopicNameResolver = &pkgAuditCommon.StaticTopicNameTestResolver{TopicName: "test"} + var messagingApi pkgMessagingApi.Api = &pkgMessagingApi.AmqpApi{} + var cloudEvent = pkgAuditCommon.CloudEvent{} var objectIdentifier = auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: "unsupported"} - err := send(topicNameResolver, messagingApi, context.Background(), NewRoutableIdentifier(&objectIdentifier), &cloudEvent) - assert.ErrorIs(t, err, ErrUnsupportedRoutableType) + err := Send(topicNameResolver, messagingApi, context.Background(), pkgAuditCommon.NewRoutableIdentifier(&objectIdentifier), &cloudEvent) + assert.ErrorIs(t, err, pkgAuditCommon.ErrUnsupportedRoutableType) } func Test_Send(t *testing.T) { topicNameResolverMock := TopicNameResolverMock{} topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", nil) - var topicNameResolver TopicNameResolver = &topicNameResolverMock + var topicNameResolver pkgAuditCommon.TopicNameResolver = &topicNameResolverMock messagingApiMock := MessagingApiMock{} messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - var messagingApi messaging.Api = &messagingApiMock + var messagingApi pkgMessagingApi.Api = &messagingApiMock - var cloudEvent = CloudEvent{} - assert.NoError(t, send(topicNameResolver, messagingApi, context.Background(), RoutableSystemIdentifier, &cloudEvent)) + var cloudEvent = pkgAuditCommon.CloudEvent{} + assert.NoError(t, Send(topicNameResolver, messagingApi, context.Background(), pkgAuditCommon.RoutableSystemIdentifier, &cloudEvent)) assert.True(t, messagingApiMock.AssertNumberOfCalls(t, "Send", 1)) } func Test_SendAllHeadersSet(t *testing.T) { topicNameResolverMock := TopicNameResolverMock{} topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", nil) - var topicNameResolver TopicNameResolver = &topicNameResolverMock + var topicNameResolver pkgAuditCommon.TopicNameResolver = &topicNameResolverMock messagingApiMock := MessagingApiMock{} messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - var messagingApi messaging.Api = &messagingApiMock + var messagingApi pkgMessagingApi.Api = &messagingApiMock traceState := "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE" traceParent := "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" expectedTime := time.Now() - var cloudEvent = CloudEvent{ + var cloudEvent = pkgAuditCommon.CloudEvent{ SpecVersion: "1.0", Source: "resourcemanager", Id: "id", Time: expectedTime, - DataContentType: ContentTypeCloudEventsProtobuf, + DataContentType: pkgAuditCommon.ContentTypeCloudEventsProtobuf, DataType: "type", Subject: "subject", TraceParent: &traceParent, TraceState: &traceState, } - assert.NoError(t, send(topicNameResolver, messagingApi, context.Background(), RoutableSystemIdentifier, &cloudEvent)) + assert.NoError(t, Send(topicNameResolver, messagingApi, context.Background(), pkgAuditCommon.RoutableSystemIdentifier, &cloudEvent)) assert.True(t, messagingApiMock.AssertNumberOfCalls(t, "Send", 1)) arguments := messagingApiMock.Calls[0].Arguments @@ -370,14 +357,14 @@ func Test_SendAllHeadersSet(t *testing.T) { assert.Equal(t, "topic", topic) contentType := arguments.Get(3).(string) - assert.Equal(t, ContentTypeCloudEventsProtobuf, contentType) + assert.Equal(t, pkgAuditCommon.ContentTypeCloudEventsProtobuf, contentType) applicationProperties := arguments.Get(4).(map[string]any) assert.Equal(t, "1.0", applicationProperties["cloudEvents:specversion"]) assert.Equal(t, "resourcemanager", applicationProperties["cloudEvents:source"]) assert.Equal(t, "id", applicationProperties["cloudEvents:id"]) assert.Equal(t, expectedTime.UnixMilli(), applicationProperties["cloudEvents:time"]) - assert.Equal(t, ContentTypeCloudEventsProtobuf, applicationProperties["cloudEvents:datacontenttype"]) + assert.Equal(t, pkgAuditCommon.ContentTypeCloudEventsProtobuf, applicationProperties["cloudEvents:datacontenttype"]) assert.Equal(t, "type", applicationProperties["cloudEvents:type"]) assert.Equal(t, "subject", applicationProperties["cloudEvents:subject"]) assert.Equal(t, traceParent, applicationProperties["cloudEvents:traceparent"]) @@ -388,23 +375,23 @@ func Test_SendAllHeadersSet(t *testing.T) { func Test_SendWithoutOptionalHeadersSet(t *testing.T) { topicNameResolverMock := TopicNameResolverMock{} topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", nil) - var topicNameResolver TopicNameResolver = &topicNameResolverMock + var topicNameResolver pkgAuditCommon.TopicNameResolver = &topicNameResolverMock messagingApiMock := MessagingApiMock{} messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - var messagingApi messaging.Api = &messagingApiMock + var messagingApi pkgMessagingApi.Api = &messagingApiMock expectedTime := time.Now() - var cloudEvent = CloudEvent{ + var cloudEvent = pkgAuditCommon.CloudEvent{ SpecVersion: "1.0", Source: "resourcemanager", Id: "id", Time: expectedTime, - DataContentType: ContentTypeCloudEventsProtobuf, + DataContentType: pkgAuditCommon.ContentTypeCloudEventsProtobuf, DataType: "type", Subject: "subject", } - assert.NoError(t, send(topicNameResolver, messagingApi, context.Background(), RoutableSystemIdentifier, &cloudEvent)) + assert.NoError(t, Send(topicNameResolver, messagingApi, context.Background(), pkgAuditCommon.RoutableSystemIdentifier, &cloudEvent)) assert.True(t, messagingApiMock.AssertNumberOfCalls(t, "Send", 1)) arguments := messagingApiMock.Calls[0].Arguments @@ -412,14 +399,14 @@ func Test_SendWithoutOptionalHeadersSet(t *testing.T) { assert.Equal(t, "topic", topic) contentType := arguments.Get(3).(string) - assert.Equal(t, ContentTypeCloudEventsProtobuf, contentType) + assert.Equal(t, pkgAuditCommon.ContentTypeCloudEventsProtobuf, contentType) applicationProperties := arguments.Get(4).(map[string]any) assert.Equal(t, "1.0", applicationProperties["cloudEvents:specversion"]) assert.Equal(t, "resourcemanager", applicationProperties["cloudEvents:source"]) assert.Equal(t, "id", applicationProperties["cloudEvents:id"]) assert.Equal(t, expectedTime.UnixMilli(), applicationProperties["cloudEvents:time"]) - assert.Equal(t, ContentTypeCloudEventsProtobuf, applicationProperties["cloudEvents:datacontenttype"]) + assert.Equal(t, pkgAuditCommon.ContentTypeCloudEventsProtobuf, applicationProperties["cloudEvents:datacontenttype"]) assert.Equal(t, "type", applicationProperties["cloudEvents:type"]) assert.Equal(t, "subject", applicationProperties["cloudEvents:subject"]) assert.Equal(t, nil, applicationProperties["cloudEvents:traceparent"]) diff --git a/audit/api/api_legacy_converter.go b/internal/audit/api/api_legacy_converter.go similarity index 91% rename from audit/api/api_legacy_converter.go rename to internal/audit/api/api_legacy_converter.go index feed0f8..5711dc2 100644 --- a/audit/api/api_legacy_converter.go +++ b/internal/audit/api/api_legacy_converter.go @@ -8,14 +8,16 @@ import ( "strings" "time" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" "google.golang.org/protobuf/encoding/protojson" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) var ErrUnsupportedSeverity = errors.New("unsupported severity level") -// convertAndSerializeIntoLegacyFormat converts the protobuf events into the json serialized legacy audit log format -func convertAndSerializeIntoLegacyFormat( +// ConvertAndSerializeIntoLegacyFormat converts the protobuf events into the json serialized legacy audit log format +func ConvertAndSerializeIntoLegacyFormat( event *auditV1.AuditLogEntry, routableEvent *auditV1.RoutableAuditEvent, ) ([]byte, error) { @@ -23,14 +25,14 @@ func convertAndSerializeIntoLegacyFormat( // Event type var eventType string switch { - case strings.HasSuffix(event.LogName, string(EventTypeAdminActivity)): + case strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeAdminActivity)): eventType = "ADMIN_ACTIVITY" - case strings.HasSuffix(event.LogName, string(EventTypeSystemEvent)): + case strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeSystemEvent)): eventType = "SYSTEM_EVENT" - case strings.HasSuffix(event.LogName, string(EventTypePolicyDenied)): + case strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypePolicyDenied)): eventType = "POLICY_DENIED" - case strings.HasSuffix(event.LogName, string(EventTypeDataAccess)): - return nil, ErrUnsupportedEventTypeDataAccess + case strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeDataAccess)): + return nil, pkgAuditCommon.ErrUnsupportedEventTypeDataAccess default: return nil, errors.New("unsupported event type") } @@ -117,34 +119,34 @@ func convertAndSerializeIntoLegacyFormat( } if routableEvent.ObjectIdentifier == nil { - return nil, ErrObjectIdentifierNil + return nil, pkgAuditCommon.ErrObjectIdentifierNil } // Context and event type var messageContext *LegacyAuditEventContext switch routableEvent.ObjectIdentifier.Type { - case string(ObjectTypeProject): + case string(pkgAuditCommon.ObjectTypeProject): messageContext = &LegacyAuditEventContext{ OrganizationId: nil, FolderId: nil, ProjectId: &routableEvent.ObjectIdentifier.Identifier, } - case string(ObjectTypeFolder): + case string(pkgAuditCommon.ObjectTypeFolder): messageContext = &LegacyAuditEventContext{ OrganizationId: nil, FolderId: &routableEvent.ObjectIdentifier.Identifier, ProjectId: nil, } - case string(ObjectTypeOrganization): + case string(pkgAuditCommon.ObjectTypeOrganization): messageContext = &LegacyAuditEventContext{ OrganizationId: &routableEvent.ObjectIdentifier.Identifier, FolderId: nil, ProjectId: nil, } - case string(ObjectTypeSystem): + case string(pkgAuditCommon.ObjectTypeSystem): messageContext = nil default: - return nil, ErrUnsupportedObjectIdentifierType + return nil, pkgAuditCommon.ErrUnsupportedObjectIdentifierType } var visibility string diff --git a/audit/api/api_legacy_converter_test.go b/internal/audit/api/api_legacy_converter_test.go similarity index 63% rename from audit/api/api_legacy_converter_test.go rename to internal/audit/api/api_legacy_converter_test.go index 769c006..1235734 100644 --- a/audit/api/api_legacy_converter_test.go +++ b/internal/audit/api/api_legacy_converter_test.go @@ -3,12 +3,14 @@ package api import ( "testing" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" "github.com/stretchr/testify/assert" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) func Test_ConvertAndSerializeIntoLegacyFormat_NoObjectIdentifier(t *testing.T) { - event, _ := newProjectAuditEvent(nil) + event, _ := NewProjectAuditEvent(nil) routableEvent := auditV1.RoutableAuditEvent{ OperationName: event.ProtoPayload.OperationName, Visibility: auditV1.Visibility_VISIBILITY_PUBLIC, @@ -16,6 +18,6 @@ func Test_ConvertAndSerializeIntoLegacyFormat_NoObjectIdentifier(t *testing.T) { Data: nil, } - _, err := convertAndSerializeIntoLegacyFormat(event, &routableEvent) - assert.ErrorIs(t, err, ErrObjectIdentifierNil) + _, err := ConvertAndSerializeIntoLegacyFormat(event, &routableEvent) + assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierNil) } diff --git a/audit/api/model.go b/internal/audit/api/model.go similarity index 80% rename from audit/api/model.go rename to internal/audit/api/model.go index a353c63..774c792 100644 --- a/audit/api/model.go +++ b/internal/audit/api/model.go @@ -2,23 +2,21 @@ package api import ( "context" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" "encoding/json" "errors" "fmt" - "github.com/google/uuid" - "github.com/lestrrat-go/jwx/v2/jwt" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/timestamppb" - "google.golang.org/protobuf/types/known/wrapperspb" - "net" "net/url" - "regexp" "slices" "strings" "time" + + "github.com/lestrrat-go/jwx/v2/jwt" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) const EmailAddressDoNotReplyAtStackItDotCloud = "do-not-reply@stackit.cloud" @@ -31,66 +29,6 @@ var ErrInvalidAuthorizationHeaderValue = errors.New("invalid authorization heade var ErrInvalidBearerToken = errors.New("invalid bearer token") var ErrTokenIsNotBearerToken = errors.New("token is not a bearer token") -var objectTypeIdPattern = regexp.MustCompile(".*/(projects|folders|organizations)/([0-9a-fA-F-]{36})(?:/.*)?") - -type ApiRequest struct { - - // Body - // - // Required: false - Body []byte - - // The (HTTP) request headers / gRPC metadata. - // - // Internal IP-Addresses have to be removed (e.g. in x-forwarded-xxx headers). - // - // Required: true - Header map[string][]string - - // The HTTP request `Host` header value. - // - // Required: true - Host string - - // Method - // - // Required: true - Method string - - // The URL scheme, such as `http`, `https` or `gRPC`. - // - // Required: true - Scheme string - - // The network protocol used with the request, such as "http/1.1", - // "spdy/3", "h2", "h2c", "webrtc", "tcp", "udp", "quic". See - // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids - // for details. - // - // Required: true - Proto string - - // The url - // - // Required: true - URL RequestUrl -} - -type RequestUrl struct { - - // The gRPC / HTTP URL path. - // - // Required: true - Path string - - // The HTTP URL query in the format of "name1=value1&name2=value2", as it - // appears in the first line of the HTTP request. - // The input should be escaped to not contain any special characters. - // - // Required: false - RawQuery *string -} - // AuditRequest bundles request related parameters type AuditRequest struct { @@ -100,7 +38,7 @@ type AuditRequest struct { // It should never include user-generated data, such as file contents. // // Required: true - Request *ApiRequest + Request *pkgAuditCommon.ApiRequest // The IP address of the caller. // For caller from internet, this will be public IPv4 or IPv6 address. @@ -409,24 +347,6 @@ func NewAuditLogEntry( return &event, nil } -// GetCalledServiceNameFromRequest extracts the called service name from subdomain name -func GetCalledServiceNameFromRequest(request *ApiRequest, fallbackName string) string { - if request == nil { - return fallbackName - } - - var calledServiceName = fallbackName - host := request.Host - ip := net.ParseIP(host) - if ip == nil && !strings.Contains(host, "localhost") { - dotIdx := strings.Index(host, ".") - if dotIdx != -1 { - calledServiceName = host[0:dotIdx] - } - } - return calledServiceName -} - // NewPbInt64Value returns protobuf int64 wrapper if value is not nil. func NewPbInt64Value(value *int64) *wrapperspb.Int64Value { if value != nil { @@ -437,7 +357,7 @@ func NewPbInt64Value(value *int64) *wrapperspb.Int64Value { // NewRequestMetadata returns initialized protobuf RequestMetadata object. func NewRequestMetadata( - request *ApiRequest, + request *pkgAuditCommon.ApiRequest, requestHeaders map[string]string, requestId *string, requestScheme string, @@ -469,7 +389,7 @@ func NewRequestMetadata( // NewRequestAttributes returns initialized protobuf AttributeContext_Request object. func NewRequestAttributes( - request *ApiRequest, + request *pkgAuditCommon.ApiRequest, requestHeaders map[string]string, requestId *string, requestScheme string, @@ -488,7 +408,7 @@ func NewRequestAttributes( return &auditV1.AttributeContext_Request{ Id: requestId, - Method: StringToHttpMethod(request.Method), + Method: pkgAuditCommon.StringToHttpMethod(request.Method), Headers: requestHeaders, Path: request.URL.Path, Host: request.Host, @@ -560,7 +480,7 @@ func NewResponseBody(response []byte) (*structpb.Struct, error) { } // NewRequestBody converts the request body into a protobuf struct. -func NewRequestBody(request *ApiRequest) (*structpb.Struct, error) { +func NewRequestBody(request *pkgAuditCommon.ApiRequest) (*structpb.Struct, error) { if len(request.Body) == 0 { return nil, nil @@ -616,8 +536,8 @@ func FilterAndMergeHeaders(headers map[string][]string) map[string]string { // NewAuditRoutingIdentifier instantiates a new auditApi.RoutableIdentifier for // the given object ID and object type. -func NewAuditRoutingIdentifier(objectId string, objectType ObjectType) *RoutableIdentifier { - return &RoutableIdentifier{ +func NewAuditRoutingIdentifier(objectId string, objectType pkgAuditCommon.ObjectType) *pkgAuditCommon.RoutableIdentifier { + return &pkgAuditCommon.RoutableIdentifier{ Identifier: objectId, Type: objectType, } @@ -628,7 +548,7 @@ func NewAuditRoutingIdentifier(objectId string, objectType ObjectType) *Routable // - authenticationPrincipal - principal identifier // - audiences - list of audience claims // - authenticationInfo - information about the user or service-account authentication -func AuditAttributesFromAuthorizationHeader(request *ApiRequest) ( +func AuditAttributesFromAuthorizationHeader(request *pkgAuditCommon.ApiRequest) ( *structpb.Struct, string, []string, @@ -832,133 +752,3 @@ func extractSubjectAndEmail(token jwt.Token) (string, string) { } return principalId, principalEmail } - -// OperationNameFromUrlPath converts the request url path into an operation name. -// UUIDs and query parameters are filtered out, slashes replaced by dots. -// HTTP methods are added as suffix as follows: -// - POST - create -// - PUT - update -// - PATCH - update -// - DELETE - delete -// - others - read -func OperationNameFromUrlPath(path, requestMethod string) string { - queryIdx := strings.Index(path, "?") - if queryIdx != -1 { - path = path[:queryIdx] - } - path = strings.TrimPrefix(path, "/") - path = strings.TrimSuffix(path, "/") - split := strings.Split(path, "/") - - operation := "" - for _, part := range split { - // skip uuids in path - _, err := uuid.Parse(part) - if err == nil { - continue - } - operation = fmt.Sprintf("%s/%s", operation, part) - } - - operation = strings.ReplaceAll(operation, "/", ".") - operation = strings.TrimPrefix(operation, ".") - operation = strings.ToLower(operation) - if operation != "" { - method := StringToHttpMethod(requestMethod) - var action string - switch method { - case auditV1.AttributeContext_HTTP_METHOD_PUT, - auditV1.AttributeContext_HTTP_METHOD_PATCH: - action = "update" - case auditV1.AttributeContext_HTTP_METHOD_POST: - action = "create" - case auditV1.AttributeContext_HTTP_METHOD_DELETE: - action = "delete" - default: - action = "read" - } - operation = fmt.Sprintf("%s.%s", operation, action) - } - - return operation -} - -// OperationNameFromGrpcMethod converts the grpc path into an operation name. -func OperationNameFromGrpcMethod(path string) string { - operation := strings.TrimPrefix(path, "/") - operation = strings.TrimSuffix(operation, "/") - - operation = strings.ReplaceAll(operation, "/", ".") - operation = strings.TrimPrefix(operation, ".") - operation = strings.ToLower(operation) - - return operation -} - -func GetObjectIdAndTypeFromUrlPath(path string) ( - string, - *ObjectType, - error, -) { - - // Extract object id and type from request url - objectTypeIdMatches := objectTypeIdPattern.FindStringSubmatch(path) - if len(objectTypeIdMatches) > 0 { - objectType := ObjectTypeFromPluralString(objectTypeIdMatches[1]) - err := objectType.IsSupportedType() - if err != nil { - return "", nil, err - } - - objectId := objectTypeIdMatches[2] - - return objectId, &objectType, nil - } - - return "", nil, nil -} - -func ToArrayMap(input map[string]string) map[string][]string { - output := map[string][]string{} - for key, value := range input { - output[key] = []string{value} - } - return output -} - -func StringAttributeFromMetadata(metadata map[string][]string, name string) string { - var value = "" - rawValue, hasAttribute := metadata[name] - if hasAttribute && len(rawValue) > 0 { - value = rawValue[0] - } - return value -} - -// ResponseBodyToBytes converts a JSON or Protobuf response into a byte array -func ResponseBodyToBytes(response any) ([]byte, error) { - if response == nil { - return nil, nil - } - - responseBytes, isBytes := response.([]byte) - if isBytes { - return responseBytes, nil - } - - responseProtoMessage, isProtoMessage := response.(proto.Message) - if isProtoMessage { - responseJson, err := protojson.Marshal(responseProtoMessage) - if err != nil { - return nil, err - } - return responseJson, nil - } - - responseJson, err := json.Marshal(response) - if err != nil { - return nil, err - } - return responseJson, nil - -} diff --git a/audit/api/model_test.go b/internal/audit/api/model_test.go similarity index 76% rename from audit/api/model_test.go rename to internal/audit/api/model_test.go index 8cf5cc8..61ef761 100644 --- a/audit/api/model_test.go +++ b/internal/audit/api/model_test.go @@ -1,61 +1,23 @@ package api import ( - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" "encoding/json" "fmt" - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/types/known/structpb" - "google.golang.org/protobuf/types/known/timestamppb" - "google.golang.org/protobuf/types/known/wrapperspb" "net/http" "net/url" "testing" "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) -func Test_GetCalledServiceNameFromRequest(t *testing.T) { - - t.Run("request is nil", func(t *testing.T) { - serviceName := GetCalledServiceNameFromRequest(nil, "resource-manager") - assert.Equal(t, "resource-manager", serviceName) - }) - - t.Run("localhost", func(t *testing.T) { - request := ApiRequest{Host: "localhost:8080"} - serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") - assert.Equal(t, "resource-manager", serviceName) - }) - - t.Run("cf", func(t *testing.T) { - request := ApiRequest{Host: "stackit-resource-manager-go-dev.apps.01.cf.eu01.stackit.cloud"} - serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") - assert.Equal(t, "stackit-resource-manager-go-dev", serviceName) - }) - - t.Run("cf invalid host", func(t *testing.T) { - request := ApiRequest{Host: ""} - serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") - assert.Equal(t, "resource-manager", serviceName) - }) - - t.Run("ip", func(t *testing.T) { - request := ApiRequest{Host: "127.0.0.1"} - serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") - assert.Equal(t, "resource-manager", serviceName) - }, - ) - - t.Run("ip short", func(t *testing.T) { - request := ApiRequest{Host: "::1"} - serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") - assert.Equal(t, "resource-manager", serviceName) - }, - ) -} - func Test_NewPbInt64Value(t *testing.T) { t.Run("nil", func(t *testing.T) { @@ -123,9 +85,9 @@ func Test_NewRequestMetadata(t *testing.T) { requestHeaders["Custom"] = []string{"customHeader"} queryString := "topic=project" - request := ApiRequest{ + request := pkgAuditCommon.ApiRequest{ Method: "GET", - URL: RequestUrl{Path: "/audit/new", RawQuery: &queryString}, + URL: pkgAuditCommon.RequestUrl{Path: "/audit/new", RawQuery: &queryString}, Host: "localhost:8080", Proto: "HTTP/1.1", Scheme: "http", @@ -187,9 +149,9 @@ func Test_NewRequestMetadata(t *testing.T) { }) t.Run("without query parameters", func(t *testing.T) { - request := ApiRequest{ + request := pkgAuditCommon.ApiRequest{ Method: "GET", - URL: RequestUrl{Path: "/audit/new"}, + URL: pkgAuditCommon.RequestUrl{Path: "/audit/new"}, Host: "localhost:8080", Proto: "HTTP/1.1", Header: requestHeaders, @@ -213,9 +175,9 @@ func Test_NewRequestMetadata(t *testing.T) { t.Run("with empty query parameters", func(t *testing.T) { emptyQuery := "" - request := ApiRequest{ + request := pkgAuditCommon.ApiRequest{ Method: "GET", - URL: RequestUrl{Path: "/audit/new", RawQuery: &emptyQuery}, + URL: pkgAuditCommon.RequestUrl{Path: "/audit/new", RawQuery: &emptyQuery}, Host: "localhost:8080", Proto: "HTTP/1.1", Header: requestHeaders, @@ -238,9 +200,9 @@ func Test_NewRequestMetadata(t *testing.T) { }) t.Run("without request id", func(t *testing.T) { - request := ApiRequest{ + request := pkgAuditCommon.ApiRequest{ Method: "GET", - URL: RequestUrl{Path: "/audit/new", RawQuery: &queryString}, + URL: pkgAuditCommon.RequestUrl{Path: "/audit/new", RawQuery: &queryString}, Host: "localhost:8080", Proto: "HTTP/1.1", Header: requestHeaders, @@ -262,9 +224,9 @@ func Test_NewRequestMetadata(t *testing.T) { t.Run("various default http methods", func(t *testing.T) { httpMethods := []string{"GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"} for _, httpMethod := range httpMethods { - request := ApiRequest{ + request := pkgAuditCommon.ApiRequest{ Method: httpMethod, - URL: RequestUrl{Path: "/audit/new", RawQuery: &queryString}, + URL: pkgAuditCommon.RequestUrl{Path: "/audit/new", RawQuery: &queryString}, Host: "localhost:8080", Proto: "HTTP/1.1", Header: requestHeaders, @@ -286,9 +248,9 @@ func Test_NewRequestMetadata(t *testing.T) { }) t.Run("unknown http method", func(t *testing.T) { - request := ApiRequest{ + request := pkgAuditCommon.ApiRequest{ Method: "", - URL: RequestUrl{Path: "/audit/new", RawQuery: &queryString}, + URL: pkgAuditCommon.RequestUrl{Path: "/audit/new", RawQuery: &queryString}, Host: "localhost:8080", Proto: "HTTP/1.1", Header: requestHeaders, @@ -377,7 +339,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { headerValue := "Basic username:password" headers := make(map[string][]string) headers["Authorization"] = []string{headerValue} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} _, _, _, _, err := AuditAttributesFromAuthorizationHeader(&request) assert.ErrorIs(t, err, ErrTokenIsNotBearerToken) @@ -387,7 +349,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { headerValue := "a b c" headers := make(map[string][]string) headers["Authorization"] = []string{headerValue} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} _, _, _, _, err := AuditAttributesFromAuthorizationHeader(&request) assert.ErrorIs(t, err, ErrInvalidAuthorizationHeaderValue) @@ -397,7 +359,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { headerValue := "Bearer a.b.c.d" headers := make(map[string][]string) headers["Authorization"] = []string{headerValue} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} _, _, _, _, err := AuditAttributesFromAuthorizationHeader(&request) assert.ErrorIs(t, err, ErrInvalidBearerToken) @@ -407,7 +369,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { headerValue := "Bearer a.b.c" headers := make(map[string][]string) headers["Authorization"] = []string{headerValue} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} _, _, _, _, err := AuditAttributesFromAuthorizationHeader(&request) assert.ErrorIs(t, err, ErrInvalidBearerToken) @@ -416,7 +378,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { t.Run("client credentials token", func(t *testing.T) { headers := make(map[string][]string) headers["Authorization"] = []string{clientCredentialsToken} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) @@ -451,7 +413,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { t.Run("service account access token", func(t *testing.T) { headers := make(map[string][]string) headers["Authorization"] = []string{serviceAccountToken} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) @@ -491,7 +453,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { t.Run("impersonated token of access token", func(t *testing.T) { headers := make(map[string][]string) headers["Authorization"] = []string{serviceAccountTokenImpersonated} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) @@ -539,7 +501,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { t.Run("impersonated token of impersonated access token", func(t *testing.T) { headers := make(map[string][]string) headers["Authorization"] = []string{serviceAccountTokenRepeatedlyImpersonated} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) @@ -592,7 +554,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { t.Run("user token", func(t *testing.T) { headers := make(map[string][]string) headers["Authorization"] = []string{userToken} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) @@ -629,7 +591,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { t.Run("user token with simple aud claim", func(t *testing.T) { headers := make(map[string][]string) headers["Authorization"] = []string{userTokenWithSimpleAudience} - request := ApiRequest{Header: headers} + request := pkgAuditCommon.ApiRequest{Header: headers} auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) @@ -671,9 +633,9 @@ func Test_NewAuditLogEntry(t *testing.T) { requestHeaders["User-Agent"] = []string{userAgent} requestHeaders["Custom"] = []string{"customHeader"} - request := ApiRequest{ + request := pkgAuditCommon.ApiRequest{ Method: "GET", - URL: RequestUrl{Path: "/audit/new"}, + URL: pkgAuditCommon.RequestUrl{Path: "/audit/new"}, Host: "localhost:8080", Proto: "HTTP/1.1", Scheme: "http", @@ -700,7 +662,7 @@ func Test_NewAuditLogEntry(t *testing.T) { } objectId := uuid.NewString() - logName := fmt.Sprintf("projects/%s/logs/%s", objectId, EventTypeAdminActivity) + logName := fmt.Sprintf("projects/%s/logs/%s", objectId, pkgAuditCommon.EventTypeAdminActivity) serviceName := "resource-manager" operationName := fmt.Sprintf("stackit.%s.v2.projects.updated", serviceName) resourceName := fmt.Sprintf("projects/%s", objectId) @@ -780,9 +742,9 @@ func Test_NewAuditLogEntry(t *testing.T) { requestBody["key"] = "request" requestBodyBytes, _ := json.Marshal(requestBody) query := "topic=project" - request := ApiRequest{ + request := pkgAuditCommon.ApiRequest{ Method: "GET", - URL: RequestUrl{Path: "/audit/new", RawQuery: &query}, + URL: pkgAuditCommon.RequestUrl{Path: "/audit/new", RawQuery: &query}, Host: "localhost:8080", Proto: "HTTP/1.1", Scheme: "http", @@ -824,7 +786,7 @@ func Test_NewAuditLogEntry(t *testing.T) { auditTime := time.Now().UTC() objectId := uuid.NewString() - logName := fmt.Sprintf("projects/%s/logs/%s", objectId, EventTypeAdminActivity) + logName := fmt.Sprintf("projects/%s/logs/%s", objectId, pkgAuditCommon.EventTypeAdminActivity) serviceName := "resource-manager" operationName := fmt.Sprintf("stackit.%s.v2.projects.updated", serviceName) resourceName := fmt.Sprintf("projects/%s", objectId) @@ -921,231 +883,9 @@ func Test_NewInsertId(t *testing.T) { func Test_NewNewAuditRoutingIdentifier(t *testing.T) { objectId := uuid.NewString() - objectType := ObjectTypeProject + objectType := pkgAuditCommon.ObjectTypeProject routingIdentifier := NewAuditRoutingIdentifier(objectId, objectType) assert.Equal(t, objectId, routingIdentifier.Identifier) assert.Equal(t, objectType, routingIdentifier.Type) } - -func Test_OperationNameFromUrlPath(t *testing.T) { - - t.Run("empty path", func(t *testing.T) { - operationName := OperationNameFromUrlPath("", "GET") - assert.Equal(t, "", operationName) - }) - - t.Run("root path", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/", "GET") - assert.Equal(t, "", operationName) - }) - - t.Run("path without version", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects", "GET") - assert.Equal(t, "projects.read", operationName) - }) - - t.Run("path with uuid without version", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects/ac51bbd2-cb23-441b-a2ee-5393189695aa", "GET") - assert.Equal(t, "projects.read", operationName) - }) - - t.Run("path with uuid and version", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/v2/projects/ac51bbd2-cb23-441b-a2ee-5393189695aa", "GET") - assert.Equal(t, "v2.projects.read", operationName) - }) - - t.Run("concatenated path", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/v2/organizations/ac51bbd2-cb23-441b-a2ee-5393189695aa/folders/167fc176-9d8e-477b-a56c-b50d7b26adcf/projects/0a2a4f9b-4e67-4562-ad02-c2d200e05aa6/audit/policy", "GET") - assert.Equal(t, "v2.organizations.folders.projects.audit.policy.read", operationName) - }) - - t.Run("path with query params", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/v2/organizations/ac51bbd2-cb23-441b-a2ee-5393189695aa/audit/policy?since=2024-08-27", "GET") - assert.Equal(t, "v2.organizations.audit.policy.read", operationName) - }) - - t.Run("path trailing slash", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects/ac51bbd2-cb23-441b-a2ee-5393189695aa/", "GET") - assert.Equal(t, "projects.read", operationName) - }) - - t.Run("path trailing slash and query params", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects/ac51bbd2-cb23-441b-a2ee-5393189695aa/?changeDate=2024-10-13", "GET") - assert.Equal(t, "projects.read", operationName) - }) - - t.Run("http method post", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects", "POST") - assert.Equal(t, "projects.create", operationName) - }) - - t.Run("http method put", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects", "PUT") - assert.Equal(t, "projects.update", operationName) - }) - - t.Run("http method patch", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects", "PATCH") - assert.Equal(t, "projects.update", operationName) - }) - - t.Run("http method delete", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects", "DELETE") - assert.Equal(t, "projects.delete", operationName) - }) - - t.Run("operation name fallback on options", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects", "OPTIONS") - assert.Equal(t, "projects.read", operationName) - }) - - t.Run("operation name fallback on unknown", func(t *testing.T) { - operationName := OperationNameFromUrlPath("/projects", "UNKNOWN") - assert.Equal(t, "projects.read", operationName) - }) -} - -func Test_OperationNameFromGrpcMethod(t *testing.T) { - - t.Run("empty path", func(t *testing.T) { - operationName := OperationNameFromGrpcMethod("") - assert.Equal(t, "", operationName) - }) - - t.Run("root path", func(t *testing.T) { - operationName := OperationNameFromGrpcMethod("/") - assert.Equal(t, "", operationName) - }) - - t.Run("path without version", func(t *testing.T) { - operationName := OperationNameFromGrpcMethod("/example.ExampleService/ManualAuditEvent") - assert.Equal(t, "example.exampleservice.manualauditevent", operationName) - }) - - t.Run("path with version", func(t *testing.T) { - operationName := OperationNameFromGrpcMethod("/example.v1.ExampleService/ManualAuditEvent") - assert.Equal(t, "example.v1.exampleservice.manualauditevent", operationName) - }) - - t.Run("path trailing slash", func(t *testing.T) { - operationName := OperationNameFromGrpcMethod("/example.v1.ExampleService/ManualAuditEvent/") - assert.Equal(t, "example.v1.exampleservice.manualauditevent", operationName) - }) -} - -func Test_GetObjectIdAndTypeFromUrlPath(t *testing.T) { - - t.Run("object id and type not in url", func(t *testing.T) { - objectId, objectType, err := GetObjectIdAndTypeFromUrlPath("/v2/projects/audit") - assert.NoError(t, err) - assert.Equal(t, "", objectId) - assert.Nil(t, objectType) - }) - - t.Run("object id and type in url", func(t *testing.T) { - objectId, objectType, err := GetObjectIdAndTypeFromUrlPath("/v2/projects/f17d4064-9b65-4334-b6a7-8fed96340124") - assert.NoError(t, err) - assert.Equal(t, "f17d4064-9b65-4334-b6a7-8fed96340124", objectId) - assert.Equal(t, ObjectTypeProject, *objectType) - }) - - t.Run("multiple object ids and types in url", func(t *testing.T) { - objectId, objectType, err := GetObjectIdAndTypeFromUrlPath("/v2/organization/8ee58bec-d496-4bb9-af8d-72fda4d78b6b/projects/f17d4064-9b65-4334-b6a7-8fed96340124") - assert.NoError(t, err) - assert.Equal(t, "f17d4064-9b65-4334-b6a7-8fed96340124", objectId) - assert.Equal(t, ObjectTypeProject, *objectType) - }) -} - -func Test_ToArrayMap(t *testing.T) { - - t.Run("empty map", func(t *testing.T) { - result := ToArrayMap(map[string]string{}) - assert.Equal(t, map[string][]string{}, result) - }) - - t.Run("empty map", func(t *testing.T) { - result := ToArrayMap(map[string]string{"key1": "value1", "key2": "value2"}) - assert.Equal(t, map[string][]string{ - "key1": {"value1"}, - "key2": {"value2"}, - }, result) - }) -} - -func Test_StringAttributeFromMetadata(t *testing.T) { - - metadata := map[string][]string{"key1": {"value1"}, "key2": {"value2"}} - - t.Run("not found", func(t *testing.T) { - attribute := StringAttributeFromMetadata(metadata, "key3") - assert.Equal(t, "", attribute) - }) - - t.Run("found", func(t *testing.T) { - attribute := StringAttributeFromMetadata(metadata, "key2") - assert.Equal(t, "value2", attribute) - }) -} - -func Test_ResponseBodyToBytes(t *testing.T) { - - t.Run( - "nil response body", func(t *testing.T) { - bytes, err := ResponseBodyToBytes(nil) - assert.Nil(t, bytes) - assert.Nil(t, err) - }, - ) - - t.Run( - "bytes", func(t *testing.T) { - responseBody := []byte("data") - bytes, err := ResponseBodyToBytes(responseBody) - assert.Nil(t, err) - assert.Equal(t, responseBody, bytes) - }, - ) - - t.Run( - "Protobuf message", func(t *testing.T) { - protobufMessage := auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(ObjectTypeProject)} - bytes, err := ResponseBodyToBytes(&protobufMessage) - assert.Nil(t, err) - - expected, err := protojson.Marshal(&protobufMessage) - assert.Nil(t, err) - assert.Equal(t, expected, bytes) - }, - ) - - t.Run( - "struct", func(t *testing.T) { - type CustomObject struct { - Value string - } - - responseBody := CustomObject{Value: "data"} - bytes, err := ResponseBodyToBytes(responseBody) - assert.Nil(t, err) - - expected, err := json.Marshal(responseBody) - assert.Nil(t, err) - assert.Equal(t, expected, bytes) - }, - ) - - t.Run( - "map", func(t *testing.T) { - - responseBody := map[string]interface{}{"value": "data"} - bytes, err := ResponseBodyToBytes(responseBody) - assert.Nil(t, err) - - expected, err := json.Marshal(responseBody) - assert.Nil(t, err) - assert.Equal(t, expected, bytes) - }, - ) -} diff --git a/audit/api/schema_validation_test.go b/internal/audit/api/schema_validation_test.go similarity index 95% rename from audit/api/schema_validation_test.go rename to internal/audit/api/schema_validation_test.go index c0b141e..24952be 100644 --- a/audit/api/schema_validation_test.go +++ b/internal/audit/api/schema_validation_test.go @@ -1,10 +1,13 @@ package api import ( - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - "github.com/bufbuild/protovalidate-go" - "github.com/stretchr/testify/assert" "testing" + + "buf.build/go/protovalidate" + "github.com/stretchr/testify/assert" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) func Test_RoutableAuditEvent(t *testing.T) { @@ -18,7 +21,7 @@ func Test_RoutableAuditEvent(t *testing.T) { Visibility: auditV1.Visibility_VISIBILITY_PUBLIC, ObjectIdentifier: &auditV1.ObjectIdentifier{ Identifier: "14f7aa86-77ba-4d77-a091-a2cf3395a221", - Type: string(ObjectTypeProject), + Type: string(pkgAuditCommon.ObjectTypeProject), }, Data: &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &auditV1.UnencryptedData{ Data: []byte("data"), diff --git a/audit/api/test_data.go b/internal/audit/api/test_data.go similarity index 91% rename from audit/api/test_data.go rename to internal/audit/api/test_data.go index dc66214..fa0fc6d 100644 --- a/audit/api/test_data.go +++ b/internal/audit/api/test_data.go @@ -4,13 +4,13 @@ import ( "fmt" "time" - "google.golang.org/protobuf/types/known/wrapperspb" - - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - "github.com/google/uuid" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) const clientCredentialsToken = "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1yZXNvdXJjZS1tYW5hZ2VyLWRldiJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXJlc291cmNlLW1hbmFnZXItZGV2IiwiZXhwIjoxNzI0NDA1MzI2LCJpYXQiOjE3MjQ0MDQ0MjYsImlzcyI6Imh0dHBzOi8vYWNjb3VudHMuZGV2LnN0YWNraXQuY2xvdWQiLCJqdGkiOiJlNDZlYmEzOC1kZWRiLTQ1NDEtOTRmMy00OWY5N2E5MzRkNTgiLCJuYmYiOjE3MjQ0MDQ0MjYsInNjb3BlIjoidWFhLm5vbmUiLCJzdWIiOiJzdGFja2l0LXJlc291cmNlLW1hbmFnZXItZGV2In0.JP5Uy7AMdK4ukzQ6aOYzbVwEmq0Tp2ppQGRqGOhuVQgbqs6yJ33GKXo7RPsJVLw3FR7XAxENIVqNvzGotbDXr0NjBGdzyxIHzrOaUqM4w1iLzD1KF51dXFwkoigqDdD7Ze9eI_Uo3tSn8FwGLTSoO-ONQYpnceCiGut2Gc6VIL8HOLdh8dzlRENGQtgYd-3Y5zqpoLrsR2Bd-0sv15sF-5aI0CqcC8gE70JPImKf2u_IYI-TYMDNk86YSCtaYO5-alOrHXXWwgzSoH-r2s5qoOhPbei9myV_P4fdcKXxMqfap9hImXPUooVhpdUr1AabZw3MtW7rION8tJAiauhMQA" @@ -22,7 +22,7 @@ const userTokenWithSimpleAudience = "Bearer eyJhbGciOiJSUzUxMiIsImtpZCI6InNlcnZp var TestHeaders = map[string][]string{"user-agent": {"custom"}, "authorization": {userToken}} -func newOrganizationAuditEvent( +func NewOrganizationAuditEvent( customization *func( *auditV1.AuditLogEntry, *auditV1.ObjectIdentifier, @@ -42,11 +42,11 @@ func newOrganizationAuditEvent( labels := make(map[string]string) labels["label1"] = "value1" auditEvent := &auditV1.AuditLogEntry{ - LogName: fmt.Sprintf("%s/%s/logs/%s", ObjectTypeOrganization.Plural(), identifier, EventTypeAdminActivity), + LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.ObjectTypeOrganization.Plural(), identifier, pkgAuditCommon.EventTypeAdminActivity), ProtoPayload: &auditV1.AuditLog{ ServiceName: "resource-manager", OperationName: "stackit.resourcemanager.v2.organization.created", - ResourceName: fmt.Sprintf("%s/%s", ObjectTypeOrganization.Plural(), identifier), + ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeOrganization.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: uuid.NewString(), PrincipalEmail: "user@example.com", @@ -54,7 +54,7 @@ func newOrganizationAuditEvent( ServiceAccountDelegationInfo: nil, }, AuthorizationInfo: []*auditV1.AuthorizationInfo{{ - Resource: fmt.Sprintf("%s/%s", ObjectTypeOrganization.Plural(), identifier), + Resource: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeOrganization.Plural(), identifier), Permission: &permission, Granted: &permissionGranted, }}, @@ -102,7 +102,7 @@ func newOrganizationAuditEvent( objectIdentifier := &auditV1.ObjectIdentifier{ Identifier: identifier.String(), - Type: string(ObjectTypeOrganization), + Type: string(pkgAuditCommon.ObjectTypeOrganization), } if customization != nil { @@ -112,7 +112,7 @@ func newOrganizationAuditEvent( return auditEvent, objectIdentifier } -func newFolderAuditEvent( +func NewFolderAuditEvent( customization *func( *auditV1.AuditLogEntry, *auditV1.ObjectIdentifier, @@ -132,11 +132,11 @@ func newFolderAuditEvent( labels := make(map[string]string) labels["label1"] = "value1" auditEvent := &auditV1.AuditLogEntry{ - LogName: fmt.Sprintf("%s/%s/logs/%s", ObjectTypeFolder.Plural(), identifier, EventTypeAdminActivity), + LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.ObjectTypeFolder.Plural(), identifier, pkgAuditCommon.EventTypeAdminActivity), ProtoPayload: &auditV1.AuditLog{ ServiceName: "resource-manager", OperationName: "stackit.resourcemanager.v2.folder.created", - ResourceName: fmt.Sprintf("%s/%s", ObjectTypeFolder.Plural(), identifier), + ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeFolder.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: uuid.NewString(), PrincipalEmail: "user@example.com", @@ -144,7 +144,7 @@ func newFolderAuditEvent( ServiceAccountDelegationInfo: nil, }, AuthorizationInfo: []*auditV1.AuthorizationInfo{{ - Resource: fmt.Sprintf("%s/%s", ObjectTypeFolder.Plural(), identifier), + Resource: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeFolder.Plural(), identifier), Permission: &permission, Granted: &permissionGranted, }}, @@ -192,7 +192,7 @@ func newFolderAuditEvent( objectIdentifier := &auditV1.ObjectIdentifier{ Identifier: identifier.String(), - Type: string(ObjectTypeFolder), + Type: string(pkgAuditCommon.ObjectTypeFolder), } if customization != nil { @@ -202,7 +202,7 @@ func newFolderAuditEvent( return auditEvent, objectIdentifier } -func newProjectAuditEvent( +func NewProjectAuditEvent( customization *func( *auditV1.AuditLogEntry, *auditV1.ObjectIdentifier, @@ -222,11 +222,11 @@ func newProjectAuditEvent( labels := make(map[string]string) labels["label1"] = "value1" auditEvent := &auditV1.AuditLogEntry{ - LogName: fmt.Sprintf("%s/%s/logs/%s", ObjectTypeProject.Plural(), identifier, EventTypeAdminActivity), + LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.ObjectTypeProject.Plural(), identifier, pkgAuditCommon.EventTypeAdminActivity), ProtoPayload: &auditV1.AuditLog{ ServiceName: "resource-manager", OperationName: "stackit.resourcemanager.v2.project.created", - ResourceName: fmt.Sprintf("%s/%s", ObjectTypeProject.Plural(), identifier), + ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeProject.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: uuid.NewString(), PrincipalEmail: "user@example.com", @@ -234,7 +234,7 @@ func newProjectAuditEvent( ServiceAccountDelegationInfo: nil, }, AuthorizationInfo: []*auditV1.AuthorizationInfo{{ - Resource: fmt.Sprintf("%s/%s", ObjectTypeProject.Plural(), identifier), + Resource: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeProject.Plural(), identifier), Permission: &permission, Granted: &permissionGranted, }}, @@ -282,7 +282,7 @@ func newProjectAuditEvent( objectIdentifier := &auditV1.ObjectIdentifier{ Identifier: identifier.String(), - Type: string(ObjectTypeProject), + Type: string(pkgAuditCommon.ObjectTypeProject), } if customization != nil { @@ -292,7 +292,7 @@ func newProjectAuditEvent( return auditEvent, objectIdentifier } -func newProjectSystemAuditEvent( +func NewProjectSystemAuditEvent( customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry { identifier := uuid.New() @@ -307,11 +307,11 @@ func newProjectSystemAuditEvent( serviceAccountName := fmt.Sprintf("projects/%s/service-accounts/%s", identifier, serviceAccountId) delegationPrincipal := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_SystemPrincipal_{}} auditEvent := &auditV1.AuditLogEntry{ - LogName: fmt.Sprintf("%s/%s/logs/%s", SystemIdentifier.Type, SystemIdentifier.Identifier, EventTypeSystemEvent), + LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.SystemIdentifier.Type, pkgAuditCommon.SystemIdentifier.Identifier, pkgAuditCommon.EventTypeSystemEvent), ProtoPayload: &auditV1.AuditLog{ ServiceName: "resource-manager", OperationName: "stackit.resourcemanager.v2.system.changed", - ResourceName: fmt.Sprintf("%s/%s", ObjectTypeProject.Plural(), identifier), + ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeProject.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: serviceAccountId, PrincipalEmail: "service-account@sa.stackit.cloud", @@ -319,7 +319,7 @@ func newProjectSystemAuditEvent( ServiceAccountDelegationInfo: []*auditV1.ServiceAccountDelegationInfo{&delegationPrincipal}, }, AuthorizationInfo: []*auditV1.AuthorizationInfo{{ - Resource: fmt.Sprintf("%s/%s", ObjectTypeProject.Plural(), identifier), + Resource: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeProject.Plural(), identifier), Permission: nil, Granted: nil, }}, @@ -372,7 +372,7 @@ func newProjectSystemAuditEvent( return auditEvent } -func newSystemAuditEvent( +func NewSystemAuditEvent( customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry { identifier := uuid.Nil @@ -387,11 +387,11 @@ func newSystemAuditEvent( serviceAccountName := fmt.Sprintf("projects/%s/service-accounts/%s", identifier, serviceAccountId) delegationPrincipal := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_SystemPrincipal_{}} auditEvent := &auditV1.AuditLogEntry{ - LogName: fmt.Sprintf("%s/%s/logs/%s", ObjectTypeSystem.Plural(), identifier, EventTypeSystemEvent), + LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.ObjectTypeSystem.Plural(), identifier, pkgAuditCommon.EventTypeSystemEvent), ProtoPayload: &auditV1.AuditLog{ ServiceName: "resource-manager", OperationName: "stackit.resourcemanager.v2.system.changed", - ResourceName: fmt.Sprintf("%s/%s", ObjectTypeSystem.Plural(), identifier), + ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeSystem.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: serviceAccountId, PrincipalEmail: "service-account@sa.stackit.cloud", @@ -399,7 +399,7 @@ func newSystemAuditEvent( ServiceAccountDelegationInfo: []*auditV1.ServiceAccountDelegationInfo{&delegationPrincipal}, }, AuthorizationInfo: []*auditV1.AuthorizationInfo{{ - Resource: fmt.Sprintf("%s/%s", ObjectTypeSystem.Plural(), identifier), + Resource: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeSystem.Plural(), identifier), Permission: nil, Granted: nil, }}, diff --git a/audit/api/trace.go b/internal/audit/api/trace.go similarity index 99% rename from audit/api/trace.go rename to internal/audit/api/trace.go index 06a35a8..5e5cf3f 100644 --- a/audit/api/trace.go +++ b/internal/audit/api/trace.go @@ -2,6 +2,7 @@ package api import ( "context" + "go.opentelemetry.io/otel/propagation" ) diff --git a/audit/api/trace_test.go b/internal/audit/api/trace_test.go similarity index 99% rename from audit/api/trace_test.go rename to internal/audit/api/trace_test.go index e4dbcc6..67f5d0b 100644 --- a/audit/api/trace_test.go +++ b/internal/audit/api/trace_test.go @@ -2,10 +2,11 @@ package api import ( "context" + "testing" + "github.com/stretchr/testify/assert" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" - "testing" ) func Test_AddTraceParentAndStateToContext(t *testing.T) { diff --git a/audit/messaging/amqp_connection.go b/internal/messaging/amqp_connection.go similarity index 65% rename from audit/messaging/amqp_connection.go rename to internal/messaging/amqp_connection.go index c39f725..d697710 100644 --- a/audit/messaging/amqp_connection.go +++ b/internal/messaging/amqp_connection.go @@ -4,43 +4,48 @@ import ( "context" "errors" "fmt" - "github.com/Azure/go-amqp" "log/slog" "sync" "time" + + "github.com/Azure/go-amqp" + + pkgCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" ) +const connectionTimeoutSeconds = 10 + var ErrConnectionClosed = errors.New("amqp connection is closed") type AmqpConnection struct { - connectionName string - lock sync.RWMutex - brokerUrl string - username string - password string - conn amqpConn - dialer amqpDial + ConnectionName string + Lock sync.RWMutex + BrokerUrl string + Username string + Password string + Conn AmqpConn + Dialer amqpDial } -// amqpConn is an abstraction of amqp.Conn -type amqpConn interface { - NewSession(ctx context.Context, opts *amqp.SessionOptions) (amqpSession, error) +// AmqpConn is an abstraction of amqp.Conn +type AmqpConn interface { + NewSession(ctx context.Context, opts *amqp.SessionOptions) (AmqpSession, error) Close() error Done() <-chan struct{} } type defaultAmqpConn struct { - conn *amqp.Conn + Conn *amqp.Conn } func newDefaultAmqpConn(conn *amqp.Conn) *defaultAmqpConn { return &defaultAmqpConn{ - conn: conn, + Conn: conn, } } -func (d defaultAmqpConn) NewSession(ctx context.Context, opts *amqp.SessionOptions) (amqpSession, error) { - session, err := d.conn.NewSession(ctx, opts) +func (d defaultAmqpConn) NewSession(ctx context.Context, opts *amqp.SessionOptions) (AmqpSession, error) { + session, err := d.Conn.NewSession(ctx, opts) if err != nil { return nil, err } @@ -48,47 +53,47 @@ func (d defaultAmqpConn) NewSession(ctx context.Context, opts *amqp.SessionOptio } func (d defaultAmqpConn) Close() error { - return d.conn.Close() + return d.Conn.Close() } func (d defaultAmqpConn) Done() <-chan struct{} { - return d.conn.Done() + return d.Conn.Done() } -var _ amqpConn = (*defaultAmqpConn)(nil) +var _ AmqpConn = (*defaultAmqpConn)(nil) type amqpDial interface { - Dial(ctx context.Context, addr string, opts *amqp.ConnOptions) (amqpConn, error) + Dial(ctx context.Context, addr string, opts *amqp.ConnOptions) (AmqpConn, error) } -type amqpSession interface { - NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (amqpSender, error) +type AmqpSession interface { + NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (AmqpSender, error) Close(ctx context.Context) error } type defaultAmqpSession struct { - session *amqp.Session + Session *amqp.Session } func newDefaultAmqpSession(session *amqp.Session) *defaultAmqpSession { return &defaultAmqpSession{ - session: session, + Session: session, } } -func (s *defaultAmqpSession) NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (amqpSender, error) { - return s.session.NewSender(ctx, target, opts) +func (s *defaultAmqpSession) NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (AmqpSender, error) { + return s.Session.NewSender(ctx, target, opts) } func (s *defaultAmqpSession) Close(ctx context.Context) error { - return s.session.Close(ctx) + return s.Session.Close(ctx) } -var _ amqpSession = (*defaultAmqpSession)(nil) +var _ AmqpSession = (*defaultAmqpSession)(nil) type defaultAmqpDialer struct{} -func (d *defaultAmqpDialer) Dial(ctx context.Context, addr string, opts *amqp.ConnOptions) (amqpConn, error) { +func (d *defaultAmqpDialer) Dial(ctx context.Context, addr string, opts *amqp.ConnOptions) (AmqpConn, error) { dial, err := amqp.Dial(ctx, addr, opts) if err != nil { return nil, err @@ -98,19 +103,19 @@ func (d *defaultAmqpDialer) Dial(ctx context.Context, addr string, opts *amqp.Co var _ amqpDial = (*defaultAmqpDialer)(nil) -func NewAmqpConnection(config AmqpConnectionConfig, connectionName string) *AmqpConnection { +func NewAmqpConnection(config pkgCommon.AmqpConnectionConfig, connectionName string) *AmqpConnection { return &AmqpConnection{ - connectionName: connectionName, - lock: sync.RWMutex{}, - brokerUrl: config.BrokerUrl, - username: config.Username, - password: config.Password, - dialer: &defaultAmqpDialer{}, + ConnectionName: connectionName, + Lock: sync.RWMutex{}, + BrokerUrl: config.BrokerUrl, + Username: config.Username, + Password: config.Password, + Dialer: &defaultAmqpDialer{}, } } func (c *AmqpConnection) NewSender(ctx context.Context, topic string) (*AmqpSenderSession, error) { - if c.conn == nil { + if c.Conn == nil { return nil, errors.New("connection is not initialized") } @@ -118,11 +123,11 @@ func (c *AmqpConnection) NewSender(ctx context.Context, topic string) (*AmqpSend return nil, ErrConnectionClosed } - c.lock.RLock() - defer c.lock.RUnlock() + c.Lock.RLock() + defer c.Lock.RUnlock() // new session - newSession, err := c.conn.NewSession(ctx, nil) + newSession, err := c.Conn.NewSession(ctx, nil) if err != nil { return nil, fmt.Errorf("new session: %w", err) } @@ -157,8 +162,8 @@ func As[T any](value any, err error) (*T, error) { } func (c *AmqpConnection) Connect() error { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock.Lock() + defer c.Lock.Unlock() subCtx, cancel := context.WithTimeout(context.Background(), connectionTimeoutSeconds*time.Second) defer cancel() @@ -170,11 +175,11 @@ func (c *AmqpConnection) Connect() error { } func (c *AmqpConnection) internalConnect(ctx context.Context) error { - if c.conn == nil { + if c.Conn == nil { // Set credentials if specified auth := amqp.SASLTypeAnonymous() - if c.username != "" && c.password != "" { - auth = amqp.SASLTypePlain(c.username, c.password) + if c.Username != "" && c.Password != "" { + auth = amqp.SASLTypePlain(c.Username, c.Password) } else { slog.Debug("amqp connection: connect: using anonymous messaging") } @@ -183,18 +188,18 @@ func (c *AmqpConnection) internalConnect(ctx context.Context) error { } // Initialize connection - conn, err := c.dialer.Dial(ctx, c.brokerUrl, options) + conn, err := c.Dialer.Dial(ctx, c.BrokerUrl, options) if err != nil { return fmt.Errorf("dial: %w", err) } - c.conn = conn + c.Conn = conn } return nil } func (c *AmqpConnection) Close() error { - c.lock.Lock() - defer c.lock.Unlock() + c.Lock.Lock() + defer c.Lock.Unlock() if err := c.internalClose(); err != nil { return fmt.Errorf("internal close: %w", err) @@ -203,28 +208,28 @@ func (c *AmqpConnection) Close() error { } func (c *AmqpConnection) internalClose() error { - if c.conn != nil { - if err := c.conn.Close(); err != nil { + if c.Conn != nil { + if err := c.Conn.Close(); err != nil { return fmt.Errorf("connection close: %w", err) } - c.conn = nil + c.Conn = nil } return nil } func (c *AmqpConnection) IsClosed() bool { - c.lock.RLock() - defer c.lock.RUnlock() + c.Lock.RLock() + defer c.Lock.RUnlock() return c.internalIsClosed() } func (c *AmqpConnection) internalIsClosed() bool { - if c.conn == nil { + if c.Conn == nil { return true } select { - case <-c.conn.Done(): + case <-c.Conn.Done(): return true default: return false diff --git a/audit/messaging/amqp_connection_pool.go b/internal/messaging/amqp_connection_pool.go similarity index 68% rename from audit/messaging/amqp_connection_pool.go rename to internal/messaging/amqp_connection_pool.go index 9fa3f7a..2ab7d21 100644 --- a/audit/messaging/amqp_connection_pool.go +++ b/internal/messaging/amqp_connection_pool.go @@ -5,15 +5,17 @@ import ( "fmt" "log/slog" "sync" + + pkgCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" ) type connectionProvider interface { - NewAmqpConnection(config AmqpConnectionConfig, connectionName string) *AmqpConnection + NewAmqpConnection(config pkgCommon.AmqpConnectionConfig, connectionName string) *AmqpConnection } type defaultAmqpConnectionProvider struct{} -func (p defaultAmqpConnectionProvider) NewAmqpConnection(config AmqpConnectionConfig, connectionName string) *AmqpConnection { +func (p defaultAmqpConnectionProvider) NewAmqpConnection(config pkgCommon.AmqpConnectionConfig, connectionName string) *AmqpConnection { return NewAmqpConnection(config, connectionName) } @@ -26,37 +28,37 @@ type ConnectionPool interface { } type AmqpConnectionPool struct { - config AmqpConnectionPoolConfig - connectionName string - connections []*AmqpConnection - connectionProvider connectionProvider - handleOffset int - lock sync.RWMutex + Config pkgCommon.AmqpConnectionPoolConfig + ConnectionName string + Connections []*AmqpConnection + ConnectionProvider connectionProvider + HandleOffset int + Lock sync.RWMutex } type ConnectionPoolHandle struct { - connectionOffset int + ConnectionOffset int } -func NewDefaultAmqpConnectionPool(config AmqpConnectionConfig, connectionName string) (ConnectionPool, error) { - poolConfig := AmqpConnectionPoolConfig{ +func NewDefaultAmqpConnectionPool(config pkgCommon.AmqpConnectionConfig, connectionName string) (ConnectionPool, error) { + poolConfig := pkgCommon.AmqpConnectionPoolConfig{ Parameters: config, PoolSize: 2, } return NewAmqpConnectionPool(poolConfig, connectionName) } -func NewAmqpConnectionPool(config AmqpConnectionPoolConfig, connectionName string) (ConnectionPool, error) { +func NewAmqpConnectionPool(config pkgCommon.AmqpConnectionPoolConfig, connectionName string) (ConnectionPool, error) { if config.PoolSize == 0 { config.PoolSize = 2 } pool := &AmqpConnectionPool{ - config: config, - connectionName: connectionName, - connections: make([]*AmqpConnection, 0), - connectionProvider: defaultAmqpConnectionProvider{}, - handleOffset: 0, - lock: sync.RWMutex{}, + Config: config, + ConnectionName: connectionName, + Connections: make([]*AmqpConnection, 0), + ConnectionProvider: defaultAmqpConnectionProvider{}, + HandleOffset: 0, + Lock: sync.RWMutex{}, } if err := pool.initializeConnections(); err != nil { @@ -70,11 +72,11 @@ func NewAmqpConnectionPool(config AmqpConnectionPoolConfig, connectionName strin } func (p *AmqpConnectionPool) initializeConnections() error { - if len(p.connections) < p.config.PoolSize { - p.lock.Lock() - defer p.lock.Unlock() + if len(p.Connections) < p.Config.PoolSize { + p.Lock.Lock() + defer p.Lock.Unlock() - numMissingConnections := p.config.PoolSize - len(p.connections) + numMissingConnections := p.Config.PoolSize - len(p.Connections) for i := 0; i < numMissingConnections; i++ { if err := p.internalAddConnection(); err != nil { @@ -90,12 +92,12 @@ func (p *AmqpConnectionPool) internalAddConnection() error { if err != nil { return fmt.Errorf("new connection: %w", err) } - p.connections = append(p.connections, newConnection) + p.Connections = append(p.Connections, newConnection) return nil } func (p *AmqpConnectionPool) internalNewConnection() (*AmqpConnection, error) { - conn := p.connectionProvider.NewAmqpConnection(p.config.Parameters, p.connectionName) + conn := p.ConnectionProvider.NewAmqpConnection(p.Config.Parameters, p.ConnectionName) if err := conn.Connect(); err != nil { slog.Warn("amqp connection: failed to connect to amqp broker", slog.Any("err", err)) @@ -114,18 +116,18 @@ func (p *AmqpConnectionPool) internalNewConnection() (*AmqpConnection, error) { } func (p *AmqpConnectionPool) Close() error { - p.lock.Lock() - defer p.lock.Unlock() + p.Lock.Lock() + defer p.Lock.Unlock() closeErrors := make([]error, 0) - for _, conn := range p.connections { + for _, conn := range p.Connections { if conn != nil { if err := conn.Close(); err != nil { closeErrors = append(closeErrors, fmt.Errorf("pooled connection: %w", err)) } } } - p.connections = make([]*AmqpConnection, p.config.PoolSize) + p.Connections = make([]*AmqpConnection, p.Config.PoolSize) if len(closeErrors) > 0 { return errors.Join(closeErrors...) } @@ -133,16 +135,16 @@ func (p *AmqpConnectionPool) Close() error { } func (p *AmqpConnectionPool) NewHandle() *ConnectionPoolHandle { - p.lock.Lock() - defer p.lock.Unlock() + p.Lock.Lock() + defer p.Lock.Unlock() - offset := p.handleOffset - p.handleOffset++ + offset := p.HandleOffset + p.HandleOffset++ - offset %= p.config.PoolSize + offset %= p.Config.PoolSize return &ConnectionPoolHandle{ - connectionOffset: offset, + ConnectionOffset: offset, } } @@ -152,16 +154,16 @@ func (p *AmqpConnectionPool) GetConnection(handle *ConnectionPoolHandle) (*AmqpC // renew the requested connection if the request connection is closed if conn == nil || addConnection { - p.lock.Lock() + p.Lock.Lock() // check that accessing the pool only with a valid index (out of bounds should only occur on shutdown) connectionIndex := p.connectionIndex(handle, 0) - if p.connections[connectionIndex] == nil { + if p.Connections[connectionIndex] == nil { connection, err := p.internalNewConnection() if err != nil { if conn == nil { // case: connection could not be renewed and no connection to return has been found - p.lock.Unlock() + p.Lock.Unlock() return nil, fmt.Errorf("renew connection: %w", err) } @@ -169,11 +171,11 @@ func (p *AmqpConnectionPool) GetConnection(handle *ConnectionPoolHandle) (*AmqpC slog.Warn("amqp connection pool: get connection: renew connection: ", slog.Any("err", err)) } else { // case: connection could be renewed and will be added to pool - p.connections[connectionIndex] = connection + p.Connections[connectionIndex] = connection conn = connection } } - p.lock.Unlock() + p.Lock.Unlock() } if conn == nil { @@ -187,18 +189,18 @@ func (p *AmqpConnectionPool) nextConnectionForHandle(handle *ConnectionPoolHandl // retry as long as there are remaining connections in the pool var conn *AmqpConnection var addConnection bool - for i := 0; i < p.config.PoolSize; i++ { + for i := 0; i < p.Config.PoolSize; i++ { // get the next possible connection (considering the retry index) idx := p.connectionIndex(handle, i) - p.lock.RLock() - if idx < len(p.connections) { - conn = p.connections[idx] + p.Lock.RLock() + if idx < len(p.Connections) { + conn = p.Connections[idx] } else { // handle the edge case that the pool is empty on shutdown conn = nil } - p.lock.RUnlock() + p.Lock.RUnlock() // remember that the requested is closed, retry with the next if conn == nil { @@ -208,9 +210,9 @@ func (p *AmqpConnectionPool) nextConnectionForHandle(handle *ConnectionPoolHandl // if the connection is closed, mark it by setting it to nil if conn.IsClosed() { - p.lock.Lock() - p.connections[idx] = nil - p.lock.Unlock() + p.Lock.Lock() + p.Connections[idx] = nil + p.Lock.Unlock() addConnection = true continue @@ -222,8 +224,8 @@ func (p *AmqpConnectionPool) nextConnectionForHandle(handle *ConnectionPoolHandl } func (p *AmqpConnectionPool) connectionIndex(handle *ConnectionPoolHandle, iteration int) int { - if iteration+handle.connectionOffset >= p.config.PoolSize { - return (iteration + handle.connectionOffset) % p.config.PoolSize + if iteration+handle.ConnectionOffset >= p.Config.PoolSize { + return (iteration + handle.ConnectionOffset) % p.Config.PoolSize } - return iteration + handle.connectionOffset + return iteration + handle.ConnectionOffset } diff --git a/audit/messaging/amqp_connection_pool_test.go b/internal/messaging/amqp_connection_pool_test.go similarity index 68% rename from audit/messaging/amqp_connection_pool_test.go rename to internal/messaging/amqp_connection_pool_test.go index 85a7df5..6a3f694 100644 --- a/audit/messaging/amqp_connection_pool_test.go +++ b/internal/messaging/amqp_connection_pool_test.go @@ -3,17 +3,20 @@ package messaging import ( "errors" "fmt" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "sync" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + pkgMessagingCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" ) type connectionProviderMock struct { mock.Mock } -func (p *connectionProviderMock) NewAmqpConnection(config AmqpConnectionConfig, connectionName string) *AmqpConnection { +func (p *connectionProviderMock) NewAmqpConnection(config pkgMessagingCommon.AmqpConnectionConfig, connectionName string) *AmqpConnection { args := p.Called(config, connectionName) return args.Get(0).(*AmqpConnection) } @@ -24,28 +27,28 @@ func Test_AmqpConnectionPool_GetHandle(t *testing.T) { t.Run("next handle", func(t *testing.T) { pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, } handle := pool.NewHandle() assert.NotNil(t, handle) - assert.Equal(t, 0, handle.connectionOffset) - assert.Equal(t, 1, pool.handleOffset) + assert.Equal(t, 0, handle.ConnectionOffset) + assert.Equal(t, 1, pool.HandleOffset) }) t.Run("next handle high offset", func(t *testing.T) { pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 13, - lock: sync.RWMutex{}, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 13, + Lock: sync.RWMutex{}, } handle := pool.NewHandle() assert.NotNil(t, handle) - assert.Equal(t, 3, handle.connectionOffset) - assert.Equal(t, 14, pool.handleOffset) + assert.Equal(t, 3, handle.ConnectionOffset) + assert.Equal(t, 14, pool.HandleOffset) }) } @@ -58,24 +61,24 @@ func Test_AmqpConnectionPool_internalAddConnection(t *testing.T) { dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil) connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: dialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: dialer, } connectionProvider := &connectionProviderMock{} connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + ConnectionProvider: connectionProvider, } err := pool.internalAddConnection() assert.NoError(t, err) - assert.Equal(t, 1, len(pool.connections)) + assert.Equal(t, 1, len(pool.Connections)) connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1) dialer.AssertNumberOfCalls(t, "Dial", 1) }) @@ -89,24 +92,24 @@ func Test_AmqpConnectionPool_internalAddConnection(t *testing.T) { dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil) connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: dialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: dialer, } connectionProvider := &connectionProviderMock{} connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + ConnectionProvider: connectionProvider, } err := pool.internalAddConnection() assert.NoError(t, err) - assert.Equal(t, 1, len(pool.connections)) + assert.Equal(t, 1, len(pool.Connections)) connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1) dialer.AssertNumberOfCalls(t, "Dial", 2) }) @@ -117,24 +120,24 @@ func Test_AmqpConnectionPool_internalAddConnection(t *testing.T) { dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error")) connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: dialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: dialer, } connectionProvider := &connectionProviderMock{} connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + ConnectionProvider: connectionProvider, } err := pool.internalAddConnection() assert.EqualError(t, err, "new connection: new internal connection: internal connect: dial: test error") - assert.Equal(t, 0, len(pool.connections)) + assert.Equal(t, 0, len(pool.Connections)) connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1) dialer.AssertNumberOfCalls(t, "Dial", 2) }) @@ -149,24 +152,24 @@ func Test_AmqpConnectionPool_initializeConnections(t *testing.T) { dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil) connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: dialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: dialer, } connectionProvider := &connectionProviderMock{} connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + ConnectionProvider: connectionProvider, } err := pool.initializeConnections() assert.NoError(t, err) - assert.Equal(t, 5, len(pool.connections)) + assert.Equal(t, 5, len(pool.Connections)) connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 5) }) @@ -177,9 +180,9 @@ func Test_AmqpConnectionPool_initializeConnections(t *testing.T) { failingDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error")) failingConnection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: failingDialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: failingDialer, } conn := &amqpConnMock{} @@ -187,25 +190,25 @@ func Test_AmqpConnectionPool_initializeConnections(t *testing.T) { successfulDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil) successfulConnection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: successfulDialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: successfulDialer, } connectionProvider := &connectionProviderMock{} connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(successfulConnection).Times(4) connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(failingConnection) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + ConnectionProvider: connectionProvider, } err := pool.initializeConnections() assert.EqualError(t, err, "new connection: new internal connection: internal connect: dial: test error") - assert.Equal(t, 4, len(pool.connections)) + assert.Equal(t, 4, len(pool.Connections)) connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 5) }) } @@ -221,30 +224,30 @@ func Test_AmqpConnectionPool_Close(t *testing.T) { dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil) connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: dialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: dialer, } connectionProvider := &connectionProviderMock{} connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + ConnectionProvider: connectionProvider, } err := pool.initializeConnections() assert.NoError(t, err) - assert.Equal(t, 5, len(pool.connections)) + assert.Equal(t, 5, len(pool.Connections)) // close the pool err = pool.Close() assert.NoError(t, err) - assert.Equal(t, 5, len(pool.connections)) - for _, c := range pool.connections { + assert.Equal(t, 5, len(pool.Connections)) + for _, c := range pool.Connections { assert.Nil(t, c) } }) @@ -258,9 +261,9 @@ func Test_AmqpConnectionPool_Close(t *testing.T) { failingDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(failingConn, nil) failingConnection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: failingDialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: failingDialer, } successfulConn := &amqpConnMock{} @@ -269,9 +272,9 @@ func Test_AmqpConnectionPool_Close(t *testing.T) { successfulDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(successfulConn, nil) successfulConnection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: successfulDialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: successfulDialer, } connectionProvider := &connectionProviderMock{} @@ -280,22 +283,22 @@ func Test_AmqpConnectionPool_Close(t *testing.T) { connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(successfulConnection).Times(1) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + ConnectionProvider: connectionProvider, } err := pool.initializeConnections() assert.NoError(t, err) - assert.Equal(t, 5, len(pool.connections)) + assert.Equal(t, 5, len(pool.Connections)) // close the pool err = pool.Close() assert.EqualError(t, err, "pooled connection: internal close: connection close: test error\npooled connection: internal close: connection close: test error") - assert.Equal(t, 5, len(pool.connections)) - for _, c := range pool.connections { + assert.Equal(t, 5, len(pool.Connections)) + for _, c := range pool.Connections { assert.Nil(t, c) } }) @@ -311,9 +314,9 @@ func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) { conn := &amqpConnMock{} conn.On("Done", mock.Anything).Return(channelReceiver(channel)) return &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - conn: conn, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Conn: conn, } } @@ -324,9 +327,9 @@ func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) { conn := &amqpConnMock{} conn.On("Done", mock.Anything).Return(channelReceiver(channel)) return &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - conn: conn, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Conn: conn, } } @@ -337,13 +340,13 @@ func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) { } pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, } - connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 1}) + connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 1}) assert.NotNil(t, connection) assert.False(t, addConnection) }) @@ -357,13 +360,13 @@ func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) { connections = append(connections, newActiveConnection()) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, } - connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 1}) + connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 1}) assert.NotNil(t, connection) assert.True(t, addConnection) }) @@ -377,13 +380,13 @@ func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) { connections = append(connections, newActiveConnection()) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, } - connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 1}) + connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 1}) assert.NotNil(t, connection) assert.True(t, addConnection) }) @@ -397,13 +400,13 @@ func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) { connections = append(connections, nil) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, } - connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 1}) + connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 1}) assert.Nil(t, connection) assert.True(t, addConnection) }) @@ -417,13 +420,13 @@ func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) { connections = append(connections, nil) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, } - connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 23}) + connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 23}) assert.NotNil(t, connection) assert.False(t, addConnection) }) @@ -437,13 +440,13 @@ func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) { connections = append(connections, newActiveConnection()) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, } - connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{connectionOffset: 23}) + connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 23}) assert.NotNil(t, connection) assert.True(t, addConnection) }) @@ -459,9 +462,9 @@ func Test_AmqpConnectionPool_GetConnection(t *testing.T) { conn := &amqpConnMock{} conn.On("Done", mock.Anything).Return(channelReceiver(channel)) return &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - conn: conn, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Conn: conn, } } @@ -472,13 +475,13 @@ func Test_AmqpConnectionPool_GetConnection(t *testing.T) { } pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, } - connection, err := pool.GetConnection(&ConnectionPoolHandle{connectionOffset: 1}) + connection, err := pool.GetConnection(&ConnectionPoolHandle{ConnectionOffset: 1}) assert.NoError(t, err) assert.NotNil(t, connection) assert.Equal(t, connections[1], connection) @@ -492,14 +495,14 @@ func Test_AmqpConnectionPool_GetConnection(t *testing.T) { connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(newActiveConnection()) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, + ConnectionProvider: connectionProvider, } - connection, err := pool.GetConnection(&ConnectionPoolHandle{connectionOffset: 1}) + connection, err := pool.GetConnection(&ConnectionPoolHandle{ConnectionOffset: 1}) assert.NoError(t, err) assert.NotNil(t, connection) assert.Equal(t, connections[1], connection) @@ -520,21 +523,21 @@ func Test_AmqpConnectionPool_GetConnection(t *testing.T) { var c *amqpConnMock = nil dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, fmt.Errorf("dial error")) connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: dialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: dialer, } connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, + ConnectionProvider: connectionProvider, } - connection, err := pool.GetConnection(&ConnectionPoolHandle{connectionOffset: 1}) + connection, err := pool.GetConnection(&ConnectionPoolHandle{ConnectionOffset: 1}) assert.NoError(t, err) assert.NotNil(t, connection) assert.Nil(t, connections[1]) @@ -556,21 +559,21 @@ func Test_AmqpConnectionPool_GetConnection(t *testing.T) { var c *amqpConnMock = nil dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, fmt.Errorf("dial error")) connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - dialer: dialer, + ConnectionName: "test", + Lock: sync.RWMutex{}, + Dialer: dialer, } connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection) pool := AmqpConnectionPool{ - config: AmqpConnectionPoolConfig{PoolSize: 5}, - handleOffset: 0, - lock: sync.RWMutex{}, - connections: connections, - connectionProvider: connectionProvider, + Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5}, + HandleOffset: 0, + Lock: sync.RWMutex{}, + Connections: connections, + ConnectionProvider: connectionProvider, } - connection, err := pool.GetConnection(&ConnectionPoolHandle{connectionOffset: 1}) + connection, err := pool.GetConnection(&ConnectionPoolHandle{ConnectionOffset: 1}) assert.EqualError(t, err, "renew connection: new internal connection: internal connect: dial: dial error") assert.Nil(t, connection) assert.Equal(t, 5, len(connections)) diff --git a/audit/messaging/amqp_connection_test.go b/internal/messaging/amqp_connection_test.go similarity index 80% rename from audit/messaging/amqp_connection_test.go rename to internal/messaging/amqp_connection_test.go index 7eace51..fc6fdca 100644 --- a/audit/messaging/amqp_connection_test.go +++ b/internal/messaging/amqp_connection_test.go @@ -3,11 +3,14 @@ package messaging import ( "context" "errors" + "sync" + "testing" + "github.com/Azure/go-amqp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "sync" - "testing" + + pkgCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" ) type amqpConnMock struct { @@ -19,9 +22,9 @@ func (m *amqpConnMock) Done() <-chan struct{} { return args.Get(0).(<-chan struct{}) } -func (m *amqpConnMock) NewSession(ctx context.Context, opts *amqp.SessionOptions) (amqpSession, error) { +func (m *amqpConnMock) NewSession(ctx context.Context, opts *amqp.SessionOptions) (AmqpSession, error) { args := m.Called(ctx, opts) - return args.Get(0).(amqpSession), args.Error(1) + return args.Get(0).(AmqpSession), args.Error(1) } func (m *amqpConnMock) Close() error { @@ -29,15 +32,15 @@ func (m *amqpConnMock) Close() error { return args.Error(0) } -var _ amqpConn = (*amqpConnMock)(nil) +var _ AmqpConn = (*amqpConnMock)(nil) type amqpDialMock struct { mock.Mock } -func (m *amqpDialMock) Dial(ctx context.Context, addr string, opts *amqp.ConnOptions) (amqpConn, error) { +func (m *amqpDialMock) Dial(ctx context.Context, addr string, opts *amqp.ConnOptions) (AmqpConn, error) { args := m.Called(ctx, addr, opts) - return args.Get(0).(amqpConn), args.Error(1) + return args.Get(0).(AmqpConn), args.Error(1) } var _ amqpDial = (*amqpDialMock)(nil) @@ -46,9 +49,9 @@ type amqpSessionMock struct { mock.Mock } -func (m *amqpSessionMock) NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (amqpSender, error) { +func (m *amqpSessionMock) NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (AmqpSender, error) { args := m.Called(ctx, target, opts) - return args.Get(0).(amqpSender), args.Error(1) + return args.Get(0).(AmqpSender), args.Error(1) } func (m *amqpSessionMock) Close(ctx context.Context) error { @@ -56,12 +59,12 @@ func (m *amqpSessionMock) Close(ctx context.Context) error { return args.Error(0) } -var _ amqpSession = (*amqpSessionMock)(nil) +var _ AmqpSession = (*amqpSessionMock)(nil) func Test_AmqpConnection_IsClosed(t *testing.T) { connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, + ConnectionName: "test", + Lock: sync.RWMutex{}, } channelReceiver := func(channel chan struct{}) <-chan struct{} { @@ -77,7 +80,7 @@ func Test_AmqpConnection_IsClosed(t *testing.T) { close(channel) amqpConnMock := &amqpConnMock{} amqpConnMock.On("Done").Return(channelReceiver(channel)) - connection.conn = amqpConnMock + connection.Conn = amqpConnMock assert.True(t, connection.IsClosed()) }) @@ -86,7 +89,7 @@ func Test_AmqpConnection_IsClosed(t *testing.T) { channel := make(chan struct{}) amqpConnMock := &amqpConnMock{} amqpConnMock.On("Done").Return(channelReceiver(channel)) - connection.conn = amqpConnMock + connection.Conn = amqpConnMock assert.False(t, connection.IsClosed()) }) @@ -94,8 +97,8 @@ func Test_AmqpConnection_IsClosed(t *testing.T) { func Test_AmqpConnection_Close(t *testing.T) { connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, + ConnectionName: "test", + Lock: sync.RWMutex{}, } t.Run("already closed", func(t *testing.T) { @@ -107,66 +110,66 @@ func Test_AmqpConnection_Close(t *testing.T) { amqpConnMock := &amqpConnMock{} amqpConnMock.On("Close").Return(err) - connection.conn = amqpConnMock + connection.Conn = amqpConnMock assert.EqualError(t, connection.Close(), "internal close: connection close: test error") - assert.NotNil(t, connection.conn) + assert.NotNil(t, connection.Conn) amqpConnMock.AssertNumberOfCalls(t, "Close", 1) }) t.Run("close without error", func(t *testing.T) { amqpConnMock := &amqpConnMock{} amqpConnMock.On("Close").Return(nil) - connection.conn = amqpConnMock + connection.Conn = amqpConnMock assert.Nil(t, connection.Close()) - assert.Nil(t, connection.conn) + assert.Nil(t, connection.Conn) amqpConnMock.AssertNumberOfCalls(t, "Close", 1) }) } func Test_AmqpConnection_Connect(t *testing.T) { connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, + ConnectionName: "test", + Lock: sync.RWMutex{}, } t.Run("already connected", func(t *testing.T) { - connection.conn = &amqpConnMock{} + connection.Conn = &amqpConnMock{} assert.NoError(t, connection.Connect()) }) t.Run("dial error", func(t *testing.T) { - connection.conn = nil - connection.username = "user" - connection.password = "pass" + connection.Conn = nil + connection.Username = "user" + connection.Password = "pass" amqpDialMock := &amqpDialMock{} var c *amqpConnMock = nil amqpDialMock.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error")) - connection.dialer = amqpDialMock + connection.Dialer = amqpDialMock assert.EqualError(t, connection.Connect(), "internal connect: dial: test error") - assert.Nil(t, connection.conn) + assert.Nil(t, connection.Conn) }) t.Run("connect without error", func(t *testing.T) { - connection.conn = nil + connection.Conn = nil amqpDialMock := &amqpDialMock{} amqpConn := &amqpConnMock{} amqpDialMock.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(amqpConn, nil) - connection.dialer = amqpDialMock + connection.Dialer = amqpDialMock assert.NoError(t, connection.Connect()) - assert.Equal(t, amqpConn, connection.conn) + assert.Equal(t, amqpConn, connection.Conn) }) } func Test_AmqpConnection_NewSender(t *testing.T) { connection := &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, + ConnectionName: "test", + Lock: sync.RWMutex{}, } channelReceiver := func(channel chan struct{}) <-chan struct{} { @@ -185,7 +188,7 @@ func Test_AmqpConnection_NewSender(t *testing.T) { conn := &amqpConnMock{} conn.On("Done").Return(channelReceiver(channel)) - connection.conn = conn + connection.Conn = conn sender, err := connection.NewSender(context.Background(), "topic") assert.EqualError(t, err, "amqp connection is closed") @@ -199,7 +202,7 @@ func Test_AmqpConnection_NewSender(t *testing.T) { conn := &amqpConnMock{} conn.On("NewSession", mock.Anything, mock.Anything).Return(session, errors.New("test error")) conn.On("Done").Return(channelReceiver(channel)) - connection.conn = conn + connection.Conn = conn sender, err := connection.NewSender(context.Background(), "topic") assert.EqualError(t, err, "new session: test error") @@ -217,7 +220,7 @@ func Test_AmqpConnection_NewSender(t *testing.T) { conn := &amqpConnMock{} conn.On("Done").Return(channelReceiver(channel)) conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil) - connection.conn = conn + connection.Conn = conn sender, err := connection.NewSender(context.Background(), "topic") assert.EqualError(t, err, "new internal sender: test error") @@ -235,7 +238,7 @@ func Test_AmqpConnection_NewSender(t *testing.T) { conn := &amqpConnMock{} conn.On("Done").Return(channelReceiver(channel)) conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil) - connection.conn = conn + connection.Conn = conn sender, err := connection.NewSender(context.Background(), "topic") assert.EqualError(t, err, "new internal sender: test error\nclose session: close error") @@ -252,29 +255,29 @@ func Test_AmqpConnection_NewSender(t *testing.T) { conn := &amqpConnMock{} conn.On("Done").Return(channelReceiver(channel)) conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil) - connection.conn = conn + connection.Conn = conn sender, err := connection.NewSender(context.Background(), "topic") assert.NoError(t, err) assert.NotNil(t, sender) - assert.Equal(t, amqpSender, sender.sender) - assert.Equal(t, sessionMock, sender.session) + assert.Equal(t, amqpSender, sender.Sender) + assert.Equal(t, sessionMock, sender.Session) }) } func Test_AmqpConnection_NewAmqpConnection(t *testing.T) { - config := AmqpConnectionConfig{ + config := pkgCommon.AmqpConnectionConfig{ BrokerUrl: "brokerUrl", Username: "username", Password: "password", } connection := NewAmqpConnection(config, "connectionName") assert.NotNil(t, connection) - assert.Equal(t, connection.connectionName, "connectionName") - assert.Equal(t, connection.brokerUrl, "brokerUrl") - assert.Equal(t, connection.username, "username") - assert.Equal(t, connection.password, "password") - assert.NotNil(t, connection.dialer) + assert.Equal(t, connection.ConnectionName, "connectionName") + assert.Equal(t, connection.BrokerUrl, "brokerUrl") + assert.Equal(t, connection.Username, "username") + assert.Equal(t, connection.Password, "password") + assert.NotNil(t, connection.Dialer) } func Test_As(t *testing.T) { diff --git a/audit/messaging/amqp_sender_session.go b/internal/messaging/amqp_sender_session.go similarity index 82% rename from audit/messaging/amqp_sender_session.go rename to internal/messaging/amqp_sender_session.go index c963945..a9e2a47 100644 --- a/audit/messaging/amqp_sender_session.go +++ b/internal/messaging/amqp_sender_session.go @@ -4,19 +4,22 @@ import ( "context" "errors" "fmt" - "github.com/Azure/go-amqp" "strings" "time" + + "github.com/Azure/go-amqp" ) -type amqpSender interface { +const amqpTopicPrefix = "topic://" + +type AmqpSender interface { Send(ctx context.Context, msg *amqp.Message, opts *amqp.SendOptions) error Close(ctx context.Context) error } type AmqpSenderSession struct { - session amqpSession - sender amqpSender + Session AmqpSession + Sender AmqpSender } func (s *AmqpSenderSession) Send( @@ -26,11 +29,11 @@ func (s *AmqpSenderSession) Send( applicationProperties map[string]any, ) error { // check topic name - if !strings.HasPrefix(topic, AmqpTopicPrefix) { + if !strings.HasPrefix(topic, amqpTopicPrefix) { return fmt.Errorf( "topic %q name lacks mandatory prefix %q", topic, - AmqpTopicPrefix, + amqpTopicPrefix, ) } @@ -54,7 +57,7 @@ func (s *AmqpSenderSession) Send( // send ctx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second) defer cancelFn() - return s.sender.Send(ctx, &message, nil) + return s.Sender.Send(ctx, &message, nil) } func (s *AmqpSenderSession) Close() error { @@ -62,11 +65,11 @@ func (s *AmqpSenderSession) Close() error { defer cancelFn() var closeErrors []error - senderErr := s.sender.Close(ctx) + senderErr := s.Sender.Close(ctx) if senderErr != nil { closeErrors = append(closeErrors, senderErr) } - sessionErr := s.session.Close(ctx) + sessionErr := s.Session.Close(ctx) if sessionErr != nil { closeErrors = append(closeErrors, sessionErr) } diff --git a/audit/messaging/amqp_sender_session_test.go b/internal/messaging/amqp_sender_session_test.go similarity index 92% rename from audit/messaging/amqp_sender_session_test.go rename to internal/messaging/amqp_sender_session_test.go index 9d0409f..2a0f5f7 100644 --- a/audit/messaging/amqp_sender_session_test.go +++ b/internal/messaging/amqp_sender_session_test.go @@ -3,10 +3,11 @@ package messaging import ( "context" "errors" + "testing" + "github.com/Azure/go-amqp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "testing" ) type amqpSenderMock struct { @@ -21,7 +22,7 @@ func (m *amqpSenderMock) Close(ctx context.Context) error { return m.Called(ctx).Error(0) } -var _ amqpSender = (*amqpSenderMock)(nil) +var _ AmqpSender = (*amqpSenderMock)(nil) func Test_AmqpSenderSession_Close(t *testing.T) { @@ -32,8 +33,8 @@ func Test_AmqpSenderSession_Close(t *testing.T) { session.On("Close", mock.Anything).Return(nil) senderSession := &AmqpSenderSession{ - sender: sender, - session: session, + Sender: sender, + Session: session, } err := senderSession.Close() @@ -50,8 +51,8 @@ func Test_AmqpSenderSession_Close(t *testing.T) { session.On("Close", mock.Anything).Return(nil) senderSession := &AmqpSenderSession{ - sender: sender, - session: session, + Sender: sender, + Session: session, } err := senderSession.Close() @@ -68,8 +69,8 @@ func Test_AmqpSenderSession_Close(t *testing.T) { session.On("Close", mock.Anything).Return(errors.New("session error")) senderSession := &AmqpSenderSession{ - sender: sender, - session: session, + Sender: sender, + Session: session, } err := senderSession.Close() @@ -86,8 +87,8 @@ func Test_AmqpSenderSession_Close(t *testing.T) { session.On("Close", mock.Anything).Return(errors.New("session error")) senderSession := &AmqpSenderSession{ - sender: sender, - session: session, + Sender: sender, + Session: session, } err := senderSession.Close() @@ -105,8 +106,8 @@ func Test_AmqpSenderSession_Send(t *testing.T) { session := &amqpSessionMock{} senderSession := &AmqpSenderSession{ - sender: sender, - session: session, + Sender: sender, + Session: session, } data := [][]byte{[]byte("data")} @@ -119,8 +120,8 @@ func Test_AmqpSenderSession_Send(t *testing.T) { session := &amqpSessionMock{} senderSession := &AmqpSenderSession{ - sender: sender, - session: session, + Sender: sender, + Session: session, } data := [][]byte{[]byte("data")} @@ -134,8 +135,8 @@ func Test_AmqpSenderSession_Send(t *testing.T) { session := &amqpSessionMock{} senderSession := &AmqpSenderSession{ - sender: sender, - session: session, + Sender: sender, + Session: session, } data := [][]byte{[]byte("data")} @@ -171,8 +172,8 @@ func Test_AmqpSenderSession_Send(t *testing.T) { session := &amqpSessionMock{} senderSession := &AmqpSenderSession{ - sender: sender, - session: session, + Sender: sender, + Session: session, } data := [][]byte{[]byte("data")} diff --git a/telemetry/telemetry.go b/internal/telemetry/telemetry.go similarity index 100% rename from telemetry/telemetry.go rename to internal/telemetry/telemetry.go diff --git a/audit/api/api_legacy.go b/pkg/audit/api/api_legacy.go similarity index 62% rename from audit/api/api_legacy.go rename to pkg/audit/api/api_legacy.go index 7c207e7..086c8dd 100644 --- a/audit/api/api_legacy.go +++ b/pkg/audit/api/api_legacy.go @@ -2,32 +2,24 @@ package api import ( "context" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" "errors" "fmt" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/trace" "strings" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" "google.golang.org/protobuf/proto" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api" ) const DataTypeLegacyAuditEventV1 = "audit.v1.LegacyAuditEvent" -// LegacyTopicNameResolver implements TopicNameResolver. -// A hard-coded topic name is used, routing identifiers are ignored. -type LegacyTopicNameResolver struct { - topicName string -} - -// Resolve implements TopicNameResolver.Resolve -func (r *LegacyTopicNameResolver) Resolve(*RoutableIdentifier) (string, error) { - return r.topicName, nil -} - -// LegacyTopicNameConfig provides topic name information required for the topic name resolution. -type LegacyTopicNameConfig struct { +// StaticTopicNameConfig provides topic name information required for the topic name resolution. +type StaticTopicNameConfig struct { TopicName string } @@ -35,39 +27,39 @@ type LegacyTopicNameConfig struct { // // Note: The implementation will be deprecated and replaced with the "routableAuditApi" once the new audit log routing is implemented type LegacyAuditApi struct { - messagingApi messaging.Api - topicNameResolver TopicNameResolver + messagingApi pkgMessagingApi.Api + topicNameResolver pkgAuditCommon.TopicNameResolver tracer trace.Tracer - validator ProtobufValidator + validator pkgAuditCommon.ProtobufValidator } // NewLegacyAuditApi can be used to initialize the audit log api. // // Note: The NewLegacyAuditApi method will be deprecated and replaced with "newRoutableAuditApi" once the new audit log routing is implemented func NewLegacyAuditApi( - messagingApi messaging.Api, - topicNameConfig LegacyTopicNameConfig, - validator ProtobufValidator, -) (AuditApi, error) { + messagingApi pkgMessagingApi.Api, + topicNameConfig StaticTopicNameConfig, + validator pkgAuditCommon.ProtobufValidator, +) (pkgAuditCommon.AuditApi, error) { if messagingApi == nil { - return nil, ErrMessagingApiNil + return nil, pkgAuditCommon.ErrMessagingApiNil } // Topic resolver if topicNameConfig.TopicName == "" { return nil, errors.New("topic name is required") } - if !TopicNamePattern.MatchString(topicNameConfig.TopicName) { + if !pkgAuditCommon.TopicNamePattern.MatchString(topicNameConfig.TopicName) { return nil, fmt.Errorf("invalid topic name: %s - "+ "expected stackit-platform/t/swz/audit-log/{region}/{version}/{eventSource}/{additionalParts} "+ "where region is one of [conway, eu01, eu02, sx-stoi01], version is vX.Y, eventSource is the service name "+ "and additionalParts is a describing string the audit log relates to or 'events'", topicNameConfig.TopicName) } - var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: topicNameConfig.TopicName} + var topicNameResolver pkgAuditCommon.TopicNameResolver = &pkgAuditCommon.StaticTopicNameTestResolver{TopicName: topicNameConfig.TopicName} // Audit api - var auditApi AuditApi = &LegacyAuditApi{ + var auditApi pkgAuditCommon.AuditApi = &LegacyAuditApi{ messagingApi: messagingApi, topicNameResolver: topicNameResolver, tracer: otel.Tracer("legacy-audit-api"), @@ -82,7 +74,7 @@ func (a *LegacyAuditApi) Log( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, ) error { cloudEvent, err := a.ValidateAndSerialize(ctx, event, visibility, routableIdentifier) @@ -99,21 +91,21 @@ func (a *LegacyAuditApi) ValidateAndSerialize( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, -) (*CloudEvent, error) { + routableIdentifier *pkgAuditCommon.RoutableIdentifier, +) (*pkgAuditCommon.CloudEvent, error) { ctx, span := a.tracer.Start(ctx, "validate-and-serialize") defer span.End() - routableEvent, err := validateAndSerializePartially(a.validator, event, visibility, routableIdentifier) + routableEvent, err := internalAuditApi.ValidateAndSerializePartially(a.validator, event, visibility, routableIdentifier) if err != nil { return nil, err } // Reject event type data-access as the downstream services // cannot handle it at the moment - if strings.HasSuffix(event.LogName, string(EventTypeDataAccess)) { - return nil, ErrUnsupportedEventTypeDataAccess + if strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeDataAccess)) { + return nil, pkgAuditCommon.ErrUnsupportedEventTypeDataAccess } // Do nothing with the serialized data in the legacy solution @@ -123,19 +115,19 @@ func (a *LegacyAuditApi) ValidateAndSerialize( } // Convert attributes - legacyBytes, err := convertAndSerializeIntoLegacyFormat(event, routableEvent) + legacyBytes, err := internalAuditApi.ConvertAndSerializeIntoLegacyFormat(event, routableEvent) if err != nil { return nil, err } - traceParent, traceState := TraceParentAndStateFromContext(ctx) + traceParent, traceState := internalAuditApi.TraceParentAndStateFromContext(ctx) - message := CloudEvent{ + message := pkgAuditCommon.CloudEvent{ SpecVersion: "1.0", Source: event.ProtoPayload.ServiceName, Id: event.InsertId, Time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), - DataContentType: ContentTypeCloudEventsJson, + DataContentType: pkgAuditCommon.ContentTypeCloudEventsJson, DataType: DataTypeLegacyAuditEventV1, Subject: event.ProtoPayload.ResourceName, Data: legacyBytes, @@ -148,15 +140,15 @@ func (a *LegacyAuditApi) ValidateAndSerialize( // Send implements AuditApi.Send func (a *LegacyAuditApi) Send( ctx context.Context, - routableIdentifier *RoutableIdentifier, - cloudEvent *CloudEvent, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, + cloudEvent *pkgAuditCommon.CloudEvent, ) error { if cloudEvent != nil && cloudEvent.TraceParent != nil && cloudEvent.TraceState != nil { - ctx = AddTraceParentAndStateToContext(ctx, *cloudEvent.TraceParent, *cloudEvent.TraceState) + ctx = internalAuditApi.AddTraceParentAndStateToContext(ctx, *cloudEvent.TraceParent, *cloudEvent.TraceState) } ctx, span := a.tracer.Start(ctx, "send") defer span.End() - return send(a.topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent) + return internalAuditApi.Send(a.topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent) } diff --git a/audit/api/api_legacy_dynamic.go b/pkg/audit/api/api_legacy_dynamic.go similarity index 66% rename from audit/api/api_legacy_dynamic.go rename to pkg/audit/api/api_legacy_dynamic.go index 54b1d8b..e7549f5 100644 --- a/audit/api/api_legacy_dynamic.go +++ b/pkg/audit/api/api_legacy_dynamic.go @@ -4,14 +4,16 @@ import ( "context" "errors" "fmt" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/trace" "strings" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" "google.golang.org/protobuf/proto" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api" ) type ContextKey string @@ -26,25 +28,25 @@ var ErrTopicNameEmpty = errors.New("empty topic name provided") // // Note: The implementation will be deprecated and replaced with the "routableAuditApi" once the new audit log routing is implemented type DynamicLegacyAuditApi struct { - messagingApi messaging.Api + messagingApi pkgMessagingApi.Api tracer trace.Tracer - validator ProtobufValidator + validator pkgAuditCommon.ProtobufValidator } // NewDynamicLegacyAuditApi can be used to initialize the audit log api. // // Note: The NewLegacyAuditApi method will be deprecated and replaced with "newRoutableAuditApi" once the new audit log routing is implemented func NewDynamicLegacyAuditApi( - messagingApi messaging.Api, - validator ProtobufValidator, -) (AuditApi, error) { + messagingApi pkgMessagingApi.Api, + validator pkgAuditCommon.ProtobufValidator, +) (pkgAuditCommon.AuditApi, error) { if messagingApi == nil { - return nil, ErrMessagingApiNil + return nil, pkgAuditCommon.ErrMessagingApiNil } // Audit api - var auditApi AuditApi = &DynamicLegacyAuditApi{ + var auditApi pkgAuditCommon.AuditApi = &DynamicLegacyAuditApi{ messagingApi: messagingApi, tracer: otel.Tracer("dynamic-legacy-audit-api"), validator: validator, @@ -58,7 +60,7 @@ func (a *DynamicLegacyAuditApi) Log( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, ) error { cloudEvent, err := a.ValidateAndSerialize(ctx, event, visibility, routableIdentifier) @@ -75,21 +77,21 @@ func (a *DynamicLegacyAuditApi) ValidateAndSerialize( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, -) (*CloudEvent, error) { + routableIdentifier *pkgAuditCommon.RoutableIdentifier, +) (*pkgAuditCommon.CloudEvent, error) { ctx, span := a.tracer.Start(ctx, "validate-and-serialize") defer span.End() - routableEvent, err := validateAndSerializePartially(a.validator, event, visibility, routableIdentifier) + routableEvent, err := internalAuditApi.ValidateAndSerializePartially(a.validator, event, visibility, routableIdentifier) if err != nil { return nil, err } // Reject event type data-access as the downstream services // cannot handle it at the moment - if strings.HasSuffix(event.LogName, string(EventTypeDataAccess)) { - return nil, ErrUnsupportedEventTypeDataAccess + if strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeDataAccess)) { + return nil, pkgAuditCommon.ErrUnsupportedEventTypeDataAccess } // Do nothing with the serialized data in the legacy solution @@ -99,19 +101,19 @@ func (a *DynamicLegacyAuditApi) ValidateAndSerialize( } // Convert attributes - legacyBytes, err := convertAndSerializeIntoLegacyFormat(event, routableEvent) + legacyBytes, err := internalAuditApi.ConvertAndSerializeIntoLegacyFormat(event, routableEvent) if err != nil { return nil, err } - traceParent, traceState := TraceParentAndStateFromContext(ctx) + traceParent, traceState := internalAuditApi.TraceParentAndStateFromContext(ctx) - message := CloudEvent{ + message := pkgAuditCommon.CloudEvent{ SpecVersion: "1.0", Source: event.ProtoPayload.ServiceName, Id: event.InsertId, Time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), - DataContentType: ContentTypeCloudEventsJson, + DataContentType: pkgAuditCommon.ContentTypeCloudEventsJson, DataType: DataTypeLegacyAuditEventV1, Subject: event.ProtoPayload.ResourceName, Data: legacyBytes, @@ -126,8 +128,8 @@ func (a *DynamicLegacyAuditApi) ValidateAndSerialize( // Requires to have the topic name set as key "topic" in the context. func (a *DynamicLegacyAuditApi) Send( ctx context.Context, - routableIdentifier *RoutableIdentifier, - cloudEvent *CloudEvent, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, + cloudEvent *pkgAuditCommon.CloudEvent, ) error { rawTopicName := ctx.Value(ContextKeyTopic) @@ -138,20 +140,20 @@ func (a *DynamicLegacyAuditApi) Send( if topicName == "" { return ErrTopicNameEmpty } - if !TopicNamePattern.MatchString(topicName) { + if !pkgAuditCommon.TopicNamePattern.MatchString(topicName) { return fmt.Errorf("invalid topic name: %s - "+ "expected stackit-platform/t/swz/audit-log/{region}/{version}/{eventSource}/{additionalParts} "+ "where region is one of [conway, eu01, eu02, sx-stoi01], version is vX.Y, eventSource is the service name "+ "and additionalParts is a describing string the audit log relates to or 'events'", topicName) } - var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: topicName} + var topicNameResolver pkgAuditCommon.TopicNameResolver = &pkgAuditCommon.StaticTopicNameTestResolver{TopicName: topicName} if cloudEvent != nil && cloudEvent.TraceParent != nil && cloudEvent.TraceState != nil { - ctx = AddTraceParentAndStateToContext(ctx, *cloudEvent.TraceParent, *cloudEvent.TraceState) + ctx = internalAuditApi.AddTraceParentAndStateToContext(ctx, *cloudEvent.TraceParent, *cloudEvent.TraceState) } ctx, span := a.tracer.Start(ctx, "send") defer span.End() - return send(topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent) + return internalAuditApi.Send(topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent) } diff --git a/audit/api/api_legacy_dynamic_test.go b/pkg/audit/api/api_legacy_dynamic_test.go similarity index 82% rename from audit/api/api_legacy_dynamic_test.go rename to pkg/audit/api/api_legacy_dynamic_test.go index 44d0f05..67c4d53 100644 --- a/audit/api/api_legacy_dynamic_test.go +++ b/pkg/audit/api/api_legacy_dynamic_test.go @@ -4,18 +4,22 @@ import ( "context" "encoding/json" "errors" - "go.opentelemetry.io/otel" "net/url" "strings" "testing" "time" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - - "github.com/bufbuild/protovalidate-go" + "buf.build/go/protovalidate" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.opentelemetry.io/otel" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api" + pkgMessagingCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" + pkgMessagingTest "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/test" ) func TestDynamicLegacyAuditApi(t *testing.T) { @@ -25,14 +29,14 @@ func TestDynamicLegacyAuditApi(t *testing.T) { defer cancelFn() // Start solace docker container - solaceContainer, err := messaging.NewSolaceContainer(context.Background()) + solaceContainer, err := pkgMessagingTest.NewSolaceContainer(context.Background()) assert.NoError(t, err) defer solaceContainer.Stop() // Instantiate the messaging api - messagingApi, err := messaging.NewAmqpApi( - messaging.AmqpConnectionPoolConfig{ - Parameters: messaging.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, + amqpApi, err := pkgMessagingApi.NewAmqpApi( + pkgMessagingCommon.AmqpConnectionPoolConfig{ + Parameters: pkgMessagingCommon.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, PoolSize: 1, }) assert.NoError(t, err) @@ -58,14 +62,14 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) - event.LogName = strings.Replace(event.LogName, string(EventTypeAdminActivity), string(EventTypeDataAccess), 1) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) + event.LogName = strings.Replace(event.LogName, string(pkgAuditCommon.EventTypeAdminActivity), string(pkgAuditCommon.EventTypeDataAccess), 1) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -74,8 +78,8 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - NewRoutableIdentifier(objectIdentifier), - ), ErrUnsupportedEventTypeDataAccess) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), + ), pkgAuditCommon.ErrUnsupportedEventTypeDataAccess) }) // Check logging of organization events @@ -92,13 +96,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -107,7 +111,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -129,13 +133,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -144,7 +148,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -167,13 +171,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -182,7 +186,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -204,13 +208,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -219,7 +223,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -242,13 +246,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newProjectAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -257,7 +261,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -279,13 +283,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newProjectAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -294,7 +298,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -316,13 +320,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event := newProjectSystemAuditEvent(nil) + event := internalAuditApi.NewProjectSystemAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -332,7 +336,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - RoutableSystemIdentifier, + pkgAuditCommon.RoutableSystemIdentifier, )) // Receive the event from solace @@ -345,7 +349,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"]) // Check deserialized message - var auditEvent LegacyAuditEvent + var auditEvent internalAuditApi.LegacyAuditEvent assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) assert.Equal(t, event.ProtoPayload.ResourceName, *auditEvent.ResourceName) @@ -372,13 +376,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event := newSystemAuditEvent(nil) + event := internalAuditApi.NewSystemAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -388,7 +392,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - RoutableSystemIdentifier, + pkgAuditCommon.RoutableSystemIdentifier, )) // Receive the event from solace @@ -401,7 +405,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"]) // Check deserialized message - var auditEvent LegacyAuditEvent + var auditEvent internalAuditApi.LegacyAuditEvent assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) assert.Equal(t, event.ProtoPayload.OperationName, auditEvent.EventName) @@ -427,13 +431,13 @@ func TestDynamicLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewDynamicLegacyAuditApi( - messagingApi, + amqpApi, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) escapedQuery := url.QueryEscape("param=value") event.ProtoPayload.RequestMetadata.RequestAttributes.Query = &escapedQuery @@ -444,7 +448,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) { ctxWithTopic, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -465,15 +469,15 @@ func TestDynamicLegacyAuditApi_ValidateAndSerialize_ValidationFailed(t *testing. validator := &ProtobufValidatorMock{} validator.On("Validate", mock.Anything).Return(expectedError) - var protobufValidator ProtobufValidator = validator + var protobufValidator pkgAuditCommon.ProtobufValidator = validator auditApi := DynamicLegacyAuditApi{ tracer: otel.Tracer("test"), validator: protobufValidator, } - event := newSystemAuditEvent(nil) - _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) + event := internalAuditApi.NewSystemAuditEvent(nil) + _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) assert.ErrorIs(t, err, expectedError) } @@ -482,22 +486,22 @@ func TestDynamicLegacyAuditApi_Log_ValidationFailed(t *testing.T) { validator := &ProtobufValidatorMock{} validator.On("Validate", mock.Anything).Return(expectedError) - var protobufValidator ProtobufValidator = validator + var protobufValidator pkgAuditCommon.ProtobufValidator = validator auditApi := DynamicLegacyAuditApi{ tracer: otel.Tracer("test"), validator: protobufValidator, } - event := newSystemAuditEvent(nil) - err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) + event := internalAuditApi.NewSystemAuditEvent(nil) + err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) assert.ErrorIs(t, err, expectedError) } func TestDynamicLegacyAuditApi_Log_NilEvent(t *testing.T) { auditApi := DynamicLegacyAuditApi{tracer: otel.Tracer("test")} - err := auditApi.Log(context.Background(), nil, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) - assert.ErrorIs(t, err, ErrEventNil) + err := auditApi.Log(context.Background(), nil, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) + assert.ErrorIs(t, err, pkgAuditCommon.ErrEventNil) } func TestDynamicLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifierType(t *testing.T) { @@ -505,16 +509,16 @@ func TestDynamicLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectI objectIdentifier *auditV1.ObjectIdentifier) { objectIdentifier.Type = "invalid" } - event, objectIdentifier := newProjectAuditEvent(&customization) + event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(&customization) validator := &ProtobufValidatorMock{} validator.On("Validate", mock.Anything).Return(nil) - var protobufValidator ProtobufValidator = validator + var protobufValidator pkgAuditCommon.ProtobufValidator = validator auditApi := DynamicLegacyAuditApi{ tracer: otel.Tracer("test"), validator: protobufValidator, } - _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) - assert.ErrorIs(t, err, ErrUnsupportedRoutableType) + _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) + assert.ErrorIs(t, err, pkgAuditCommon.ErrUnsupportedRoutableType) } diff --git a/audit/api/api_legacy_test.go b/pkg/audit/api/api_legacy_test.go similarity index 82% rename from audit/api/api_legacy_test.go rename to pkg/audit/api/api_legacy_test.go index a430b3d..ed77433 100644 --- a/audit/api/api_legacy_test.go +++ b/pkg/audit/api/api_legacy_test.go @@ -5,19 +5,23 @@ import ( "encoding/json" "errors" "fmt" - "go.opentelemetry.io/otel" "net/url" "strings" "testing" "time" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - + "buf.build/go/protovalidate" "github.com/Azure/go-amqp" - "github.com/bufbuild/protovalidate-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.opentelemetry.io/otel" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api" + pkgMessagingCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" + pkgMessagingTest "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/test" ) func TestLegacyAuditApi(t *testing.T) { @@ -27,13 +31,13 @@ func TestLegacyAuditApi(t *testing.T) { defer cancelFn() // Start solace docker container - solaceContainer, err := messaging.NewSolaceContainer(context.Background()) + solaceContainer, err := pkgMessagingTest.NewSolaceContainer(context.Background()) assert.NoError(t, err) defer solaceContainer.Stop() // Instantiate the messaging api - messagingApi, err := messaging.NewAmqpApi(messaging.AmqpConnectionPoolConfig{ - Parameters: messaging.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, + amqpApi, err := pkgMessagingApi.NewAmqpApi(pkgMessagingCommon.AmqpConnectionPoolConfig{ + Parameters: pkgMessagingCommon.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, PoolSize: 1, }) assert.NoError(t, err) @@ -59,23 +63,23 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) - event.LogName = strings.Replace(event.LogName, string(EventTypeAdminActivity), string(EventTypeDataAccess), 1) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) + event.LogName = strings.Replace(event.LogName, string(pkgAuditCommon.EventTypeAdminActivity), string(pkgAuditCommon.EventTypeDataAccess), 1) // Log the event to solace assert.ErrorIs(t, auditApi.Log( ctx, event, auditV1.Visibility_VISIBILITY_PUBLIC, - NewRoutableIdentifier(objectIdentifier), - ), ErrUnsupportedEventTypeDataAccess) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), + ), pkgAuditCommon.ErrUnsupportedEventTypeDataAccess) }) // Check logging of organization events @@ -92,14 +96,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -107,7 +111,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -129,14 +133,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -144,7 +148,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -167,14 +171,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -182,7 +186,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -204,14 +208,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -219,7 +223,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -242,14 +246,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newProjectAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -257,7 +261,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -279,14 +283,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newProjectAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -294,7 +298,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -316,14 +320,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event := newProjectSystemAuditEvent(nil) + event := internalAuditApi.NewProjectSystemAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -332,7 +336,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - RoutableSystemIdentifier, + pkgAuditCommon.RoutableSystemIdentifier, )) // Receive the event from solace @@ -345,7 +349,7 @@ func TestLegacyAuditApi(t *testing.T) { assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"]) // Check deserialized message - var auditEvent LegacyAuditEvent + var auditEvent internalAuditApi.LegacyAuditEvent assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) assert.Equal(t, event.ProtoPayload.ResourceName, *auditEvent.ResourceName) @@ -372,14 +376,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event := newSystemAuditEvent(nil) + event := internalAuditApi.NewSystemAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -388,7 +392,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - RoutableSystemIdentifier, + pkgAuditCommon.RoutableSystemIdentifier, )) // Receive the event from solace @@ -401,7 +405,7 @@ func TestLegacyAuditApi(t *testing.T) { assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"]) // Check deserialized message - var auditEvent LegacyAuditEvent + var auditEvent internalAuditApi.LegacyAuditEvent assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) assert.Equal(t, event.ProtoPayload.OperationName, auditEvent.EventName) @@ -427,14 +431,14 @@ func TestLegacyAuditApi(t *testing.T) { // Instantiate audit api auditApi, err := NewLegacyAuditApi( - messagingApi, - LegacyTopicNameConfig{TopicName: topicName}, + amqpApi, + StaticTopicNameConfig{TopicName: topicName}, validator, ) assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) escapedQuery := url.QueryEscape("param=value") event.ProtoPayload.RequestMetadata.RequestAttributes.Query = &escapedQuery @@ -444,7 +448,7 @@ func TestLegacyAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier), + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier), )) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -467,7 +471,7 @@ func validateSentMessage( assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"]) // Check deserialized message - var auditEvent LegacyAuditEvent + var auditEvent internalAuditApi.LegacyAuditEvent assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) var severity string @@ -513,13 +517,13 @@ func validateSentMessageWithDetails( // Check topic name assert.Equal(t, topicName, *message.Properties.To) - assert.Equal(t, ContentTypeCloudEventsJson, message.ApplicationProperties["cloudEvents:datacontenttype"]) + assert.Equal(t, pkgAuditCommon.ContentTypeCloudEventsJson, message.ApplicationProperties["cloudEvents:datacontenttype"]) assert.Equal(t, DataTypeLegacyAuditEventV1, message.ApplicationProperties["cloudEvents:type"]) assert.Equal(t, "", message.ApplicationProperties["cloudEvents:traceparent"]) assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"]) // Check deserialized message - var auditEvent LegacyAuditEvent + var auditEvent internalAuditApi.LegacyAuditEvent assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) assert.Equal(t, event.ProtoPayload.OperationName, auditEvent.EventName) @@ -570,20 +574,20 @@ func validateSentMessageWithDetails( func TestLegacyAuditApi_NewLegacyAuditApi(t *testing.T) { t.Run("messaging api nil", func(t *testing.T) { - auditApi, err := NewLegacyAuditApi(nil, LegacyTopicNameConfig{}, nil) + auditApi, err := NewLegacyAuditApi(nil, StaticTopicNameConfig{}, nil) assert.Nil(t, auditApi) assert.EqualError(t, err, "messaging api nil") }) t.Run("topic name is blank", func(t *testing.T) { // Start solace docker container - solaceContainer, err := messaging.NewSolaceContainer(context.Background()) + solaceContainer, err := pkgMessagingTest.NewSolaceContainer(context.Background()) assert.NoError(t, err) defer solaceContainer.Stop() // Instantiate the messaging api - messagingApi, err := messaging.NewAmqpApi(messaging.AmqpConnectionPoolConfig{ - Parameters: messaging.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, + amqpApi, err := pkgMessagingApi.NewAmqpApi(pkgMessagingCommon.AmqpConnectionPoolConfig{ + Parameters: pkgMessagingCommon.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, PoolSize: 1, }) assert.NoError(t, err) @@ -592,7 +596,7 @@ func TestLegacyAuditApi_NewLegacyAuditApi(t *testing.T) { validator, err := protovalidate.New() assert.NoError(t, err) - auditApi, err := NewLegacyAuditApi(messagingApi, LegacyTopicNameConfig{ + auditApi, err := NewLegacyAuditApi(amqpApi, StaticTopicNameConfig{ TopicName: "", }, validator) @@ -606,15 +610,15 @@ func TestLegacyAuditApi_ValidateAndSerialize_ValidationFailed(t *testing.T) { validator := &ProtobufValidatorMock{} validator.On("Validate", mock.Anything).Return(expectedError) - var protobufValidator ProtobufValidator = validator + var protobufValidator pkgAuditCommon.ProtobufValidator = validator auditApi := LegacyAuditApi{ tracer: otel.Tracer("test"), validator: protobufValidator, } - event := newSystemAuditEvent(nil) - _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) + event := internalAuditApi.NewSystemAuditEvent(nil) + _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) assert.ErrorIs(t, err, expectedError) } @@ -623,22 +627,22 @@ func TestLegacyAuditApi_Log_ValidationFailed(t *testing.T) { validator := &ProtobufValidatorMock{} validator.On("Validate", mock.Anything).Return(expectedError) - var protobufValidator ProtobufValidator = validator + var protobufValidator pkgAuditCommon.ProtobufValidator = validator auditApi := LegacyAuditApi{ tracer: otel.Tracer("test"), validator: protobufValidator, } - event := newSystemAuditEvent(nil) - err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) + event := internalAuditApi.NewSystemAuditEvent(nil) + err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) assert.ErrorIs(t, err, expectedError) } func TestLegacyAuditApi_Log_NilEvent(t *testing.T) { auditApi := LegacyAuditApi{tracer: otel.Tracer("test")} - err := auditApi.Log(context.Background(), nil, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) - assert.ErrorIs(t, err, ErrEventNil) + err := auditApi.Log(context.Background(), nil, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) + assert.ErrorIs(t, err, pkgAuditCommon.ErrEventNil) } func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifierType(t *testing.T) { @@ -646,16 +650,16 @@ func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifi objectIdentifier *auditV1.ObjectIdentifier) { objectIdentifier.Type = "invalid" } - event, objectIdentifier := newProjectAuditEvent(&customization) + event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(&customization) validator := &ProtobufValidatorMock{} validator.On("Validate", mock.Anything).Return(nil) - var protobufValidator ProtobufValidator = validator + var protobufValidator pkgAuditCommon.ProtobufValidator = validator auditApi := LegacyAuditApi{ tracer: otel.Tracer("test"), validator: protobufValidator, } - _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier)) - assert.ErrorIs(t, err, ErrUnsupportedRoutableType) + _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)) + assert.ErrorIs(t, err, pkgAuditCommon.ErrUnsupportedRoutableType) } diff --git a/audit/api/api_mock.go b/pkg/audit/api/api_mock.go similarity index 64% rename from audit/api/api_mock.go rename to pkg/audit/api/api_mock.go index 28dd2c0..14016e1 100644 --- a/audit/api/api_mock.go +++ b/pkg/audit/api/api_mock.go @@ -3,30 +3,31 @@ package api import ( "context" "fmt" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/trace" "strings" + "buf.build/go/protovalidate" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" "google.golang.org/protobuf/proto" auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - - "github.com/bufbuild/protovalidate-go" + internalApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) // MockAuditApi is an implementation of AuditApi that does nothing and has no dependency to external systems. type MockAuditApi struct { tracer trace.Tracer - validator ProtobufValidator + validator pkgAuditCommon.ProtobufValidator } -func NewMockAuditApi() (AuditApi, error) { +func NewMockAuditApi() (pkgAuditCommon.AuditApi, error) { validator, err := protovalidate.New() if err != nil { return nil, err } - var protobufValidator ProtobufValidator = validator - var auditApi AuditApi = &MockAuditApi{ + var protobufValidator pkgAuditCommon.ProtobufValidator = validator + var auditApi pkgAuditCommon.AuditApi = &MockAuditApi{ tracer: otel.Tracer("mock-audit-api"), validator: protobufValidator, } @@ -39,7 +40,7 @@ func (a *MockAuditApi) Log( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, ) error { _, err := a.ValidateAndSerialize(ctx, event, visibility, routableIdentifier) @@ -51,21 +52,21 @@ func (a *MockAuditApi) ValidateAndSerialize( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, -) (*CloudEvent, error) { + routableIdentifier *pkgAuditCommon.RoutableIdentifier, +) (*pkgAuditCommon.CloudEvent, error) { ctx, span := a.tracer.Start(ctx, "validate-and-serialize") defer span.End() - routableEvent, err := validateAndSerializePartially(a.validator, event, visibility, routableIdentifier) + routableEvent, err := internalApi.ValidateAndSerializePartially(a.validator, event, visibility, routableIdentifier) if err != nil { return nil, err } // Reject event type data-access as the downstream services // cannot handle it at the moment - if strings.HasSuffix(event.LogName, string(EventTypeDataAccess)) { - return nil, ErrUnsupportedEventTypeDataAccess + if strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeDataAccess)) { + return nil, pkgAuditCommon.ErrUnsupportedEventTypeDataAccess } routableEventBytes, err := proto.Marshal(routableEvent) @@ -73,9 +74,9 @@ func (a *MockAuditApi) ValidateAndSerialize( return nil, err } - traceParent, traceState := TraceParentAndStateFromContext(ctx) + traceParent, traceState := internalApi.TraceParentAndStateFromContext(ctx) - message := CloudEvent{ + message := pkgAuditCommon.CloudEvent{ SpecVersion: "1.0", Source: event.ProtoPayload.ServiceName, Id: event.InsertId, @@ -92,6 +93,6 @@ func (a *MockAuditApi) ValidateAndSerialize( } // Send implements AuditApi.Send -func (a *MockAuditApi) Send(context.Context, *RoutableIdentifier, *CloudEvent) error { +func (a *MockAuditApi) Send(context.Context, *pkgAuditCommon.RoutableIdentifier, *pkgAuditCommon.CloudEvent) error { return nil } diff --git a/audit/api/api_mock_test.go b/pkg/audit/api/api_mock_test.go similarity index 54% rename from audit/api/api_mock_test.go rename to pkg/audit/api/api_mock_test.go index f69337b..493bfe9 100644 --- a/audit/api/api_mock_test.go +++ b/pkg/audit/api/api_mock_test.go @@ -5,19 +5,38 @@ import ( "strings" "testing" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - + "buf.build/go/protovalidate" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "google.golang.org/protobuf/proto" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) +type ProtobufValidatorMock struct { + mock.Mock +} + +func (m *ProtobufValidatorMock) Validate(msg proto.Message, options ...protovalidate.ValidationOption) error { + var args mock.Arguments + if len(options) > 0 { + args = m.Called(msg, options) + } else { + args = m.Called(msg) + } + return args.Error(0) +} + func TestMockAuditApi_Log(t *testing.T) { auditApi, err := NewMockAuditApi() assert.NoError(t, err) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) - routableObjectIdentifier := NewRoutableIdentifier(objectIdentifier) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) + routableObjectIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier) // Test t.Run("Log", func(t *testing.T) { @@ -26,13 +45,13 @@ func TestMockAuditApi_Log(t *testing.T) { }) t.Run("reject data access event", func(t *testing.T) { - orgEvent, objIdentifier := newOrganizationAuditEvent(nil) - orgEvent.LogName = strings.Replace(orgEvent.LogName, string(EventTypeAdminActivity), string(EventTypeDataAccess), 1) - rtIdentifier := NewRoutableIdentifier(objIdentifier) + orgEvent, objIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) + orgEvent.LogName = strings.Replace(orgEvent.LogName, string(pkgAuditCommon.EventTypeAdminActivity), string(pkgAuditCommon.EventTypeDataAccess), 1) + rtIdentifier := pkgAuditCommon.NewRoutableIdentifier(objIdentifier) assert.ErrorIs(t, auditApi.Log( context.Background(), orgEvent, auditV1.Visibility_VISIBILITY_PUBLIC, rtIdentifier), - ErrUnsupportedEventTypeDataAccess) + pkgAuditCommon.ErrUnsupportedEventTypeDataAccess) }) t.Run("ValidateAndSerialize", func(t *testing.T) { @@ -50,11 +69,11 @@ func TestMockAuditApi_Log(t *testing.T) { visibility := auditV1.Visibility_VISIBILITY_PUBLIC _, err := auditApi.ValidateAndSerialize(context.Background(), nil, visibility, routableObjectIdentifier) - assert.ErrorIs(t, err, ErrEventNil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrEventNil) }) t.Run("Send", func(t *testing.T) { - var cloudEvent = CloudEvent{} + var cloudEvent = pkgAuditCommon.CloudEvent{} assert.Nil(t, auditApi.Send(context.Background(), routableObjectIdentifier, &cloudEvent)) }) diff --git a/audit/api/api_routable.go b/pkg/audit/api/api_routable.go similarity index 69% rename from audit/api/api_routable.go rename to pkg/audit/api/api_routable.go index 07d91c4..e99529d 100644 --- a/audit/api/api_routable.go +++ b/pkg/audit/api/api_routable.go @@ -4,14 +4,16 @@ import ( "context" "errors" "fmt" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/trace" "strings" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" "google.golang.org/protobuf/proto" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging" auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api" ) // routableTopicNameResolver implements TopicNameResolver. @@ -25,23 +27,23 @@ type routableTopicNameResolver struct { } // Resolve implements TopicNameResolver.Resolve. -func (r *routableTopicNameResolver) Resolve(routableIdentifier *RoutableIdentifier) (string, error) { +func (r *routableTopicNameResolver) Resolve(routableIdentifier *pkgAuditCommon.RoutableIdentifier) (string, error) { if routableIdentifier == nil { - return "", ErrObjectIdentifierNil + return "", pkgAuditCommon.ErrObjectIdentifierNil } switch routableIdentifier.Type { - case ObjectTypeOrganization: + case pkgAuditCommon.ObjectTypeOrganization: return fmt.Sprintf("topic://%s/%s", r.organizationTopicPrefix, routableIdentifier.Identifier), nil - case ObjectTypeProject: + case pkgAuditCommon.ObjectTypeProject: return fmt.Sprintf("topic://%s/%s", r.projectTopicPrefix, routableIdentifier.Identifier), nil - case ObjectTypeFolder: + case pkgAuditCommon.ObjectTypeFolder: return fmt.Sprintf("topic://%s/%s", r.folderTopicPrefix, routableIdentifier.Identifier), nil - case ObjectTypeSystem: + case pkgAuditCommon.ObjectTypeSystem: return r.systemTopicName, nil default: - return "", ErrUnsupportedObjectIdentifierType + return "", pkgAuditCommon.ErrUnsupportedObjectIdentifierType } } @@ -57,18 +59,18 @@ type topicNameConfig struct { // Warning: It is only there for local (compatibility) testing. // DO NOT USE IT! type routableAuditApi struct { - messagingApi messaging.Api - topicNameResolver TopicNameResolver + messagingApi pkgMessagingApi.Api + topicNameResolver pkgAuditCommon.TopicNameResolver tracer trace.Tracer - validator ProtobufValidator + validator pkgAuditCommon.ProtobufValidator } // NewRoutableAuditApi can be used to initialize the audit log api. func newRoutableAuditApi( - messagingApi messaging.Api, + messagingApi pkgMessagingApi.Api, topicNameConfig topicNameConfig, - validator ProtobufValidator, -) (AuditApi, error) { + validator pkgAuditCommon.ProtobufValidator, +) (pkgAuditCommon.AuditApi, error) { if messagingApi == nil { return nil, errors.New("messaging api nil") @@ -88,7 +90,7 @@ func newRoutableAuditApi( return nil, errors.New("system topic name is required") } - var topicNameResolver TopicNameResolver = &routableTopicNameResolver{ + var topicNameResolver pkgAuditCommon.TopicNameResolver = &routableTopicNameResolver{ folderTopicPrefix: topicNameConfig.FolderTopicPrefix, organizationTopicPrefix: topicNameConfig.OrganizationTopicPrefix, projectTopicPrefix: topicNameConfig.ProjectTopicPrefix, @@ -96,7 +98,7 @@ func newRoutableAuditApi( } // Audit api - var auditApi AuditApi = &routableAuditApi{ + var auditApi pkgAuditCommon.AuditApi = &routableAuditApi{ messagingApi: messagingApi, topicNameResolver: topicNameResolver, tracer: otel.Tracer("routable-audit-api"), @@ -111,7 +113,7 @@ func (a *routableAuditApi) Log( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, ) error { cloudEvent, err := a.ValidateAndSerialize(ctx, event, visibility, routableIdentifier) @@ -127,13 +129,13 @@ func (a *routableAuditApi) ValidateAndSerialize( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, - routableIdentifier *RoutableIdentifier, -) (*CloudEvent, error) { + routableIdentifier *pkgAuditCommon.RoutableIdentifier, +) (*pkgAuditCommon.CloudEvent, error) { ctx, span := a.tracer.Start(ctx, "validate-and-serialize") defer span.End() - routableEvent, err := validateAndSerializePartially( + routableEvent, err := internalAuditApi.ValidateAndSerializePartially( a.validator, event, visibility, @@ -145,8 +147,8 @@ func (a *routableAuditApi) ValidateAndSerialize( // Reject event type data-access as the downstream services // cannot handle it at the moment - if strings.HasSuffix(event.LogName, string(EventTypeDataAccess)) { - return nil, ErrUnsupportedEventTypeDataAccess + if strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeDataAccess)) { + return nil, pkgAuditCommon.ErrUnsupportedEventTypeDataAccess } routableEventBytes, err := proto.Marshal(routableEvent) @@ -154,14 +156,14 @@ func (a *routableAuditApi) ValidateAndSerialize( return nil, err } - traceParent, traceState := TraceParentAndStateFromContext(ctx) + traceParent, traceState := internalAuditApi.TraceParentAndStateFromContext(ctx) - message := CloudEvent{ + message := pkgAuditCommon.CloudEvent{ SpecVersion: "1.0", Source: event.ProtoPayload.ServiceName, Id: event.InsertId, Time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), - DataContentType: ContentTypeCloudEventsProtobuf, + DataContentType: pkgAuditCommon.ContentTypeCloudEventsProtobuf, DataType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()), Subject: event.ProtoPayload.ResourceName, Data: routableEventBytes, @@ -175,15 +177,15 @@ func (a *routableAuditApi) ValidateAndSerialize( // Send implements AuditApi.Send func (a *routableAuditApi) Send( ctx context.Context, - routableIdentifier *RoutableIdentifier, - cloudEvent *CloudEvent, + routableIdentifier *pkgAuditCommon.RoutableIdentifier, + cloudEvent *pkgAuditCommon.CloudEvent, ) error { if cloudEvent != nil && cloudEvent.TraceParent != nil && cloudEvent.TraceState != nil { - ctx = AddTraceParentAndStateToContext(ctx, *cloudEvent.TraceParent, *cloudEvent.TraceState) + ctx = internalAuditApi.AddTraceParentAndStateToContext(ctx, *cloudEvent.TraceParent, *cloudEvent.TraceState) } ctx, span := a.tracer.Start(ctx, "send") defer span.End() - return send(a.topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent) + return internalAuditApi.Send(a.topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent) } diff --git a/audit/api/api_routable_test.go b/pkg/audit/api/api_routable_test.go similarity index 82% rename from audit/api/api_routable_test.go rename to pkg/audit/api/api_routable_test.go index 78048e0..6bac6ca 100644 --- a/audit/api/api_routable_test.go +++ b/pkg/audit/api/api_routable_test.go @@ -4,21 +4,24 @@ import ( "context" "errors" "fmt" - "go.opentelemetry.io/otel" "strings" "testing" "time" - "github.com/google/uuid" - - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - + "buf.build/go/protovalidate" "github.com/Azure/go-amqp" - "github.com/bufbuild/protovalidate-go" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "go.opentelemetry.io/otel" "google.golang.org/protobuf/proto" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api" + pgkMessagingCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" + pkgMessagingTest "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/test" ) func TestRoutableAuditApi(t *testing.T) { @@ -28,13 +31,13 @@ func TestRoutableAuditApi(t *testing.T) { defer cancelFn() // Start solace docker container - solaceContainer, err := messaging.NewSolaceContainer(context.Background()) + solaceContainer, err := pkgMessagingTest.NewSolaceContainer(context.Background()) assert.NoError(t, err) defer solaceContainer.Stop() // Instantiate the messaging api - messagingApi, err := messaging.NewAmqpApi(messaging.AmqpConnectionPoolConfig{ - Parameters: messaging.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, + amqpApi, err := pkgMessagingApi.NewAmqpApi(pgkMessagingCommon.AmqpConnectionPoolConfig{ + Parameters: pgkMessagingCommon.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, PoolSize: 1, }) assert.NoError(t, err) @@ -50,7 +53,7 @@ func TestRoutableAuditApi(t *testing.T) { systemTopicName := "topic://system/admin-events" auditApi, err := newRoutableAuditApi( - messagingApi, + amqpApi, topicNameConfig{ FolderTopicPrefix: folderTopicPrefix, OrganizationTopicPrefix: organizationTopicPrefix, @@ -71,8 +74,8 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "org/*")) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) - event.LogName = strings.Replace(event.LogName, string(EventTypeAdminActivity), string(EventTypeDataAccess), 1) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) + event.LogName = strings.Replace(event.LogName, string(pkgAuditCommon.EventTypeAdminActivity), string(pkgAuditCommon.EventTypeDataAccess), 1) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -80,7 +83,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier)), ErrUnsupportedEventTypeDataAccess) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)), pkgAuditCommon.ErrUnsupportedEventTypeDataAccess) }) // Check logging of organization events @@ -93,7 +96,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "org/*")) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -101,7 +104,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier))) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) @@ -124,7 +127,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "org/*")) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) topicName := fmt.Sprintf("org/%s", objectIdentifier.Identifier) assert.NoError( t, @@ -137,7 +140,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier))) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))) // Receive the event from solace message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -163,7 +166,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "folder/*")) // Instantiate test data - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -171,7 +174,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier))) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) @@ -194,7 +197,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "folder/*")) // Instantiate test data - event, objectIdentifier := newFolderAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil) topicName := fmt.Sprintf("folder/%s", objectIdentifier.Identifier) assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicName)) @@ -205,7 +208,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier))) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))) // Receive the event from solace message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -230,7 +233,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "project/*")) // Instantiate test data - event, objectIdentifier := newProjectAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -239,7 +242,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier))) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))) // Receive the event from solace message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -263,7 +266,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "project/*")) // Instantiate test data - event, objectIdentifier := newProjectAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -272,7 +275,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier))) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))) // Receive the event from solace message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) @@ -297,7 +300,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "system/*")) // Instantiate test data - event := newProjectSystemAuditEvent(nil) + event := internalAuditApi.NewProjectSystemAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -306,7 +309,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - RoutableSystemIdentifier, + pkgAuditCommon.RoutableSystemIdentifier, )) // Receive the event from solace @@ -332,7 +335,7 @@ func TestRoutableAuditApi(t *testing.T) { validateRoutableEventPayload( t, message.Data[0], - RoutableSystemIdentifier.ToObjectIdentifier(), + pkgAuditCommon.RoutableSystemIdentifier.ToObjectIdentifier(), event, "stackit.resourcemanager.v2.system.changed", visibility) @@ -347,7 +350,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "system/*")) // Instantiate test data - event := newSystemAuditEvent(nil) + event := internalAuditApi.NewSystemAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -356,7 +359,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - RoutableSystemIdentifier, + pkgAuditCommon.RoutableSystemIdentifier, )) // Receive the event from solace @@ -382,7 +385,7 @@ func TestRoutableAuditApi(t *testing.T) { validateRoutableEventPayload( t, message.Data[0], - SystemIdentifier, + pkgAuditCommon.SystemIdentifier, event, "stackit.resourcemanager.v2.system.changed", visibility) @@ -398,7 +401,7 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "org/*")) // Instantiate test data - event, objectIdentifier := newOrganizationAuditEvent(nil) + event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PUBLIC @@ -406,7 +409,7 @@ func TestRoutableAuditApi(t *testing.T) { ctx, event, visibility, - NewRoutableIdentifier(objectIdentifier))) + pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))) message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) @@ -444,7 +447,7 @@ func validateSentEvent( _, isUuid := uuid.Parse(fmt.Sprintf("%s", applicationProperties["cloudEvents:id"])) assert.True(t, true, isUuid) assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().UnixMilli(), applicationProperties["cloudEvents:time"]) - assert.Equal(t, ContentTypeCloudEventsProtobuf, applicationProperties["cloudEvents:datacontenttype"]) + assert.Equal(t, pkgAuditCommon.ContentTypeCloudEventsProtobuf, applicationProperties["cloudEvents:datacontenttype"]) assert.Equal(t, "audit.v1.RoutableAuditEvent", applicationProperties["cloudEvents:type"]) assert.Equal(t, "", applicationProperties["cloudEvents:traceparent"]) assert.Equal(t, "", applicationProperties["cloudEvents:tracestate"]) @@ -486,8 +489,8 @@ func validateRoutableEventPayload( func TestRoutableTopicNameResolver_Resolve_UnsupportedIdentifierType(t *testing.T) { resolver := routableTopicNameResolver{} - _, err := resolver.Resolve(NewRoutableIdentifier(&auditV1.ObjectIdentifier{Type: "unsupported"})) - assert.ErrorIs(t, err, ErrUnsupportedObjectIdentifierType) + _, err := resolver.Resolve(pkgAuditCommon.NewRoutableIdentifier(&auditV1.ObjectIdentifier{Type: "unsupported"})) + assert.ErrorIs(t, err, pkgAuditCommon.ErrUnsupportedObjectIdentifierType) } func TestNewRoutableAuditApi_NewRoutableAuditApi_MessagingApiNil(t *testing.T) { @@ -501,15 +504,15 @@ func TestRoutableAuditApi_ValidateAndSerialize_ValidationFailed(t *testing.T) { validator := &ProtobufValidatorMock{} validator.On("Validate", mock.Anything).Return(expectedError) - var protobufValidator ProtobufValidator = validator + var protobufValidator pkgAuditCommon.ProtobufValidator = validator auditApi := routableAuditApi{ tracer: otel.Tracer("test"), validator: protobufValidator, } - event := newSystemAuditEvent(nil) - _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) + event := internalAuditApi.NewSystemAuditEvent(nil) + _, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) assert.ErrorIs(t, err, expectedError) } @@ -518,20 +521,20 @@ func TestRoutableAuditApi_Log_ValidationFailed(t *testing.T) { validator := &ProtobufValidatorMock{} validator.On("Validate", mock.Anything).Return(expectedError) - var protobufValidator ProtobufValidator = validator + var protobufValidator pkgAuditCommon.ProtobufValidator = validator auditApi := routableAuditApi{ tracer: otel.Tracer("test"), validator: protobufValidator, } - event := newSystemAuditEvent(nil) - err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) + event := internalAuditApi.NewSystemAuditEvent(nil) + err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) assert.ErrorIs(t, err, expectedError) } func TestRoutableAuditApi_Log_NilEvent(t *testing.T) { auditApi := routableAuditApi{tracer: otel.Tracer("test")} - err := auditApi.Log(context.Background(), nil, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier) - assert.ErrorIs(t, err, ErrEventNil) + err := auditApi.Log(context.Background(), nil, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.RoutableSystemIdentifier) + assert.ErrorIs(t, err, pkgAuditCommon.ErrEventNil) } diff --git a/audit/api/base64.go b/pkg/audit/api/base64.go similarity index 72% rename from audit/api/base64.go rename to pkg/audit/api/base64.go index c0bf93e..f2f3184 100644 --- a/audit/api/base64.go +++ b/pkg/audit/api/base64.go @@ -5,6 +5,8 @@ import ( "encoding/json" "errors" "strings" + + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) const base64AuditEventV1 = "v1" @@ -14,16 +16,16 @@ var ErrRoutableIdentifierNil = errors.New("routableIdentifier must not be nil") var ErrUnsupportedBase64StringVersion = errors.New("unsupported base64 cloud event string version") type serializableEvent struct { - CloudEvent CloudEvent `json:"cloudEvent"` - RoutableIdentifier RoutableIdentifier `json:"routableIdentifier"` + CloudEvent pkgAuditCommon.CloudEvent `json:"cloudEvent"` + RoutableIdentifier pkgAuditCommon.RoutableIdentifier `json:"routableIdentifier"` } func ToBase64( - cloudEvent *CloudEvent, - routableIdentifier *RoutableIdentifier) (*string, error) { + cloudEvent *pkgAuditCommon.CloudEvent, + routableIdentifier *pkgAuditCommon.RoutableIdentifier) (*string, error) { if cloudEvent == nil { - return nil, ErrCloudEventNil + return nil, pkgAuditCommon.ErrCloudEventNil } if routableIdentifier == nil { @@ -45,7 +47,7 @@ func ToBase64( return &base64Str, nil } -func FromBase64(base64Str string) (*CloudEvent, *RoutableIdentifier, error) { +func FromBase64(base64Str string) (*pkgAuditCommon.CloudEvent, *pkgAuditCommon.RoutableIdentifier, error) { if base64Str == "" { return nil, nil, ErrBase64StringEmpty } diff --git a/audit/api/base64_test.go b/pkg/audit/api/base64_test.go similarity index 79% rename from audit/api/base64_test.go rename to pkg/audit/api/base64_test.go index 321225f..926e27b 100644 --- a/audit/api/base64_test.go +++ b/pkg/audit/api/base64_test.go @@ -2,24 +2,27 @@ package api import ( "encoding/base64" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" + + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" ) func Test_ToBase64(t *testing.T) { t.Run("cloud event nil", func(t *testing.T) { - var cloudEvent *CloudEvent = nil - routableIdentifier := RoutableSystemIdentifier + var cloudEvent *pkgAuditCommon.CloudEvent = nil + routableIdentifier := pkgAuditCommon.RoutableSystemIdentifier base64str, err := ToBase64(cloudEvent, routableIdentifier) - assert.ErrorIs(t, err, ErrCloudEventNil) + assert.ErrorIs(t, err, pkgAuditCommon.ErrCloudEventNil) assert.Nil(t, base64str) }) t.Run("routable identifier nil", func(t *testing.T) { - cloudEvent := &CloudEvent{} - var routableIdentifier *RoutableIdentifier = nil + cloudEvent := &pkgAuditCommon.CloudEvent{} + var routableIdentifier *pkgAuditCommon.RoutableIdentifier = nil base64str, err := ToBase64(cloudEvent, routableIdentifier) assert.ErrorIs(t, err, ErrRoutableIdentifierNil) @@ -27,8 +30,8 @@ func Test_ToBase64(t *testing.T) { }) t.Run("encoded event", func(t *testing.T) { - e := &CloudEvent{} - r := RoutableSystemIdentifier + e := &pkgAuditCommon.CloudEvent{} + r := pkgAuditCommon.RoutableSystemIdentifier base64str, err := ToBase64(e, r) assert.NoError(t, err) @@ -72,8 +75,8 @@ func Test_FromBase64(t *testing.T) { }) t.Run("decoded event", func(t *testing.T) { - e := &CloudEvent{} - r := RoutableSystemIdentifier + e := &pkgAuditCommon.CloudEvent{} + r := pkgAuditCommon.RoutableSystemIdentifier base64str, err := ToBase64(e, r) assert.NoError(t, err) diff --git a/audit/api/builder.go b/pkg/audit/api/builder.go similarity index 89% rename from audit/api/builder.go rename to pkg/audit/api/builder.go index a71fdaa..81c5a88 100644 --- a/audit/api/builder.go +++ b/pkg/audit/api/builder.go @@ -2,14 +2,18 @@ package api import ( "context" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/utils" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" "errors" "fmt" + "time" + "github.com/google/uuid" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" - "time" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgAuditUtils "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/utils" ) const quadZero = "0.0.0.0" @@ -22,7 +26,7 @@ type AuditParameters struct { Details map[string]interface{} // The type of the event - EventType EventType + EventType pkgAuditCommon.EventType // A set of user-defined (key, value) data that provides additional // information about the log entry. @@ -32,7 +36,7 @@ type AuditParameters struct { ObjectId string // Type of the object, the audit event refers to - ObjectType ObjectType + ObjectType pkgAuditCommon.ObjectType ResponseBody any @@ -42,14 +46,14 @@ type AuditParameters struct { func getObjectIdAndTypeFromAuditParams( auditParams *AuditParameters, -) (string, *ObjectType, error) { +) (string, *pkgAuditCommon.ObjectType, error) { objectId := auditParams.ObjectId if objectId == "" { return "", nil, errors.New("object id missing") } - var objectType *ObjectType + var objectType *pkgAuditCommon.ObjectType if auditParams.ObjectType != "" { objectType = &auditParams.ObjectType } @@ -66,9 +70,9 @@ func getObjectIdAndTypeFromAuditParams( // AuditLogEntryBuilder collects audit params to construct auditV1.AuditLogEntry type AuditLogEntryBuilder struct { auditParams AuditParameters - auditRequest AuditRequest - auditResponse AuditResponse - auditMetadata AuditMetadata + auditRequest internalAuditApi.AuditRequest + auditResponse internalAuditApi.AuditResponse + auditMetadata internalAuditApi.AuditMetadata // Region and optional zone id. If both, separated with a - (dash). // Example: eu01 @@ -88,23 +92,23 @@ func NewAuditLogEntryBuilder() *AuditLogEntryBuilder { return &AuditLogEntryBuilder{ auditParams: AuditParameters{ - EventType: EventTypeAdminActivity, + EventType: pkgAuditCommon.EventTypeAdminActivity, }, - auditRequest: AuditRequest{ - Request: &ApiRequest{}, + auditRequest: internalAuditApi.AuditRequest{ + Request: &pkgAuditCommon.ApiRequest{}, RequestClientIP: quadZero, RequestCorrelationId: nil, RequestId: nil, RequestTime: &requestTime, }, - auditResponse: AuditResponse{ + auditResponse: internalAuditApi.AuditResponse{ ResponseBodyBytes: nil, ResponseStatusCode: 200, ResponseHeaders: make(map[string][]string), ResponseNumItems: nil, ResponseTime: nil, }, - auditMetadata: AuditMetadata{ + auditMetadata: internalAuditApi.AuditMetadata{ AuditInsertId: "", AuditLabels: nil, AuditLogName: "", @@ -124,7 +128,7 @@ func NewAuditLogEntryBuilder() *AuditLogEntryBuilder { func (builder *AuditLogEntryBuilder) AsSystemEvent() *AuditLogEntryBuilder { if builder.auditRequest.Request == nil { - builder.auditRequest.Request = &ApiRequest{} + builder.auditRequest.Request = &pkgAuditCommon.ApiRequest{} } if builder.auditRequest.Request.Header == nil { builder.auditRequest.Request.Header = map[string][]string{"user-agent": {"none"}} @@ -147,12 +151,12 @@ func (builder *AuditLogEntryBuilder) AsSystemEvent() *AuditLogEntryBuilder { if builder.auditRequest.RequestClientIP == "" { builder.auditRequest.RequestClientIP = quadZero } - builder.WithEventType(EventTypeSystemEvent) + builder.WithEventType(pkgAuditCommon.EventTypeSystemEvent) return builder } // WithRequiredApiRequest adds api request details -func (builder *AuditLogEntryBuilder) WithRequiredApiRequest(request ApiRequest) *AuditLogEntryBuilder { +func (builder *AuditLogEntryBuilder) WithRequiredApiRequest(request pkgAuditCommon.ApiRequest) *AuditLogEntryBuilder { builder.auditRequest.Request = &request return builder } @@ -209,7 +213,7 @@ func (builder *AuditLogEntryBuilder) WithRequiredObjectId(objectId string) *Audi // WithRequiredObjectType adds the object type. // May be prefilled by audit middleware (if the type can be extracted from the url path). -func (builder *AuditLogEntryBuilder) WithRequiredObjectType(objectType ObjectType) *AuditLogEntryBuilder { +func (builder *AuditLogEntryBuilder) WithRequiredObjectType(objectType pkgAuditCommon.ObjectType) *AuditLogEntryBuilder { builder.auditParams.ObjectType = objectType return builder } @@ -266,7 +270,7 @@ func (builder *AuditLogEntryBuilder) WithNumResponseItems(numResponseItems int64 } // WithEventType overwrites the default event type EventTypeAdminActivity -func (builder *AuditLogEntryBuilder) WithEventType(eventType EventType) *AuditLogEntryBuilder { +func (builder *AuditLogEntryBuilder) WithEventType(eventType pkgAuditCommon.EventType) *AuditLogEntryBuilder { builder.auditParams.EventType = eventType return builder } @@ -346,16 +350,16 @@ func (builder *AuditLogEntryBuilder) Build(ctx context.Context, sequenceNumber S resourceName := fmt.Sprintf("%s/%s", objectType.Plural(), objectId) var logIdentifier string - var logType ObjectType - if builder.auditParams.EventType == EventTypeSystemEvent { - logIdentifier = SystemIdentifier.Identifier - logType = ObjectTypeSystem + var logType pkgAuditCommon.ObjectType + if builder.auditParams.EventType == pkgAuditCommon.EventTypeSystemEvent { + logIdentifier = pkgAuditCommon.SystemIdentifier.Identifier + logType = pkgAuditCommon.ObjectTypeSystem } else { logIdentifier = objectId logType = *objectType } - builder.auditMetadata.AuditInsertId = NewInsertId(time.Now().UTC(), builder.location, builder.workerId, uint64(sequenceNumber)) + builder.auditMetadata.AuditInsertId = internalAuditApi.NewInsertId(time.Now().UTC(), builder.location, builder.workerId, uint64(sequenceNumber)) builder.auditMetadata.AuditLogName = fmt.Sprintf("%s/%s/logs/%s", logType.Plural(), logIdentifier, builder.auditParams.EventType) builder.auditMetadata.AuditResourceName = resourceName @@ -365,7 +369,7 @@ func (builder *AuditLogEntryBuilder) Build(ctx context.Context, sequenceNumber S } // Instantiate the audit event - return NewAuditLogEntry( + return internalAuditApi.NewAuditLogEntry( builder.auditRequest, builder.auditResponse, details, @@ -378,7 +382,7 @@ func (builder *AuditLogEntryBuilder) Build(ctx context.Context, sequenceNumber S type AuditEventBuilder struct { // The audit api used to validate, serialize and send events - api AuditApi + api pkgAuditCommon.AuditApi // The audit log entry builder which is used to build the actual protobuf message auditLogEntryBuilder *AuditLogEntryBuilder @@ -387,7 +391,7 @@ type AuditEventBuilder struct { built bool // Sequence number generator providing sequential increasing numbers for the insert IDs - sequenceNumberGenerator utils.SequenceNumberGenerator + sequenceNumberGenerator pkgAuditUtils.SequenceNumberGenerator // Opentelemetry tracer tracer trace.Tracer @@ -400,10 +404,10 @@ type AuditEventBuilder struct { // validates input and returns a cloud event that can be sent to the audit log system. func NewAuditEventBuilder( // The audit api used to validate, serialize and send events - api AuditApi, + api pkgAuditCommon.AuditApi, // The sequence number generator can be used to get and revert sequence numbers to build audit log events - sequenceNumberGenerator utils.SequenceNumberGenerator, + sequenceNumberGenerator pkgAuditUtils.SequenceNumberGenerator, // The service name in lowercase (allowed characters are [a-z-]). serviceName string, @@ -450,7 +454,7 @@ func (builder *AuditEventBuilder) WithAuditLogEntryBuilder(auditLogEntryBuilder } // WithRequiredApiRequest adds api request details -func (builder *AuditEventBuilder) WithRequiredApiRequest(request ApiRequest) *AuditEventBuilder { +func (builder *AuditEventBuilder) WithRequiredApiRequest(request pkgAuditCommon.ApiRequest) *AuditEventBuilder { builder.auditLogEntryBuilder.WithRequiredApiRequest(request) return builder } @@ -488,7 +492,7 @@ func (builder *AuditEventBuilder) WithRequiredObjectId(objectId string) *AuditEv // WithRequiredObjectType adds the object type. // May be prefilled by audit middleware (if the type can be extracted from the url path). -func (builder *AuditEventBuilder) WithRequiredObjectType(objectType ObjectType) *AuditEventBuilder { +func (builder *AuditEventBuilder) WithRequiredObjectType(objectType pkgAuditCommon.ObjectType) *AuditEventBuilder { builder.auditLogEntryBuilder.WithRequiredObjectType(objectType) return builder } @@ -545,7 +549,7 @@ func (builder *AuditEventBuilder) WithNumResponseItems(numResponseItems int64) * } // WithEventType overwrites the default event type EventTypeAdminActivity -func (builder *AuditEventBuilder) WithEventType(eventType EventType) *AuditEventBuilder { +func (builder *AuditEventBuilder) WithEventType(eventType pkgAuditCommon.EventType) *AuditEventBuilder { builder.auditLogEntryBuilder.WithEventType(eventType) return builder } @@ -621,7 +625,7 @@ func (builder *AuditEventBuilder) MarkAsBuilt() { // - The RoutableIdentifier required for routing the cloud event // - The operation name // - Error if the event cannot be built -func (builder *AuditEventBuilder) Build(ctx context.Context, sequenceNumber SequenceNumber) (*CloudEvent, *RoutableIdentifier, error) { +func (builder *AuditEventBuilder) Build(ctx context.Context, sequenceNumber SequenceNumber) (*pkgAuditCommon.CloudEvent, *pkgAuditCommon.RoutableIdentifier, error) { if builder.auditLogEntryBuilder == nil { return nil, nil, fmt.Errorf("audit log entry builder not set") } @@ -632,19 +636,19 @@ func (builder *AuditEventBuilder) Build(ctx context.Context, sequenceNumber Sequ visibility := builder.visibility objectId := builder.auditLogEntryBuilder.auditParams.ObjectId objectType := builder.auditLogEntryBuilder.auditParams.ObjectType - var routingIdentifier *RoutableIdentifier - if builder.auditLogEntryBuilder.auditParams.EventType == EventTypeSystemEvent { - routingIdentifier = NewAuditRoutingIdentifier(uuid.Nil.String(), ObjectTypeSystem) + var routingIdentifier *pkgAuditCommon.RoutableIdentifier + if builder.auditLogEntryBuilder.auditParams.EventType == pkgAuditCommon.EventTypeSystemEvent { + routingIdentifier = internalAuditApi.NewAuditRoutingIdentifier(uuid.Nil.String(), pkgAuditCommon.ObjectTypeSystem) if objectId == "" { objectId = uuid.Nil.String() builder.WithRequiredObjectId(objectId) } if objectType == "" { - objectType = ObjectTypeSystem + objectType = pkgAuditCommon.ObjectTypeSystem builder.WithRequiredObjectType(objectType) } } else { - routingIdentifier = NewAuditRoutingIdentifier(objectId, objectType) + routingIdentifier = internalAuditApi.NewAuditRoutingIdentifier(objectId, objectType) } auditLogEntry, err := builder.auditLogEntryBuilder.Build(ctx, sequenceNumber) diff --git a/audit/api/builder_test.go b/pkg/audit/api/builder_test.go similarity index 90% rename from audit/api/builder_test.go rename to pkg/audit/api/builder_test.go index 84fc9fd..cdc653b 100644 --- a/audit/api/builder_test.go +++ b/pkg/audit/api/builder_test.go @@ -2,17 +2,21 @@ package api import ( "context" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/utils" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" "fmt" - "github.com/bufbuild/protovalidate-go" + "testing" + "time" + + "buf.build/go/protovalidate" "github.com/google/uuid" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" - "testing" - "time" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgAuditUtils "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/utils" ) func Test_getObjectIdAndTypeFromAuditParams(t *testing.T) { @@ -40,7 +44,7 @@ func Test_getObjectIdAndTypeFromAuditParams(t *testing.T) { objectId, objectType, err := getObjectIdAndTypeFromAuditParams( &AuditParameters{ ObjectId: "value", - ObjectType: ObjectTypeFromPluralString("invalid"), + ObjectType: pkgAuditCommon.ObjectTypeFromPluralString("invalid"), }, ) assert.EqualError(t, err, "unknown object type") @@ -54,12 +58,12 @@ func Test_getObjectIdAndTypeFromAuditParams(t *testing.T) { objectId, objectType, err := getObjectIdAndTypeFromAuditParams( &AuditParameters{ ObjectId: "value", - ObjectType: ObjectTypeProject, + ObjectType: pkgAuditCommon.ObjectTypeProject, }, ) assert.NoError(t, err) assert.Equal(t, "value", objectId) - assert.Equal(t, ObjectTypeProject, *objectType) + assert.Equal(t, pkgAuditCommon.ObjectTypeProject, *objectType) }, ) } @@ -76,7 +80,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { t.Run("details missing", func(t *testing.T) { logEntry, err := NewAuditLogEntryBuilder().WithRequiredLocation("eu01"). WithRequiredObjectId("1"). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). Build(context.Background(), SequenceNumber(1)) assert.NoError(t, err) @@ -93,16 +97,16 @@ func Test_AuditLogEntryBuilder(t *testing.T) { builder := NewAuditLogEntryBuilder(). WithRequiredLocation("eu01"). WithRequiredObjectId("1"). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation("stackit.demo-service.v1.operation"). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "POST", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, @@ -195,16 +199,16 @@ func Test_AuditLogEntryBuilder(t *testing.T) { builder := NewAuditLogEntryBuilder(). WithRequiredLocation("eu01"). WithRequiredObjectId("1"). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation("stackit.demo-service.v1.operation"). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "POST", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, @@ -215,7 +219,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { WithAuditPermission(permission). WithAuditPermissionCheckResult(permissionCheckResult). WithDetails(details). - WithEventType(EventTypePolicyDenied). + WithEventType(pkgAuditCommon.EventTypePolicyDenied). WithLabels(map[string]string{"key": "label"}). WithNumResponseItems(int64(10)). WithRequestCorrelationId("correlationId"). @@ -310,7 +314,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { builder := NewAuditLogEntryBuilder(). WithRequiredLocation("eu01"). WithRequiredObjectId("1"). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation("stackit.demo-service.v1.operation"). WithRequiredServiceName("demo-service"). WithRequiredWorkerId("worker-id"). @@ -331,7 +335,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) + assert.Equal(t, internalAuditApi.EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) @@ -399,16 +403,16 @@ func Test_AuditLogEntryBuilder(t *testing.T) { builder := NewAuditLogEntryBuilder(). WithRequiredLocation("eu01"). WithRequiredObjectId("1"). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation("stackit.demo-service.v1.operation"). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "POST", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, @@ -419,7 +423,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { WithAuditPermission(permission). WithAuditPermissionCheckResult(permissionCheckResult). WithDetails(details). - WithEventType(EventTypeSystemEvent). + WithEventType(pkgAuditCommon.EventTypeSystemEvent). WithLabels(map[string]string{"key": "label"}). WithNumResponseItems(int64(10)). WithRequestCorrelationId("correlationId"). @@ -451,21 +455,21 @@ func Test_AuditLogEntryBuilder(t *testing.T) { responseBodyBytes, err := ResponseBodyToBytes(responseBody) assert.NoError(t, err) builder := NewAuditLogEntryBuilder(). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "POST", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, }). WithRequiredLocation("eu01"). WithRequiredObjectId("1"). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation("stackit.demo-service.v1.operation"). WithRequiredRequestClientIp("127.0.0.1"). WithRequiredServiceName("demo-service"). @@ -480,21 +484,21 @@ func Test_AuditLogEntryBuilder(t *testing.T) { t.Run("with invalid response body", func(t *testing.T) { builder := NewAuditLogEntryBuilder(). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "POST", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, }). WithRequiredLocation("eu01"). WithRequiredObjectId("1"). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation("stackit.demo-service.v1.operation"). WithRequiredRequestClientIp("127.0.0.1"). WithRequiredServiceName("demo-service"). @@ -511,7 +515,7 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("nothing set", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() cloudEvent, routingIdentifier, err := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01"). Build(context.Background(), SequenceNumber(1)) @@ -524,11 +528,11 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("details missing", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() cloudEvent, routingIdentifier, err := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01"). WithRequiredObjectId("objectId"). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). Build(context.Background(), SequenceNumber(1)) assert.Error(t, err) @@ -539,29 +543,29 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("required only", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() objectId := uuid.NewString() operation := "stackit.demo-service.v1.operation" builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01"). WithRequiredObjectId(objectId). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation(operation). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "POST", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, }). WithRequiredRequestClientIp("127.0.0.1") - routableIdentifier := RoutableIdentifier{Identifier: objectId, Type: ObjectTypeProject} + routableIdentifier := pkgAuditCommon.RoutableIdentifier{Identifier: objectId, Type: pkgAuditCommon.ObjectTypeProject} cloudEvent, routingIdentifier, err := builder.Build(context.Background(), SequenceNumber(1)) assert.NoError(t, err) @@ -663,7 +667,7 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("with details", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() objectId := uuid.NewString() operation := "stackit.demo-service.v1.operation" @@ -677,16 +681,16 @@ func Test_AuditEventBuilder(t *testing.T) { assert.NoError(t, err) builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01"). WithRequiredObjectId(objectId). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation(operation). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "POST", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, @@ -695,7 +699,7 @@ func Test_AuditEventBuilder(t *testing.T) { WithAuditPermission(permission). WithAuditPermissionCheckResult(permissionCheckResult). WithDetails(details). - WithEventType(EventTypeAdminActivity). + WithEventType(pkgAuditCommon.EventTypeAdminActivity). WithLabels(map[string]string{"key": "label"}). WithNumResponseItems(int64(10)). WithRequestCorrelationId("correlationId"). @@ -708,7 +712,7 @@ func Test_AuditEventBuilder(t *testing.T) { WithStatusCode(400). WithVisibility(auditV1.Visibility_VISIBILITY_PRIVATE) - routableIdentifier := RoutableIdentifier{Identifier: objectId, Type: ObjectTypeProject} + routableIdentifier := pkgAuditCommon.RoutableIdentifier{Identifier: objectId, Type: pkgAuditCommon.ObjectTypeProject} cloudEvent, routingIdentifier, err := builder.Build(context.Background(), SequenceNumber(1)) assert.NoError(t, err) @@ -817,13 +821,13 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("system event with object reference", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() objectId := uuid.NewString() operation := "stackit.demo-service.v1.operation" builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01"). WithRequiredObjectId(objectId). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation(operation). AsSystemEvent() @@ -831,8 +835,8 @@ func Test_AuditEventBuilder(t *testing.T) { assert.NoError(t, err) assert.True(t, builder.IsBuilt()) - assert.Equal(t, SystemIdentifier.Identifier, routingIdentifier.ToObjectIdentifier().Identifier) - assert.Equal(t, SystemIdentifier.Type, routingIdentifier.ToObjectIdentifier().Type) + assert.Equal(t, pkgAuditCommon.SystemIdentifier.Identifier, routingIdentifier.ToObjectIdentifier().Identifier) + assert.Equal(t, pkgAuditCommon.SystemIdentifier.Type, routingIdentifier.ToObjectIdentifier().Type) assert.NotNil(t, cloudEvent) assert.Equal(t, "application/cloudevents+protobuf", cloudEvent.DataContentType) @@ -849,8 +853,8 @@ func Test_AuditEventBuilder(t *testing.T) { assert.NotNil(t, cloudEvent.Data) assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent)) - assert.Equal(t, SystemIdentifier.Identifier, routableAuditEvent.ObjectIdentifier.Identifier) - assert.Equal(t, SystemIdentifier.Type, routableAuditEvent.ObjectIdentifier.Type) + assert.Equal(t, pkgAuditCommon.SystemIdentifier.Identifier, routableAuditEvent.ObjectIdentifier.Identifier) + assert.Equal(t, pkgAuditCommon.SystemIdentifier.Type, routableAuditEvent.ObjectIdentifier.Type) assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility) assert.Equal(t, operation, routableAuditEvent.OperationName) @@ -869,7 +873,7 @@ func Test_AuditEventBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) + assert.Equal(t, internalAuditApi.EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) @@ -929,7 +933,7 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("system event", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() operation := "stackit.demo-service.v1.operation" builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01"). @@ -940,8 +944,8 @@ func Test_AuditEventBuilder(t *testing.T) { assert.NoError(t, err) assert.True(t, builder.IsBuilt()) - assert.Equal(t, SystemIdentifier.Identifier, routingIdentifier.ToObjectIdentifier().Identifier) - assert.Equal(t, SystemIdentifier.Type, routingIdentifier.ToObjectIdentifier().Type) + assert.Equal(t, pkgAuditCommon.SystemIdentifier.Identifier, routingIdentifier.ToObjectIdentifier().Identifier) + assert.Equal(t, pkgAuditCommon.SystemIdentifier.Type, routingIdentifier.ToObjectIdentifier().Type) assert.NotNil(t, cloudEvent) assert.Equal(t, "application/cloudevents+protobuf", cloudEvent.DataContentType) @@ -958,8 +962,8 @@ func Test_AuditEventBuilder(t *testing.T) { assert.NotNil(t, cloudEvent.Data) assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent)) - assert.Equal(t, SystemIdentifier.Identifier, routableAuditEvent.ObjectIdentifier.Identifier) - assert.Equal(t, SystemIdentifier.Type, routableAuditEvent.ObjectIdentifier.Type) + assert.Equal(t, pkgAuditCommon.SystemIdentifier.Identifier, routableAuditEvent.ObjectIdentifier.Identifier) + assert.Equal(t, pkgAuditCommon.SystemIdentifier.Type, routableAuditEvent.ObjectIdentifier.Type) assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility) assert.Equal(t, operation, routableAuditEvent.OperationName) @@ -978,7 +982,7 @@ func Test_AuditEventBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) + assert.Equal(t, internalAuditApi.EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) @@ -1038,7 +1042,7 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("with response body unserialized", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() objectId := uuid.NewString() operation := "stackit.demo-service.v1.operation" @@ -1050,16 +1054,16 @@ func Test_AuditEventBuilder(t *testing.T) { responseBody := map[string]interface{}{"key": "response"} builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01"). WithRequiredObjectId(objectId). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation(operation). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "POST", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, @@ -1068,7 +1072,7 @@ func Test_AuditEventBuilder(t *testing.T) { WithAuditPermission(permission). WithAuditPermissionCheckResult(permissionCheckResult). WithDetails(details). - WithEventType(EventTypeAdminActivity). + WithEventType(pkgAuditCommon.EventTypeAdminActivity). WithLabels(map[string]string{"key": "label"}). WithNumResponseItems(int64(10)). WithRequestCorrelationId("correlationId"). @@ -1081,7 +1085,7 @@ func Test_AuditEventBuilder(t *testing.T) { WithStatusCode(400). WithVisibility(auditV1.Visibility_VISIBILITY_PRIVATE) - routableIdentifier := RoutableIdentifier{Identifier: objectId, Type: ObjectTypeProject} + routableIdentifier := pkgAuditCommon.RoutableIdentifier{Identifier: objectId, Type: pkgAuditCommon.ObjectTypeProject} cloudEvent, routingIdentifier, err := builder.Build(context.Background(), SequenceNumber(1)) assert.NoError(t, err) @@ -1111,7 +1115,7 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("mark as built", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01") builder.MarkAsBuilt() @@ -1121,7 +1125,7 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("no entry builder", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() cloudEvent, routingIdentifier, err := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01"). WithAuditLogEntryBuilder(nil).Build(context.Background(), SequenceNumber(1)) @@ -1133,7 +1137,7 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("next sequence number", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01") assert.Equal(t, SequenceNumber(0), builder.NextSequenceNumber()) @@ -1142,7 +1146,7 @@ func Test_AuditEventBuilder(t *testing.T) { t.Run("revert sequence number", func(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01") assert.Equal(t, SequenceNumber(0), builder.NextSequenceNumber()) diff --git a/audit/api/log.go b/pkg/audit/api/log.go similarity index 87% rename from audit/api/log.go rename to pkg/audit/api/log.go index ec75b1b..25a5577 100644 --- a/audit/api/log.go +++ b/pkg/audit/api/log.go @@ -1,20 +1,23 @@ package api import ( - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/log" "encoding/json" "errors" + "time" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "time" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgLog "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/log" ) // LogEvent logs an event to the terminal -func LogEvent(event *CloudEvent) error { +func LogEvent(event *pkgAuditCommon.CloudEvent) error { if event.DataType == DataTypeLegacyAuditEventV1 { - log.AuditLogger.Info(string(event.Data)) + pkgLog.AuditLogger.Info(string(event.Data)) return nil } else if event.DataType != "audit.v1.RoutableAuditEvent" { return errors.New("Unsupported data type " + event.DataType) @@ -75,7 +78,7 @@ func LogEvent(event *CloudEvent) error { return err } - log.AuditLogger.Info(string(cloudEventJson)) + pkgLog.AuditLogger.Info(string(cloudEventJson)) return nil } diff --git a/audit/api/log_test.go b/pkg/audit/api/log_test.go similarity index 61% rename from audit/api/log_test.go rename to pkg/audit/api/log_test.go index ff22e53..1bdf10d 100644 --- a/audit/api/log_test.go +++ b/pkg/audit/api/log_test.go @@ -2,37 +2,41 @@ package api import ( "context" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/utils" - auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - "github.com/bufbuild/protovalidate-go" + "testing" + + "buf.build/go/protovalidate" "github.com/google/uuid" "github.com/stretchr/testify/assert" - "testing" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" + pkgAuditUtils "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/utils" ) func Test_LogEvent(t *testing.T) { api, _ := NewMockAuditApi() - sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator() + sequenceNumberGenerator := pkgAuditUtils.NewDefaultSequenceNumberGenerator() t.Run("new format", func(t *testing.T) { eventBuilder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", uuid.NewString(), "eu01") cloudEvent, _, err := eventBuilder. - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "GET", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, }). WithRequiredObjectId(uuid.NewString()). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation("stackit.demo-service.v1.project.update"). WithRequiredRequestClientIp("0.0.0.0"). Build(context.Background(), eventBuilder.NextSequenceNumber()) @@ -44,21 +48,21 @@ func Test_LogEvent(t *testing.T) { t.Run("legacy format", func(t *testing.T) { objectId := uuid.NewString() entry, err := NewAuditLogEntryBuilder(). - WithRequiredApiRequest(ApiRequest{ + WithRequiredApiRequest(pkgAuditCommon.ApiRequest{ Body: nil, - Header: TestHeaders, + Header: internalAuditApi.TestHeaders, Host: "localhost", Method: "GET", Scheme: "https", Proto: "HTTP/1.1", - URL: RequestUrl{ + URL: pkgAuditCommon.RequestUrl{ Path: "/", RawQuery: nil, }, }). WithRequiredLocation("eu01"). WithRequiredObjectId(objectId). - WithRequiredObjectType(ObjectTypeProject). + WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject). WithRequiredOperation("stackit.demo-service.v1.project.update"). WithRequiredRequestClientIp("0.0.0.0"). WithRequiredServiceName("demo-service"). @@ -68,25 +72,25 @@ func Test_LogEvent(t *testing.T) { validator, err := protovalidate.New() assert.NoError(t, err) - var protoValidator ProtobufValidator = validator + var protoValidator pkgAuditCommon.ProtobufValidator = validator - routableIdentifier := RoutableIdentifier{ + routableIdentifier := pkgAuditCommon.RoutableIdentifier{ Identifier: objectId, - Type: ObjectTypeProject, + Type: pkgAuditCommon.ObjectTypeProject, } - routableEvent, err := validateAndSerializePartially(protoValidator, entry, auditV1.Visibility_VISIBILITY_PUBLIC, &routableIdentifier) + routableEvent, err := internalAuditApi.ValidateAndSerializePartially(protoValidator, entry, auditV1.Visibility_VISIBILITY_PUBLIC, &routableIdentifier) assert.NoError(t, err) - legacyBytes, err := convertAndSerializeIntoLegacyFormat(entry, routableEvent) + legacyBytes, err := internalAuditApi.ConvertAndSerializeIntoLegacyFormat(entry, routableEvent) assert.NoError(t, err) - cloudEvent := CloudEvent{ + cloudEvent := pkgAuditCommon.CloudEvent{ SpecVersion: "1.0", Source: entry.ProtoPayload.ServiceName, Id: entry.InsertId, Time: entry.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), - DataContentType: ContentTypeCloudEventsJson, + DataContentType: pkgAuditCommon.ContentTypeCloudEventsJson, DataType: DataTypeLegacyAuditEventV1, Subject: entry.ProtoPayload.ResourceName, Data: legacyBytes, diff --git a/pkg/audit/api/utils.go b/pkg/audit/api/utils.go new file mode 100644 index 0000000..aead3eb --- /dev/null +++ b/pkg/audit/api/utils.go @@ -0,0 +1,91 @@ +package api + +import ( + "encoding/json" + "net" + "regexp" + "strings" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" +) + +var objectTypeIdPattern = regexp.MustCompile(".*/(projects|folders|organizations)/([0-9a-fA-F-]{36})(?:/.*)?") + +// GetCalledServiceNameFromRequest extracts the called service name from subdomain name +func GetCalledServiceNameFromRequest(request *pkgAuditCommon.ApiRequest, fallbackName string) string { + if request == nil { + return fallbackName + } + + var calledServiceName = fallbackName + host := request.Host + ip := net.ParseIP(host) + if ip == nil && !strings.Contains(host, "localhost") { + dotIdx := strings.Index(host, ".") + if dotIdx != -1 { + calledServiceName = host[0:dotIdx] + } + } + return calledServiceName +} + +func GetObjectIdAndTypeFromUrlPath(path string) ( + string, + *pkgAuditCommon.ObjectType, + error, +) { + + // Extract object id and type from request url + objectTypeIdMatches := objectTypeIdPattern.FindStringSubmatch(path) + if len(objectTypeIdMatches) > 0 { + objectType := pkgAuditCommon.ObjectTypeFromPluralString(objectTypeIdMatches[1]) + err := objectType.IsSupportedType() + if err != nil { + return "", nil, err + } + + objectId := objectTypeIdMatches[2] + + return objectId, &objectType, nil + } + + return "", nil, nil +} + +// ResponseBodyToBytes converts a JSON or Protobuf response into a byte array +func ResponseBodyToBytes(response any) ([]byte, error) { + if response == nil { + return nil, nil + } + + responseBytes, isBytes := response.([]byte) + if isBytes { + return responseBytes, nil + } + + responseProtoMessage, isProtoMessage := response.(proto.Message) + if isProtoMessage { + responseJson, err := protojson.Marshal(responseProtoMessage) + if err != nil { + return nil, err + } + return responseJson, nil + } + + responseJson, err := json.Marshal(response) + if err != nil { + return nil, err + } + return responseJson, nil +} + +func ToArrayMap(input map[string]string) map[string][]string { + output := map[string][]string{} + for key, value := range input { + output[key] = []string{value} + } + return output +} diff --git a/pkg/audit/api/utils_test.go b/pkg/audit/api/utils_test.go new file mode 100644 index 0000000..67dd285 --- /dev/null +++ b/pkg/audit/api/utils_test.go @@ -0,0 +1,154 @@ +package api + +import ( + "encoding/json" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/encoding/protojson" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" + pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common" +) + +func Test_GetCalledServiceNameFromRequest(t *testing.T) { + + t.Run("request is nil", func(t *testing.T) { + serviceName := GetCalledServiceNameFromRequest(nil, "resource-manager") + assert.Equal(t, "resource-manager", serviceName) + }) + + t.Run("localhost", func(t *testing.T) { + request := pkgAuditCommon.ApiRequest{Host: "localhost:8080"} + serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") + assert.Equal(t, "resource-manager", serviceName) + }) + + t.Run("cf", func(t *testing.T) { + request := pkgAuditCommon.ApiRequest{Host: "stackit-resource-manager-go-dev.apps.01.cf.eu01.stackit.cloud"} + serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") + assert.Equal(t, "stackit-resource-manager-go-dev", serviceName) + }) + + t.Run("cf invalid host", func(t *testing.T) { + request := pkgAuditCommon.ApiRequest{Host: ""} + serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") + assert.Equal(t, "resource-manager", serviceName) + }) + + t.Run("ip", func(t *testing.T) { + request := pkgAuditCommon.ApiRequest{Host: "127.0.0.1"} + serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") + assert.Equal(t, "resource-manager", serviceName) + }, + ) + + t.Run("ip short", func(t *testing.T) { + request := pkgAuditCommon.ApiRequest{Host: "::1"} + serviceName := GetCalledServiceNameFromRequest(&request, "resource-manager") + assert.Equal(t, "resource-manager", serviceName) + }, + ) +} + +func Test_GetObjectIdAndTypeFromUrlPath(t *testing.T) { + + t.Run("object id and type not in url", func(t *testing.T) { + objectId, objectType, err := GetObjectIdAndTypeFromUrlPath("/v2/projects/audit") + assert.NoError(t, err) + assert.Equal(t, "", objectId) + assert.Nil(t, objectType) + }) + + t.Run("object id and type in url", func(t *testing.T) { + objectId, objectType, err := GetObjectIdAndTypeFromUrlPath("/v2/projects/f17d4064-9b65-4334-b6a7-8fed96340124") + assert.NoError(t, err) + assert.Equal(t, "f17d4064-9b65-4334-b6a7-8fed96340124", objectId) + assert.Equal(t, pkgAuditCommon.ObjectTypeProject, *objectType) + }) + + t.Run("multiple object ids and types in url", func(t *testing.T) { + objectId, objectType, err := GetObjectIdAndTypeFromUrlPath("/v2/organization/8ee58bec-d496-4bb9-af8d-72fda4d78b6b/projects/f17d4064-9b65-4334-b6a7-8fed96340124") + assert.NoError(t, err) + assert.Equal(t, "f17d4064-9b65-4334-b6a7-8fed96340124", objectId) + assert.Equal(t, pkgAuditCommon.ObjectTypeProject, *objectType) + }) +} + +func Test_ResponseBodyToBytes(t *testing.T) { + + t.Run( + "nil response body", func(t *testing.T) { + bytes, err := ResponseBodyToBytes(nil) + assert.Nil(t, bytes) + assert.Nil(t, err) + }, + ) + + t.Run( + "bytes", func(t *testing.T) { + responseBody := []byte("data") + bytes, err := ResponseBodyToBytes(responseBody) + assert.Nil(t, err) + assert.Equal(t, responseBody, bytes) + }, + ) + + t.Run( + "Protobuf message", func(t *testing.T) { + protobufMessage := auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(pkgAuditCommon.ObjectTypeProject)} + bytes, err := ResponseBodyToBytes(&protobufMessage) + assert.Nil(t, err) + + expected, err := protojson.Marshal(&protobufMessage) + assert.Nil(t, err) + assert.Equal(t, expected, bytes) + }, + ) + + t.Run( + "struct", func(t *testing.T) { + type CustomObject struct { + Value string + } + + responseBody := CustomObject{Value: "data"} + bytes, err := ResponseBodyToBytes(responseBody) + assert.Nil(t, err) + + expected, err := json.Marshal(responseBody) + assert.Nil(t, err) + assert.Equal(t, expected, bytes) + }, + ) + + t.Run( + "map", func(t *testing.T) { + + responseBody := map[string]interface{}{"value": "data"} + bytes, err := ResponseBodyToBytes(responseBody) + assert.Nil(t, err) + + expected, err := json.Marshal(responseBody) + assert.Nil(t, err) + assert.Equal(t, expected, bytes) + }, + ) +} + +func Test_ToArrayMap(t *testing.T) { + + t.Run("empty map", func(t *testing.T) { + result := ToArrayMap(map[string]string{}) + assert.Equal(t, map[string][]string{}, result) + }) + + t.Run("empty map", func(t *testing.T) { + result := ToArrayMap(map[string]string{"key1": "value1", "key2": "value2"}) + assert.Equal(t, map[string][]string{ + "key1": {"value1"}, + "key2": {"value2"}, + }, result) + }) +} diff --git a/audit/api/api.go b/pkg/audit/common/api.go similarity index 90% rename from audit/api/api.go rename to pkg/audit/common/api.go index e08a818..7172617 100644 --- a/audit/api/api.go +++ b/pkg/audit/common/api.go @@ -1,17 +1,23 @@ -package api +package common import ( "context" - "github.com/bufbuild/protovalidate-go" + "regexp" "time" + "buf.build/go/protovalidate" "github.com/google/uuid" + "google.golang.org/protobuf/proto" auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - - "google.golang.org/protobuf/proto" ) +// ContentTypeCloudEventsProtobuf the cloudevents protobuf content-type sent in metadata of messages +const ContentTypeCloudEventsProtobuf = "application/cloudevents+protobuf" +const ContentTypeCloudEventsJson = "application/cloudevents+json; charset=UTF-8" + +var TopicNamePattern = regexp.MustCompile(`^topic://stackit-platform/t/swz/audit-log/(?:conway|eu01|eu02|sx-stoi01)/[Vv][1-9](?:\.\d)?/[A-Za-z0-9-]+/[A-Za-z0-9-/]+`) + type EventType string const ( @@ -218,6 +224,17 @@ type TopicNameResolver interface { Resolve(routableIdentifier *RoutableIdentifier) (string, error) } +// StaticTopicNameTestResolver implements TopicNameResolver. +// A hard-coded topic name is used, routable identifiers are ignored. +type StaticTopicNameTestResolver struct { + TopicName string +} + +// Resolve implements TopicNameResolver.Resolve +func (r *StaticTopicNameTestResolver) Resolve(*RoutableIdentifier) (string, error) { + return r.TopicName, nil +} + type RoutableIdentifier struct { Identifier string Type ObjectType diff --git a/audit/api/converter.go b/pkg/audit/common/converter.go similarity index 98% rename from audit/api/converter.go rename to pkg/audit/common/converter.go index c2f5971..dda868c 100644 --- a/audit/api/converter.go +++ b/pkg/audit/common/converter.go @@ -1,4 +1,4 @@ -package api +package common import ( auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" diff --git a/pkg/audit/common/errors.go b/pkg/audit/common/errors.go new file mode 100644 index 0000000..3bb1be8 --- /dev/null +++ b/pkg/audit/common/errors.go @@ -0,0 +1,49 @@ +package common + +import ( + "errors" +) + +// ErrAttributeIdentifierInvalid indicates that the object identifier +// and the identifier in the checked attribute do not match +var ErrAttributeIdentifierInvalid = errors.New("attribute identifier invalid") + +// ErrAttributeTypeInvalid indicates that an invalid type has been provided. +var ErrAttributeTypeInvalid = errors.New("attribute type invalid") + +// ErrCloudEventNil states that the given cloud event is nil +var ErrCloudEventNil = errors.New("cloud event nil") + +// ErrEventNil indicates that the event was nil +var ErrEventNil = errors.New("event is nil") + +// ErrInvalidRoutableIdentifierForSystemEvent states that the routable identifier is not valid for a system event +var ErrInvalidRoutableIdentifierForSystemEvent = errors.New("invalid identifier for system event") + +// ErrMessagingApiNil states that the messaging api is nilØØ +var ErrMessagingApiNil = errors.New("messaging api nil") + +// ErrObjectIdentifierNil indicates that the object identifier was nil +var ErrObjectIdentifierNil = errors.New("object identifier is nil") + +// ErrObjectIdentifierVisibilityMismatch indicates that a reference mismatch was detected. +// +// Valid combinations are: +// * Visibility: Public, ObjectIdentifier: +// * Visibility: Private, ObjectIdentifier: +var ErrObjectIdentifierVisibilityMismatch = errors.New("object reference visibility mismatch") + +// ErrTopicNameResolverNil states that the topic name resolve is nil +var ErrTopicNameResolverNil = errors.New("topic name resolver nil") + +// ErrUnknownObjectType indicates that the given input is an unknown object type +var ErrUnknownObjectType = errors.New("unknown object type") + +// ErrUnsupportedEventTypeDataAccess states that the event type "data-access" is currently not supported +var ErrUnsupportedEventTypeDataAccess = errors.New("unsupported event type data access") + +// ErrUnsupportedObjectIdentifierType indicates that an unsupported object identifier type has been provided +var ErrUnsupportedObjectIdentifierType = errors.New("unsupported object identifier type") + +// ErrUnsupportedRoutableType indicates that the given input is an unsupported routable type +var ErrUnsupportedRoutableType = errors.New("unsupported routable type") diff --git a/pkg/audit/common/model.go b/pkg/audit/common/model.go new file mode 100644 index 0000000..d197c22 --- /dev/null +++ b/pkg/audit/common/model.go @@ -0,0 +1,59 @@ +package common + +type ApiRequest struct { + + // Body + // + // Required: false + Body []byte + + // The (HTTP) request headers / gRPC metadata. + // + // Internal IP-Addresses have to be removed (e.g. in x-forwarded-xxx headers). + // + // Required: true + Header map[string][]string + + // The HTTP request `Host` header value. + // + // Required: true + Host string + + // Method + // + // Required: true + Method string + + // The URL scheme, such as `http`, `https` or `gRPC`. + // + // Required: true + Scheme string + + // The network protocol used with the request, such as "http/1.1", + // "spdy/3", "h2", "h2c", "webrtc", "tcp", "udp", "quic". See + // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids + // for details. + // + // Required: true + Proto string + + // The url + // + // Required: true + URL RequestUrl +} + +type RequestUrl struct { + + // The gRPC / HTTP URL path. + // + // Required: true + Path string + + // The HTTP URL query in the format of "name1=value1&name2=value2", as it + // appears in the first line of the HTTP request. + // The input should be escaped to not contain any special characters. + // + // Required: false + RawQuery *string +} diff --git a/audit/utils/sequence_generator.go b/pkg/audit/utils/sequence_generator.go similarity index 100% rename from audit/utils/sequence_generator.go rename to pkg/audit/utils/sequence_generator.go diff --git a/audit/utils/sequence_generator_test.go b/pkg/audit/utils/sequence_generator_test.go similarity index 99% rename from audit/utils/sequence_generator_test.go rename to pkg/audit/utils/sequence_generator_test.go index 18712a5..82bce1e 100644 --- a/audit/utils/sequence_generator_test.go +++ b/pkg/audit/utils/sequence_generator_test.go @@ -1,8 +1,9 @@ package utils import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func Test_DefaultSequenceNumberGenerator(t *testing.T) { diff --git a/log/log.go b/pkg/log/log.go similarity index 100% rename from log/log.go rename to pkg/log/log.go diff --git a/log/log_test.go b/pkg/log/log_test.go similarity index 100% rename from log/log_test.go rename to pkg/log/log_test.go diff --git a/log/slog.go b/pkg/log/slog.go similarity index 100% rename from log/slog.go rename to pkg/log/slog.go diff --git a/log/slog_test.go b/pkg/log/slog_test.go similarity index 100% rename from log/slog_test.go rename to pkg/log/slog_test.go diff --git a/log/zerolog.go b/pkg/log/zerolog.go similarity index 100% rename from log/zerolog.go rename to pkg/log/zerolog.go diff --git a/log/zerolog_test.go b/pkg/log/zerolog_test.go similarity index 100% rename from log/zerolog_test.go rename to pkg/log/zerolog_test.go diff --git a/audit/messaging/messaging.go b/pkg/messaging/api/amqp.go similarity index 63% rename from audit/messaging/messaging.go rename to pkg/messaging/api/amqp.go index d3c6cfa..5452716 100644 --- a/audit/messaging/messaging.go +++ b/pkg/messaging/api/amqp.go @@ -1,50 +1,30 @@ -package messaging +package api import ( "context" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/log" "errors" "fmt" "sync" "time" + + internalMessaging "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/messaging" + pkgLog "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/log" + pkgMessagingCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" ) -// Api is an abstraction for a messaging system that can be used to send -// audit logs to the audit log system. -type Api interface { - - // Send method will send the given data to the specified topic synchronously. - // Parameters: - // * ctx - the context object - // * topic - the messaging topic where to send the data to - // * data - the serialized data as byte array - // * contentType - the contentType of the serialized data - // * applicationProperties - properties to send with the message (i.e. cloud event headers) - // - // It returns technical errors for connection issues or sending problems. - Send(ctx context.Context, topic string, data []byte, contentType string, applicationProperties map[string]any) error - - // Close the underlying connection to the messaging system. - // Parameters: - // * ctx - the context object - // - // It returns an error if the connection cannot be closed successfully - Close(ctx context.Context) error -} - // AmqpApi implements Api. type AmqpApi struct { - connection *AmqpConnection - connectionPool ConnectionPool - connectionPoolHandle *ConnectionPoolHandle - senderCache map[string]*AmqpSenderSession + connection *internalMessaging.AmqpConnection + connectionPool internalMessaging.ConnectionPool + connectionPoolHandle *internalMessaging.ConnectionPoolHandle + senderCache map[string]*internalMessaging.AmqpSenderSession lock sync.RWMutex } var _ Api = &AmqpApi{} -func NewDefaultAmqpApi(amqpConfig AmqpConnectionConfig) (Api, error) { - connectionPool, err := NewDefaultAmqpConnectionPool(amqpConfig, "sdk") +func NewDefaultAmqpApi(amqpConfig pkgMessagingCommon.AmqpConnectionConfig) (Api, error) { + connectionPool, err := internalMessaging.NewDefaultAmqpConnectionPool(amqpConfig, "sdk") if err != nil { return nil, fmt.Errorf("new amqp connection pool: %w", err) } @@ -52,15 +32,15 @@ func NewDefaultAmqpApi(amqpConfig AmqpConnectionConfig) (Api, error) { amqpApi := &AmqpApi{ connectionPool: connectionPool, connectionPoolHandle: connectionPool.NewHandle(), - senderCache: make(map[string]*AmqpSenderSession), + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } var messagingApi Api = amqpApi return messagingApi, nil } -func NewAmqpApi(amqpConfig AmqpConnectionPoolConfig) (Api, error) { - connectionPool, err := NewAmqpConnectionPool(amqpConfig, "sdk") +func NewAmqpApi(amqpConfig pkgMessagingCommon.AmqpConnectionPoolConfig) (Api, error) { + connectionPool, err := internalMessaging.NewAmqpConnectionPool(amqpConfig, "sdk") if err != nil { return nil, fmt.Errorf("new amqp connection pool: %w", err) } @@ -68,7 +48,7 @@ func NewAmqpApi(amqpConfig AmqpConnectionPoolConfig) (Api, error) { amqpApi := &AmqpApi{ connectionPool: connectionPool, connectionPoolHandle: connectionPool.NewHandle(), - senderCache: make(map[string]*AmqpSenderSession), + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } var messagingApi Api = amqpApi @@ -110,7 +90,7 @@ func (a *AmqpApi) Send(_ context.Context, topic string, data []byte, contentType return nil } -func (a *AmqpApi) senderFromCache(topic string) *AmqpSenderSession { +func (a *AmqpApi) senderFromCache(topic string) *internalMessaging.AmqpSenderSession { a.lock.RLock() defer a.lock.RUnlock() return a.senderCache[topic] @@ -142,7 +122,7 @@ func (a *AmqpApi) newSender(topic string) error { // Close implements Api.Close func (a *AmqpApi) Close(_ context.Context) error { - log.AuditLogger.Info("close audit amqp connection pool") + pkgLog.AuditLogger.Info("close audit amqp connection pool") a.lock.Lock() defer a.lock.Unlock() diff --git a/audit/messaging/messaging_test.go b/pkg/messaging/api/amqp_test.go similarity index 66% rename from audit/messaging/messaging_test.go rename to pkg/messaging/api/amqp_test.go index b70e43a..57a430d 100644 --- a/audit/messaging/messaging_test.go +++ b/pkg/messaging/api/amqp_test.go @@ -1,16 +1,73 @@ -package messaging +package api import ( "context" "errors" "fmt" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "sync" "testing" "time" + + "github.com/Azure/go-amqp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + internalMessaging "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/messaging" + pkgMessagingCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common" + pkgMessagingTest "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/test" ) +type amqpConnMock struct { + mock.Mock +} + +func (m *amqpConnMock) Done() <-chan struct{} { + args := m.Called() + return args.Get(0).(<-chan struct{}) +} + +func (m *amqpConnMock) NewSession(ctx context.Context, opts *amqp.SessionOptions) (internalMessaging.AmqpSession, error) { + args := m.Called(ctx, opts) + return args.Get(0).(internalMessaging.AmqpSession), args.Error(1) +} + +func (m *amqpConnMock) Close() error { + args := m.Called() + return args.Error(0) +} + +var _ internalMessaging.AmqpConn = (*amqpConnMock)(nil) + +type amqpSenderMock struct { + mock.Mock +} + +func (m *amqpSenderMock) Send(ctx context.Context, msg *amqp.Message, opts *amqp.SendOptions) error { + return m.Called(ctx, msg, opts).Error(0) +} + +func (m *amqpSenderMock) Close(ctx context.Context) error { + return m.Called(ctx).Error(0) +} + +var _ internalMessaging.AmqpSender = (*amqpSenderMock)(nil) + +type amqpSessionMock struct { + mock.Mock +} + +func (m *amqpSessionMock) NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (internalMessaging.AmqpSender, error) { + args := m.Called(ctx, target, opts) + return args.Get(0).(internalMessaging.AmqpSender), args.Error(1) +} + +func (m *amqpSessionMock) Close(ctx context.Context) error { + args := m.Called(ctx) + return args.Error(0) +} + +var _ internalMessaging.AmqpSession = (*amqpSessionMock)(nil) + type connectionPoolMock struct { mock.Mock } @@ -19,20 +76,20 @@ func (m *connectionPoolMock) Close() error { return m.Called().Error(0) } -func (m *connectionPoolMock) NewHandle() *ConnectionPoolHandle { - return m.Called().Get(0).(*ConnectionPoolHandle) +func (m *connectionPoolMock) NewHandle() *internalMessaging.ConnectionPoolHandle { + return m.Called().Get(0).(*internalMessaging.ConnectionPoolHandle) } -func (m *connectionPoolMock) GetConnection(handle *ConnectionPoolHandle) (*AmqpConnection, error) { - return m.Called(handle).Get(0).(*AmqpConnection), m.Called(handle).Error(1) +func (m *connectionPoolMock) GetConnection(handle *internalMessaging.ConnectionPoolHandle) (*internalMessaging.AmqpConnection, error) { + return m.Called(handle).Get(0).(*internalMessaging.AmqpConnection), m.Called(handle).Error(1) } -var _ ConnectionPool = (*connectionPoolMock)(nil) +var _ internalMessaging.ConnectionPool = (*connectionPoolMock)(nil) func Test_NewAmqpMessagingApi(t *testing.T) { _, err := NewAmqpApi( - AmqpConnectionPoolConfig{ - Parameters: AmqpConnectionConfig{BrokerUrl: "not-handled-protocol://localhost:5672"}, + pkgMessagingCommon.AmqpConnectionPoolConfig{ + Parameters: pkgMessagingCommon.AmqpConnectionConfig{BrokerUrl: "not-handled-protocol://localhost:5672"}, PoolSize: 1, }) assert.EqualError(t, err, "new amqp connection pool: initialize connections: new connection: new internal connection: internal connect: dial: unsupported scheme \"not-handled-protocol\"") @@ -44,15 +101,15 @@ func Test_AmqpMessagingApi_Send(t *testing.T) { defer cancelFn() // Start solace docker container - solaceContainer, err := NewSolaceContainer(context.Background()) + solaceContainer, err := pkgMessagingTest.NewSolaceContainer(context.Background()) assert.NoError(t, err) defer solaceContainer.Stop() t.Run("Missing topic prefix", func(t *testing.T) { defer solaceContainer.StopOnError() - api, err := NewAmqpApi(AmqpConnectionPoolConfig{ - Parameters: AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, + api, err := NewAmqpApi(pkgMessagingCommon.AmqpConnectionPoolConfig{ + Parameters: pkgMessagingCommon.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}, PoolSize: 1, }) assert.NoError(t, err) @@ -72,7 +129,7 @@ func Test_AmqpMessagingApi_Send(t *testing.T) { topicName := fmt.Sprintf("topic://auditlog/%s", "amqp-send-successfully") assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName)) - api, err := NewDefaultAmqpApi(AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}) + api, err := NewDefaultAmqpApi(pkgMessagingCommon.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString}) assert.NoError(t, err) data := []byte("data") @@ -100,27 +157,27 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { return channel } - newActiveConnection := func() *AmqpConnection { + newActiveConnection := func() *internalMessaging.AmqpConnection { channel := make(chan struct{}) conn := &amqpConnMock{} conn.On("Done", mock.Anything).Return(channelReceiver(channel)) - return &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - conn: conn, + return &internalMessaging.AmqpConnection{ + ConnectionName: "test", + Lock: sync.RWMutex{}, + Conn: conn, } } - newClosedConnection := func() *AmqpConnection { + newClosedConnection := func() *internalMessaging.AmqpConnection { channel := make(chan struct{}) close(channel) conn := &amqpConnMock{} conn.On("Done", mock.Anything).Return(channelReceiver(channel)) - return &AmqpConnection{ - connectionName: "test", - lock: sync.RWMutex{}, - conn: conn, + return &internalMessaging.AmqpConnection{ + ConnectionName: "test", + Lock: sync.RWMutex{}, + Conn: conn, } } @@ -132,7 +189,7 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { session.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(sender, nil) connection := newActiveConnection() - conn := connection.conn.(*amqpConnMock) + conn := connection.Conn.(*amqpConnMock) conn.On("NewSession", mock.Anything, mock.Anything).Return(session, nil) pool := &connectionPoolMock{} @@ -140,8 +197,8 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { amqpApi := &AmqpApi{ connectionPool: pool, - connectionPoolHandle: &ConnectionPoolHandle{connectionOffset: 0}, - senderCache: make(map[string]*AmqpSenderSession), + connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0}, + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } err := amqpApi.Send(context.Background(), "topic://some-topic", []byte("data"), "application/json", make(map[string]any)) @@ -160,19 +217,19 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { session.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(sender, nil) connection := newActiveConnection() - conn := connection.conn.(*amqpConnMock) + conn := connection.Conn.(*amqpConnMock) conn.On("NewSession", mock.Anything, mock.Anything).Return(session, nil) pool := &connectionPoolMock{} pool.On("GetConnection", mock.Anything).Return(connection, nil) closedConnection := newClosedConnection() - closedConnMock := closedConnection.conn.(*amqpConnMock) + closedConnMock := closedConnection.Conn.(*amqpConnMock) amqpApi := &AmqpApi{ connection: closedConnection, connectionPool: pool, - connectionPoolHandle: &ConnectionPoolHandle{connectionOffset: 0}, - senderCache: make(map[string]*AmqpSenderSession), + connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0}, + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } err := amqpApi.Send(context.Background(), "topic://some-topic", []byte("data"), "application/json", make(map[string]any)) @@ -185,15 +242,15 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { }) t.Run("connection nil get connection fail", func(t *testing.T) { - var connection *AmqpConnection = nil + var connection *internalMessaging.AmqpConnection = nil pool := &connectionPoolMock{} pool.On("GetConnection", mock.Anything).Return(connection, errors.New("connection error")) amqpApi := &AmqpApi{ connectionPool: pool, - connectionPoolHandle: &ConnectionPoolHandle{connectionOffset: 0}, - senderCache: make(map[string]*AmqpSenderSession), + connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0}, + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } err := amqpApi.Send(context.Background(), "topic://some-topic", []byte("data"), "application/json", make(map[string]any)) @@ -210,12 +267,12 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { session.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(sender, nil) connection := newActiveConnection() - conn := connection.conn.(*amqpConnMock) + conn := connection.Conn.(*amqpConnMock) conn.On("NewSession", mock.Anything, mock.Anything).Return(session, nil) amqpApi := &AmqpApi{ connection: connection, - senderCache: make(map[string]*AmqpSenderSession), + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } err := amqpApi.Send(context.Background(), "topic://some-topic", []byte("data"), "application/json", make(map[string]any)) @@ -233,12 +290,12 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { session.On("Close", mock.Anything).Return(nil) connection := newActiveConnection() - conn := connection.conn.(*amqpConnMock) + conn := connection.Conn.(*amqpConnMock) conn.On("NewSession", mock.Anything, mock.Anything).Return(session, nil) amqpApi := &AmqpApi{ connection: connection, - senderCache: make(map[string]*AmqpSenderSession), + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } err := amqpApi.Send(context.Background(), "topic://some-topic", []byte("data"), "application/json", make(map[string]any)) @@ -255,7 +312,7 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { topic := "topic://some-topic" amqpApi := &AmqpApi{ connection: newActiveConnection(), - senderCache: map[string]*AmqpSenderSession{topic: {sender: sender}}, + senderCache: map[string]*internalMessaging.AmqpSenderSession{topic: {Sender: sender}}, } err := amqpApi.Send(context.Background(), topic, []byte("data"), "application/json", make(map[string]any)) @@ -273,10 +330,10 @@ func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) { topic := "topic://some-topic" connection := newActiveConnection() - connection.conn.(*amqpConnMock).On("NewSession", mock.Anything, mock.Anything, mock.Anything).Return(session, nil) + connection.Conn.(*amqpConnMock).On("NewSession", mock.Anything, mock.Anything, mock.Anything).Return(session, nil) amqpApi := &AmqpApi{ connection: connection, - senderCache: map[string]*AmqpSenderSession{topic: {sender: sender}}, + senderCache: map[string]*internalMessaging.AmqpSenderSession{topic: {Sender: sender}}, } err := amqpApi.Send(context.Background(), topic, []byte("data"), "application/json", make(map[string]any)) @@ -294,8 +351,8 @@ func Test_AmqpMessagingApi_Close(t *testing.T) { amqpApi := &AmqpApi{ connectionPool: pool, - connectionPoolHandle: &ConnectionPoolHandle{connectionOffset: 0}, - senderCache: make(map[string]*AmqpSenderSession), + connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0}, + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } err := amqpApi.Close(context.Background()) @@ -310,8 +367,8 @@ func Test_AmqpMessagingApi_Close(t *testing.T) { amqpApi := &AmqpApi{ connectionPool: pool, - connectionPoolHandle: &ConnectionPoolHandle{connectionOffset: 0}, - senderCache: make(map[string]*AmqpSenderSession), + connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0}, + senderCache: make(map[string]*internalMessaging.AmqpSenderSession), } err := amqpApi.Close(context.Background()) @@ -328,15 +385,15 @@ func Test_AmqpMessagingApi_Close(t *testing.T) { session.On("Close", mock.Anything).Return(nil) sender := &amqpSenderMock{} sender.On("Close", mock.Anything).Return(nil) - senderSession := &AmqpSenderSession{ - session: session, - sender: sender, + senderSession := &internalMessaging.AmqpSenderSession{ + Session: session, + Sender: sender, } amqpApi := &AmqpApi{ connectionPool: pool, - connectionPoolHandle: &ConnectionPoolHandle{connectionOffset: 0}, - senderCache: map[string]*AmqpSenderSession{"key": senderSession}, + connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0}, + senderCache: map[string]*internalMessaging.AmqpSenderSession{"key": senderSession}, } err := amqpApi.Close(context.Background()) @@ -356,15 +413,15 @@ func Test_AmqpMessagingApi_Close(t *testing.T) { session.On("Close", mock.Anything).Return(nil) sender := &amqpSenderMock{} sender.On("Close", mock.Anything).Return(errors.New("close sender error")) - senderSession := &AmqpSenderSession{ - session: session, - sender: sender, + senderSession := &internalMessaging.AmqpSenderSession{ + Session: session, + Sender: sender, } amqpApi := &AmqpApi{ connectionPool: pool, - connectionPoolHandle: &ConnectionPoolHandle{connectionOffset: 0}, - senderCache: map[string]*AmqpSenderSession{"key": senderSession}, + connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0}, + senderCache: map[string]*internalMessaging.AmqpSenderSession{"key": senderSession}, } err := amqpApi.Close(context.Background()) @@ -384,15 +441,15 @@ func Test_AmqpMessagingApi_Close(t *testing.T) { session.On("Close", mock.Anything).Return(errors.New("close session error")) sender := &amqpSenderMock{} sender.On("Close", mock.Anything).Return(errors.New("close sender error")) - senderSession := &AmqpSenderSession{ - session: session, - sender: sender, + senderSession := &internalMessaging.AmqpSenderSession{ + Session: session, + Sender: sender, } amqpApi := &AmqpApi{ connectionPool: pool, - connectionPoolHandle: &ConnectionPoolHandle{connectionOffset: 0}, - senderCache: map[string]*AmqpSenderSession{"key": senderSession}, + connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0}, + senderCache: map[string]*internalMessaging.AmqpSenderSession{"key": senderSession}, } err := amqpApi.Close(context.Background()) diff --git a/pkg/messaging/api/messaging.go b/pkg/messaging/api/messaging.go new file mode 100644 index 0000000..01c7657 --- /dev/null +++ b/pkg/messaging/api/messaging.go @@ -0,0 +1,28 @@ +package api + +import ( + "context" +) + +// Api is an abstraction for a messaging system that can be used to send +// audit logs to the audit log system. +type Api interface { + + // Send method will send the given data to the specified topic synchronously. + // Parameters: + // * ctx - the context object + // * topic - the messaging topic where to send the data to + // * data - the serialized data as byte array + // * contentType - the contentType of the serialized data + // * applicationProperties - properties to send with the message (i.e. cloud event headers) + // + // It returns technical errors for connection issues or sending problems. + Send(ctx context.Context, topic string, data []byte, contentType string, applicationProperties map[string]any) error + + // Close the underlying connection to the messaging system. + // Parameters: + // * ctx - the context object + // + // It returns an error if the connection cannot be closed successfully + Close(ctx context.Context) error +} diff --git a/audit/messaging/amqp_config.go b/pkg/messaging/common/amqp_config.go similarity index 76% rename from audit/messaging/amqp_config.go rename to pkg/messaging/common/amqp_config.go index 11a571f..03f1db4 100644 --- a/audit/messaging/amqp_config.go +++ b/pkg/messaging/common/amqp_config.go @@ -1,7 +1,4 @@ -package messaging - -const AmqpTopicPrefix = "topic://" -const connectionTimeoutSeconds = 10 +package common type AmqpConnectionConfig struct { BrokerUrl string `json:"brokerUrl"` diff --git a/audit/messaging/solace.go b/pkg/messaging/test/solace.go similarity index 97% rename from audit/messaging/solace.go rename to pkg/messaging/test/solace.go index 52ad748..53b7708 100644 --- a/audit/messaging/solace.go +++ b/pkg/messaging/test/solace.go @@ -1,24 +1,27 @@ -package messaging +package test import ( "bytes" "context" - "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/log" "encoding/json" "errors" "fmt" - "github.com/Azure/go-amqp" - docker "github.com/docker/docker/api/types/container" - "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/wait" "io" "net/http" "regexp" "strings" "time" + + "github.com/Azure/go-amqp" + docker "github.com/docker/docker/api/types/container" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" + + pkgLog "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/log" ) const AmqpQueuePrefix = "queue://" +const AmqpTopicPrefix = "topic://" const dockerImage = "schwarzit-docker.jfrog.io/solace/solace-pubsub-standard:10.8.1.241" var ErrResourceNotFound = errors.New("resource not found") @@ -136,9 +139,9 @@ func NewSolaceContainer(ctx context.Context) (*SolaceContainer, error) { ExposedPorts: []string{"5672/tcp", "8080/tcp"}, HostConfigModifier: func(config *docker.HostConfig) { config.AutoRemove = true + config.ShmSize = 1024 * 1024 * 1024 // 1 GB, }, - ShmSize: 1024 * 1024 * 1024, // 1 GB, - Env: env, + Env: env, WaitingFor: wait.ForLog("Running pre-startup checks:"). WithStartupTimeout(90 * time.Second), } @@ -168,7 +171,7 @@ func NewSolaceContainer(ctx context.Context) (*SolaceContainer, error) { _ = container.Terminate(ctx) return nil, err } - log.AuditLogger.Info("UI Port: " + sempPort.Port()) + pkgLog.AuditLogger.Info("UI Port: " + sempPort.Port()) // Construct connection strings amqpConnectionString := fmt.Sprintf("amqp://%s:%s/", host, amqpPort.Port()) @@ -296,8 +299,8 @@ func (c SolaceContainer) NewAmqpConnection(ctx context.Context) (*amqp.Conn, err func (c SolaceContainer) ValidateTopicName(topicSubscriptionTopicPattern string, topicName string) error { // Cut off the topic:// prefix var name string - if strings.HasPrefix(topicName, "topic://") { - name = topicName[len("topic://"):] + if strings.HasPrefix(topicName, AmqpTopicPrefix) { + name = topicName[len(AmqpTopicPrefix):] } else { name = topicName }