mirror of
https://dev.azure.com/schwarzit/schwarzit.stackit-public/_git/audit-go
synced 2026-02-07 16:47:24 +00:00
Merged PR 779949: feat: Refactor module structure to reflect best practices
Security-concept-update-needed: false. JIRA Work Item: STACKITALO-259
This commit is contained in:
parent
56b04b94cb
commit
85aae1c2e7
53 changed files with 1713 additions and 1739 deletions
|
|
@ -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$
|
||||
|
|
|
|||
28
go.mod
28
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
|
||||
)
|
||||
|
|
|
|||
62
go.sum
62
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=
|
||||
|
|
|
|||
|
|
@ -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: <type>
|
||||
// * Visibility: Private, ObjectIdentifier: <type | system>
|
||||
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
|
||||
}
|
||||
|
|
@ -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"])
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -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"),
|
||||
|
|
@ -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,
|
||||
}},
|
||||
|
|
@ -2,6 +2,7 @@ package api
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
)
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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")}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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))
|
||||
})
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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,
|
||||
91
pkg/audit/api/utils.go
Normal file
91
pkg/audit/api/utils.go
Normal file
|
|
@ -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
|
||||
}
|
||||
154
pkg/audit/api/utils_test.go
Normal file
154
pkg/audit/api/utils_test.go
Normal file
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package api
|
||||
package common
|
||||
|
||||
import (
|
||||
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1"
|
||||
49
pkg/audit/common/errors.go
Normal file
49
pkg/audit/common/errors.go
Normal file
|
|
@ -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: <type>
|
||||
// * Visibility: Private, ObjectIdentifier: <type | system>
|
||||
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")
|
||||
59
pkg/audit/common/model.go
Normal file
59
pkg/audit/common/model.go
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_DefaultSequenceNumberGenerator(t *testing.T) {
|
||||
|
|
@ -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()
|
||||
|
|
@ -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())
|
||||
28
pkg/messaging/api/messaging.go
Normal file
28
pkg/messaging/api/messaging.go
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
package messaging
|
||||
|
||||
const AmqpTopicPrefix = "topic://"
|
||||
const connectionTimeoutSeconds = 10
|
||||
package common
|
||||
|
||||
type AmqpConnectionConfig struct {
|
||||
BrokerUrl string `json:"brokerUrl"`
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in a new issue