mirror of
https://dev.azure.com/schwarzit/schwarzit.stackit-public/_git/audit-go
synced 2026-02-08 09:07:26 +00:00
Compare commits
20 commits
v0.1.8.1-d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80326d88a6 | ||
|
|
b10f6797ff | ||
|
|
84c49f2690 | ||
|
|
4c7c36c8f1 | ||
|
|
a706af62a4 | ||
|
|
f2715624e9 | ||
|
|
40eacfe4ad | ||
|
|
cdea0ac81a | ||
|
|
85aae1c2e7 | ||
|
|
56b04b94cb | ||
|
|
6af0e83a95 | ||
|
|
6b5bc6dfe2 | ||
|
|
e8567c19ff | ||
|
|
618be58a26 | ||
|
|
68cec628e0 | ||
|
|
3eb803ae1c | ||
|
|
ddee3db2fe | ||
|
|
720a1a6d72 | ||
|
|
3472ce1585 | ||
|
|
5742604629 |
71 changed files with 5155 additions and 3254 deletions
|
|
@ -1,17 +1,22 @@
|
|||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: 'ubuntu-24.04'
|
||||
|
||||
variables:
|
||||
- name: bufVersion
|
||||
value: v1.47.2
|
||||
# go install github.com/bufbuild/buf/cmd/buf@
|
||||
value: v1.63.0
|
||||
- name: golangCiLintVersion
|
||||
value: v1.62.2
|
||||
# github.com/golangci/golangci-lint
|
||||
value: v2.8.0
|
||||
- name: goVersion
|
||||
value: 1.22.7
|
||||
# github.com/golang/go
|
||||
value: 1.24.0
|
||||
- name: protobufValidateVersion
|
||||
value: v1.1.0
|
||||
# go install github.com/envoyproxy/protoc-gen-validate@
|
||||
value: v1.3.0
|
||||
- name: protobufVersion
|
||||
value: v1.36.0
|
||||
# go install google.golang.org/protobuf/cmd/protoc-gen-go@
|
||||
value: v1.36.11
|
||||
- name: GOPATH
|
||||
value: '$(system.defaultWorkingDirectory)/gopath'
|
||||
|
||||
|
|
@ -21,6 +26,7 @@ stages:
|
|||
- job: GoBuildTest
|
||||
displayName: Run build and tests
|
||||
variables:
|
||||
- group: artifactory-xx-sit-odj-sec-ident
|
||||
- name: isCiBuild
|
||||
value: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')]
|
||||
steps:
|
||||
|
|
@ -80,6 +86,10 @@ stages:
|
|||
condition: succeeded()
|
||||
displayName: Check local changes after code generation and formatting
|
||||
|
||||
- script: echo "$(ARTIFACTORY_PASSWORD)" | docker login schwarzit-docker.jfrog.io --username $(ARTIFACTORY_USER) --password-stdin
|
||||
displayName: 'Docker login'
|
||||
condition: succeeded()
|
||||
|
||||
- bash: go build ./...
|
||||
condition: succeeded()
|
||||
displayName: Build
|
||||
|
|
|
|||
46
.azuredevops/main-code-analyze.yml
Normal file
46
.azuredevops/main-code-analyze.yml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
name: audit_go_main_code_analyze_$(Date:yyyy-MM-dd)_$(SourceBranchName)_$(Rev:r)
|
||||
|
||||
trigger:
|
||||
- main
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: tools
|
||||
type: git
|
||||
name: schwarzit.stackit-core-platform/core-platform-tools
|
||||
ref: refs/tags/v1.15.0
|
||||
|
||||
pool:
|
||||
vmImage: ubuntu-24.04
|
||||
|
||||
variables:
|
||||
- name: reportDir
|
||||
value: '$(System.DefaultWorkingDirectory)/out'
|
||||
- name: goVersion
|
||||
value: 1.25.5
|
||||
|
||||
stages:
|
||||
- stage: CodeQualityScans
|
||||
displayName: "Code Quality Scans"
|
||||
jobs:
|
||||
- template: ./.azuredevops/templates/jobs/code/code-format.yml@tools
|
||||
parameters:
|
||||
lintReports: true
|
||||
lintReportDir: $(reportDir)
|
||||
|
||||
- template: ./.azuredevops/templates/jobs/code/code-test.yml@tools
|
||||
parameters:
|
||||
testReports: true
|
||||
testReportDir: $(reportDir)
|
||||
|
||||
- template: ./.azuredevops/templates/jobs/code/code-quality-scans.yml@tools
|
||||
parameters:
|
||||
dependsOn:
|
||||
- Tests
|
||||
- Linter
|
||||
organization: 'xx-sit-odj-stackit-public'
|
||||
serviceConnection: 'xx-sit-odj-stackit-public-snyk'
|
||||
sonar: true
|
||||
sonarReportSourceDir: $(reportDir)
|
||||
sonarServiceConnection: sonarqube-audit-go
|
||||
|
|
@ -6,6 +6,6 @@
|
|||
|
||||
[Describe how the change was tested if it needs explanation]
|
||||
|
||||
Security-concept-update-needed: true/false.
|
||||
Security-concept-update-needed: false.
|
||||
|
||||
JIRA Work Item: STACKITALO-xxx
|
||||
JIRA Work Item: [STACKITRMA-XXX](https://jira.schwarz/browse/STACKITRMA-XXX)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
trigger: none
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: 'ubuntu-24.04'
|
||||
|
||||
parameters:
|
||||
- name: releaseType
|
||||
|
|
|
|||
287
.golangci.yml
Normal file
287
.golangci.yml
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
version: "2"
|
||||
run:
|
||||
issues-exit-code: 1
|
||||
tests: true
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
- copyloopvar
|
||||
- cyclop
|
||||
- dogsled
|
||||
- dupl
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gomoddirectives
|
||||
- gomodguard
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- govet
|
||||
- importas
|
||||
- ineffassign
|
||||
- lll
|
||||
- makezero
|
||||
- misspell
|
||||
- nakedret
|
||||
- nestif
|
||||
- nilerr
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- predeclared
|
||||
- promlinter
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- tparallel
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- wastedassign
|
||||
settings:
|
||||
cyclop:
|
||||
max-complexity: 45
|
||||
package-average: 0
|
||||
dogsled:
|
||||
max-blank-identifiers: 2
|
||||
dupl:
|
||||
threshold: 150
|
||||
errcheck:
|
||||
check-type-assertions: true
|
||||
check-blank: true
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
funlen:
|
||||
lines: 100
|
||||
statements: 50
|
||||
gocognit:
|
||||
min-complexity: 45
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 5
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- dupImport
|
||||
- octalLiteral
|
||||
- unnamedResult
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- experimental
|
||||
- opinionated
|
||||
- performance
|
||||
- style
|
||||
settings:
|
||||
hugeParam:
|
||||
sizeThreshold: 121
|
||||
gocyclo:
|
||||
min-complexity: 45
|
||||
govet:
|
||||
disable:
|
||||
- fieldalignment
|
||||
enable-all: true
|
||||
lll:
|
||||
line-length: 180
|
||||
tab-width: 1
|
||||
nakedret:
|
||||
max-func-lines: 5
|
||||
nestif:
|
||||
min-complexity: 10
|
||||
nlreturn:
|
||||
block-size: 5
|
||||
nolintlint:
|
||||
require-explanation: true
|
||||
require-specific: true
|
||||
allow-unused: false
|
||||
prealloc:
|
||||
simple: true
|
||||
range-loops: true
|
||||
for-loops: true
|
||||
revive:
|
||||
rules:
|
||||
- name: context-keys-type
|
||||
disabled: false
|
||||
- name: time-naming
|
||||
disabled: false
|
||||
- name: var-declaration
|
||||
disabled: false
|
||||
- name: unexported-return
|
||||
disabled: false
|
||||
- name: errorf
|
||||
disabled: false
|
||||
- name: blank-imports
|
||||
disabled: false
|
||||
- name: context-as-argument
|
||||
disabled: false
|
||||
- name: dot-imports
|
||||
disabled: false
|
||||
- name: error-return
|
||||
disabled: false
|
||||
- name: error-strings
|
||||
disabled: false
|
||||
- name: error-naming
|
||||
disabled: false
|
||||
- name: exported
|
||||
disabled: false
|
||||
- name: increment-decrement
|
||||
disabled: false
|
||||
- name: var-naming
|
||||
disabled: true
|
||||
- name: package-comments
|
||||
disabled: false
|
||||
- name: range
|
||||
disabled: false
|
||||
- name: receiver-naming
|
||||
disabled: false
|
||||
- name: indent-error-flow
|
||||
disabled: false
|
||||
staticcheck:
|
||||
initialisms:
|
||||
- ACL
|
||||
- API
|
||||
- ASCII
|
||||
- CPU
|
||||
- CSS
|
||||
- DNS
|
||||
- EOF
|
||||
- GUID
|
||||
- HTML
|
||||
- HTTP
|
||||
- HTTPS
|
||||
- ID
|
||||
- IP
|
||||
- JSON
|
||||
- QPS
|
||||
- RAM
|
||||
- RPC
|
||||
- SLA
|
||||
- SMTP
|
||||
- SQL
|
||||
- SSH
|
||||
- TCP
|
||||
- TLS
|
||||
- TTL
|
||||
- UDP
|
||||
- UI
|
||||
- GID
|
||||
- UID
|
||||
- UUID
|
||||
- URI
|
||||
- URL
|
||||
- UTF8
|
||||
- VM
|
||||
- XML
|
||||
- XMPP
|
||||
- XSRF
|
||||
- XSS
|
||||
- SIP
|
||||
- RTP
|
||||
- AMQP
|
||||
- DB
|
||||
- TS
|
||||
unparam:
|
||||
check-exported: false
|
||||
unused:
|
||||
exported-fields-are-used: false
|
||||
whitespace:
|
||||
multi-if: false
|
||||
multi-func: false
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- path: internal/audit/api/api_common.go
|
||||
text: context-as-argument
|
||||
- linters:
|
||||
- gochecknoglobals
|
||||
path: pkg/audit/common/api.go|pkg/log/log.go|internal/telemetry/telemetry.go
|
||||
- linters:
|
||||
- dupl
|
||||
path: pkg/audit/api/api_.*.go
|
||||
- path: internal/audit/api/model_test.go|internal/audit/api/model.go
|
||||
text: G115
|
||||
- linters:
|
||||
- gosec
|
||||
path: internal/audit/api/test_data.go
|
||||
- linters:
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
- gochecknoglobals
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- ineffassign
|
||||
- lll
|
||||
- nakedret
|
||||
- nestif
|
||||
- nlreturn
|
||||
- noctx
|
||||
- revive
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- wastedassign
|
||||
- wsl
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- govet
|
||||
text: declaration of "err" shadows declaration
|
||||
- linters:
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
- gochecknoglobals
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- ineffassign
|
||||
- lll
|
||||
- nakedret
|
||||
- nestif
|
||||
- nlreturn
|
||||
- noctx
|
||||
- revive
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- wastedassign
|
||||
- wsl
|
||||
path: test_.*\.go|pkg/messaging/test/solace.go
|
||||
- linters:
|
||||
- prealloc
|
||||
path: internal/messaging/amqp_connection_pool_test.go
|
||||
text: Consider preallocating connections with capacity 5
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
formatters:
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
82
Makefile
Normal file
82
Makefile
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
SHELL = /bin/bash -euo pipefail
|
||||
PWD = $(shell pwd)
|
||||
export PATH := $(PWD)/bin:$(PATH)
|
||||
|
||||
# constants
|
||||
GOLANGCI_VERSION = 2.8.0
|
||||
|
||||
all: download build ## Initializes all tools and files
|
||||
all/ci: ado-git-setup all
|
||||
|
||||
out:
|
||||
@mkdir -pv "$(@)"
|
||||
|
||||
build: out ## do nothing
|
||||
|
||||
.PHONY: build/%
|
||||
build/%: out ## do nothing
|
||||
|
||||
download:
|
||||
@go mod download
|
||||
|
||||
fmt:
|
||||
@go fmt ./...
|
||||
|
||||
GOLANGCI_LINT = bin/golangci-lint-$(GOLANGCI_VERSION)
|
||||
$(GOLANGCI_LINT):
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b bin v$(GOLANGCI_VERSION)
|
||||
@mv bin/golangci-lint "$(@)"
|
||||
|
||||
lint: fmt $(GOLANGCI_LINT) download ## Lints all code with golangci-lint
|
||||
@$(GOLANGCI_LINT) run
|
||||
|
||||
lint/fix: fmt $(GOLANGCI_LINT) download ## Fixes automatically fixable things like imports for the defined lint rules
|
||||
@$(GOLANGCI_LINT) run --fix
|
||||
|
||||
lint/reports: fmt $(GOLANGCI_LINT) download ## Fixes automatically fixable things like imports for the defined lint rules
|
||||
@$(GOLANGCI_LINT) run ./... --output.checkstyle.path stdout | awk '!/0 issues./' > out/lint.xml
|
||||
|
||||
test-clean:
|
||||
@go clean -testcache
|
||||
|
||||
tidy:
|
||||
@go mod tidy
|
||||
|
||||
test:
|
||||
@go test ./...
|
||||
|
||||
coverage: out/report.json ## Displays coverage per func on cli
|
||||
go tool cover -func=out/cover.out
|
||||
|
||||
html-coverage: out/report.json ## Displays the coverage results in the browser
|
||||
go tool cover -html=out/cover.out
|
||||
|
||||
test-reports: out/report.json
|
||||
|
||||
.PHONY: out/report.json
|
||||
out/report.json: out
|
||||
go test -v $$(go list ./... | grep -v '/tests') -tags=unit -coverprofile=out/cover.out -json | tee "$(@)"
|
||||
|
||||
clean:
|
||||
@rm -rf bin out
|
||||
|
||||
.PHONY: ado-git-setup
|
||||
ado-git-setup:
|
||||
# Add "dev.azure.com/schwarzit" to GOPRIVATE if not present
|
||||
@priv="$$(go env GOPRIVATE)"; \
|
||||
[[ "$$priv" =~ '(^|,)dev\.azure\.com(/|,|$)' ]] || go env -w "GOPRIVATE=$${priv:+$$priv,}dev.azure.com/schwarzit"
|
||||
# Configure HTTPS (with PAT) or SSH access to Go import paths
|
||||
@if [[ -n "$${ADO_PAT:+x}" ]]; then \
|
||||
git config --global "url.https://schwarzit:$${ADO_PAT}@dev.azure.com/schwarzit/.insteadof" 'https://dev.azure.com/schwarzit/'; \
|
||||
else \
|
||||
git config --global 'url.git@ssh.dev.azure.com:v3/schwarzit.insteadOf' 'https://dev.azure.com/schwarzit'; \
|
||||
fi
|
||||
|
||||
help:
|
||||
@echo 'Usage: make <OPTIONS> ... <TARGETS>'
|
||||
@echo ''
|
||||
@echo 'Available targets are:'
|
||||
@echo ''
|
||||
@grep -E '^[ a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
||||
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
@echo ''
|
||||
48
README.md
48
README.md
|
|
@ -1,3 +1,35 @@
|
|||
> ## DEPRECATION NOTICE
|
||||
>
|
||||
> ### Discontinuation of the current audit log system
|
||||
>
|
||||
> The audit log system provided to date will be discontinued in its current form.
|
||||
> This decision was made to pave the way for a new, more powerful audit log system that
|
||||
> will be provided in the future. The new system offers extended functionalities and
|
||||
> improved integration options, particularly with regard to the use and analysis of
|
||||
> audit data by our customers.
|
||||
>
|
||||
> ### What does it mean?
|
||||
> The existing audit log system will be supported until the new system is generally
|
||||
> available to customers on Mai 1, 2026.
|
||||
> **Services that are already sending audit log events to the existing audit log
|
||||
> system must continue to do so until the new system is GA** and further information
|
||||
> about the shutdown process is provided.
|
||||
> **Large volumes of new audit event types must not be sent to the existing audit log
|
||||
> system.**
|
||||
>
|
||||
> STACKIT services should start migrating to the new system now by sending data to
|
||||
> the new system (**in parallel**).
|
||||
> **The new audit log system may drop and does not guarantee to store events until
|
||||
> it will be GA**.
|
||||
> Further information on the changeover and how to use the new system can be found in the
|
||||
> [developer docs](https://developers.stackit.schwarz/domains/central-services/telemetry-router/integration/).
|
||||
>
|
||||
> We are confident that the new audit log system will make an important contribution to
|
||||
> improving the transparency, traceability, and integration for our customers.
|
||||
> If you have any questions or need assistance, the
|
||||
> [STACKIT Telemetry Hub](https://chat.google.com/room/AAQAf9NsX6M?cls=7) team will be
|
||||
> happy to help.
|
||||
|
||||
## audit-go
|
||||
|
||||
The audit-go library is the core library for validation and sending of audit events.
|
||||
|
|
@ -32,13 +64,7 @@ The code can be found in the [api_routable.go](./api_routable.go) and
|
|||
### Development
|
||||
|
||||
#### Go
|
||||
The current minimum toolchain version is **go1.22**.
|
||||
The toolchain version can be set as environment variable (either manually in the terminal
|
||||
or in the ~/.basrc or ~/.zshrc):
|
||||
|
||||
```shell
|
||||
export GOTOOLCHAIN=go1.22.7
|
||||
```
|
||||
The current minimum Go version is **go1.24.0**.
|
||||
|
||||
#### Linter
|
||||
|
||||
|
|
@ -46,7 +72,7 @@ The linter *golangci-lint* can either be installed via package manager (e.g. bre
|
|||
by running the following command in the terminal:
|
||||
|
||||
```shell
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.62.2
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.8.0
|
||||
```
|
||||
|
||||
#### Schema Generation
|
||||
|
|
@ -61,9 +87,9 @@ Buf and the required plugins can either be installed via package manager (e.g. b
|
|||
or manually by running:
|
||||
|
||||
```shell
|
||||
go install github.com/bufbuild/buf/cmd/buf@v1.47.2 #Pipeline: bufVersion
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.0 #Pipeline: protobufVersion, go.mod: buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go
|
||||
go install github.com/envoyproxy/protoc-gen-validate@v1.1.0 #Pipeline: protobufValidateVersion, go.mod: google.golang.org/protobuf
|
||||
go install github.com/bufbuild/buf/cmd/buf@v1.63.0 #Pipeline: bufVersion
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11 #Pipeline: protobufVersion, go.mod: buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go
|
||||
go install github.com/envoyproxy/protoc-gen-validate@v1.3.0 #Pipeline: protobufValidateVersion, go.mod: google.golang.org/protobuf
|
||||
```
|
||||
|
||||
Please check that the versions above match the versions in the *go.mod* file
|
||||
|
|
|
|||
|
|
@ -1,423 +0,0 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type MessagingApiMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MessagingApiMock) Send(
|
||||
ctx context.Context,
|
||||
topic string,
|
||||
data []byte,
|
||||
contentType string,
|
||||
applicationProperties map[string]any,
|
||||
) error {
|
||||
|
||||
args := m.Called(ctx, topic, data, contentType, applicationProperties)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MessagingApiMock) Close(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ProtobufValidatorMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *ProtobufValidatorMock) Validate(msg proto.Message) error {
|
||||
args := m.Called(msg)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
type TopicNameResolverMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *TopicNameResolverMock) Resolve(routableIdentifier *RoutableIdentifier) (string, error) {
|
||||
args := m.Called(routableIdentifier)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func NewValidator(t *testing.T) ProtobufValidator {
|
||||
validator, err := protovalidate.New()
|
||||
var protoValidator ProtobufValidator = validator
|
||||
assert.NoError(t, err)
|
||||
|
||||
return protoValidator
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_EventNil(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, nil, auditV1.Visibility_VISIBILITY_PUBLIC, nil)
|
||||
|
||||
assert.ErrorIs(t, err, ErrEventNil)
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_AuditEventValidationFailed(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event, objectIdentifier := newOrganizationAuditEvent(nil)
|
||||
event.LogName = ""
|
||||
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.EqualError(t, err, "validation error:\n - log_name: value is required [required]")
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_RoutableEventValidationFailed(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event, objectIdentifier := newOrganizationAuditEvent(nil)
|
||||
_, err := validateAndSerializePartially(validator, event, 3, NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.EqualError(t, err, "validation error:\n - visibility: value must be one of the defined enum values [enum.defined_only]")
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_CheckVisibility_Event(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event, objectIdentifier := newOrganizationAuditEvent(nil)
|
||||
|
||||
t.Run("Visibility public - object identifier nil", func(t *testing.T) {
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil)
|
||||
|
||||
assert.ErrorIs(t, err, ErrObjectIdentifierNil)
|
||||
})
|
||||
|
||||
t.Run("Visibility private - object identifier nil", func(t *testing.T) {
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil)
|
||||
|
||||
assert.ErrorIs(t, err, ErrObjectIdentifierNil)
|
||||
})
|
||||
|
||||
t.Run("Visibility public - object identifier system", func(t *testing.T) {
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier)
|
||||
|
||||
assert.ErrorIs(t, err, ErrObjectIdentifierVisibilityMismatch)
|
||||
})
|
||||
|
||||
t.Run("Visibility public - object identifier set", func(t *testing.T) {
|
||||
routableEvent, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, 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)
|
||||
|
||||
assert.ErrorIs(t, err, ErrAttributeIdentifierInvalid)
|
||||
})
|
||||
|
||||
t.Run("Visibility private - object identifier set", func(t *testing.T) {
|
||||
routableEvent, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, routableEvent)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_CheckVisibility_SystemEvent(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event := newSystemAuditEvent(nil)
|
||||
|
||||
t.Run("Visibility public - object identifier nil", func(t *testing.T) {
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil)
|
||||
|
||||
assert.ErrorIs(t, err, ErrObjectIdentifierNil)
|
||||
})
|
||||
|
||||
t.Run("Visibility private - object identifier nil", func(t *testing.T) {
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil)
|
||||
|
||||
assert.ErrorIs(t, err, ErrObjectIdentifierNil)
|
||||
})
|
||||
|
||||
t.Run("Visibility public - object identifier system", func(t *testing.T) {
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier)
|
||||
|
||||
assert.ErrorIs(t, err, 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)}))
|
||||
|
||||
assert.ErrorIs(t, err, ErrInvalidRoutableIdentifierForSystemEvent)
|
||||
})
|
||||
|
||||
t.Run("Visibility private - object identifier system", func(t *testing.T) {
|
||||
routableEvent, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, 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)}))
|
||||
|
||||
assert.ErrorIs(t, err, ErrInvalidRoutableIdentifierForSystemEvent)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_UnsupportedIdentifierType(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event, objectIdentifier := newFolderAuditEvent(nil)
|
||||
objectIdentifier.Type = "invalid"
|
||||
|
||||
_, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.ErrorIs(t, err, ErrUnsupportedRoutableType)
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_LogNameIdentifierMismatch(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
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))
|
||||
|
||||
assert.ErrorIs(t, err, 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))
|
||||
|
||||
assert.ErrorIs(t, err, ErrAttributeIdentifierInvalid)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_ResourceNameIdentifierMismatch(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
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))
|
||||
|
||||
assert.ErrorIs(t, err, 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))
|
||||
|
||||
assert.ErrorIs(t, err, ErrAttributeIdentifierInvalid)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_SystemEvent(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event := newSystemAuditEvent(nil)
|
||||
|
||||
routableEvent, err := validateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, 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))
|
||||
}
|
||||
|
||||
func Test_Send_TopicNameResolverNil(t *testing.T) {
|
||||
err := send(nil, nil, context.Background(), nil, nil)
|
||||
assert.ErrorIs(t, err, ErrTopicNameResolverNil)
|
||||
}
|
||||
|
||||
func Test_Send_TopicNameResolutionError(t *testing.T) {
|
||||
expectedError := errors.New("expected error")
|
||||
|
||||
topicNameResolverMock := TopicNameResolverMock{}
|
||||
topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", expectedError)
|
||||
var topicNameResolver TopicNameResolver = &topicNameResolverMock
|
||||
|
||||
var cloudEvent = CloudEvent{}
|
||||
|
||||
var messagingApi messaging.Api = &messaging.AmqpApi{}
|
||||
err := send(topicNameResolver, messagingApi, context.Background(), 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)
|
||||
}
|
||||
|
||||
func Test_Send_CloudEventNil(t *testing.T) {
|
||||
var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: "test"}
|
||||
var messagingApi messaging.Api = &messaging.AmqpApi{}
|
||||
|
||||
err := send(topicNameResolver, messagingApi, context.Background(), nil, nil)
|
||||
assert.ErrorIs(t, err, ErrCloudEventNil)
|
||||
}
|
||||
|
||||
func Test_Send_ObjectIdentifierNil(t *testing.T) {
|
||||
var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: "test"}
|
||||
var messagingApi messaging.Api = &messaging.AmqpApi{}
|
||||
var cloudEvent = CloudEvent{}
|
||||
|
||||
err := send(topicNameResolver, messagingApi, context.Background(), nil, &cloudEvent)
|
||||
assert.ErrorIs(t, err, ErrObjectIdentifierNil)
|
||||
}
|
||||
|
||||
func Test_Send_UnsupportedObjectIdentifierType(t *testing.T) {
|
||||
var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: "test"}
|
||||
var messagingApi messaging.Api = &messaging.AmqpApi{}
|
||||
var cloudEvent = CloudEvent{}
|
||||
var objectIdentifier = auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: "unsupported"}
|
||||
|
||||
err := send(topicNameResolver, messagingApi, context.Background(), NewRoutableIdentifier(&objectIdentifier), &cloudEvent)
|
||||
assert.ErrorIs(t, err, ErrUnsupportedRoutableType)
|
||||
}
|
||||
|
||||
func Test_Send(t *testing.T) {
|
||||
topicNameResolverMock := TopicNameResolverMock{}
|
||||
topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", nil)
|
||||
var topicNameResolver 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 cloudEvent = CloudEvent{}
|
||||
assert.NoError(t, send(topicNameResolver, messagingApi, context.Background(), 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
|
||||
|
||||
messagingApiMock := MessagingApiMock{}
|
||||
messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
var messagingApi messaging.Api = &messagingApiMock
|
||||
|
||||
traceState := "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"
|
||||
traceParent := "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
|
||||
expectedTime := time.Now()
|
||||
var cloudEvent = CloudEvent{
|
||||
SpecVersion: "1.0",
|
||||
Source: "resourcemanager",
|
||||
Id: "id",
|
||||
Time: expectedTime,
|
||||
DataContentType: ContentTypeCloudEventsProtobuf,
|
||||
DataType: "type",
|
||||
Subject: "subject",
|
||||
TraceParent: &traceParent,
|
||||
TraceState: &traceState,
|
||||
}
|
||||
assert.NoError(t, send(topicNameResolver, messagingApi, context.Background(), RoutableSystemIdentifier, &cloudEvent))
|
||||
assert.True(t, messagingApiMock.AssertNumberOfCalls(t, "Send", 1))
|
||||
|
||||
arguments := messagingApiMock.Calls[0].Arguments
|
||||
topic := arguments.Get(1).(string)
|
||||
assert.Equal(t, "topic", topic)
|
||||
|
||||
contentType := arguments.Get(3).(string)
|
||||
assert.Equal(t, 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, "type", applicationProperties["cloudEvents:type"])
|
||||
assert.Equal(t, "subject", applicationProperties["cloudEvents:subject"])
|
||||
assert.Equal(t, traceParent, applicationProperties["cloudEvents:traceparent"])
|
||||
assert.Equal(t, traceState, applicationProperties["cloudEvents:tracestate"])
|
||||
messagingApiMock.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func Test_SendWithoutOptionalHeadersSet(t *testing.T) {
|
||||
topicNameResolverMock := TopicNameResolverMock{}
|
||||
topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", nil)
|
||||
var topicNameResolver TopicNameResolver = &topicNameResolverMock
|
||||
|
||||
messagingApiMock := MessagingApiMock{}
|
||||
messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
var messagingApi messaging.Api = &messagingApiMock
|
||||
|
||||
expectedTime := time.Now()
|
||||
var cloudEvent = CloudEvent{
|
||||
SpecVersion: "1.0",
|
||||
Source: "resourcemanager",
|
||||
Id: "id",
|
||||
Time: expectedTime,
|
||||
DataContentType: ContentTypeCloudEventsProtobuf,
|
||||
DataType: "type",
|
||||
Subject: "subject",
|
||||
}
|
||||
assert.NoError(t, send(topicNameResolver, messagingApi, context.Background(), RoutableSystemIdentifier, &cloudEvent))
|
||||
assert.True(t, messagingApiMock.AssertNumberOfCalls(t, "Send", 1))
|
||||
|
||||
arguments := messagingApiMock.Calls[0].Arguments
|
||||
topic := arguments.Get(1).(string)
|
||||
assert.Equal(t, "topic", topic)
|
||||
|
||||
contentType := arguments.Get(3).(string)
|
||||
assert.Equal(t, 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, "type", applicationProperties["cloudEvents:type"])
|
||||
assert.Equal(t, "subject", applicationProperties["cloudEvents:subject"])
|
||||
assert.Equal(t, nil, applicationProperties["cloudEvents:traceparent"])
|
||||
assert.Equal(t, nil, applicationProperties["cloudEvents:tracestate"])
|
||||
messagingApiMock.AssertExpectations(t)
|
||||
}
|
||||
|
|
@ -1,241 +0,0 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Azure/go-amqp"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ConnectionClosedError = errors.New("amqp connection is closed")
|
||||
|
||||
type AmqpConnection struct {
|
||||
connectionName string
|
||||
lock sync.RWMutex
|
||||
brokerUrl string
|
||||
username string
|
||||
password string
|
||||
conn *amqp.Conn
|
||||
}
|
||||
|
||||
func NewAmqpConnection(config *AmqpConnectionConfig, connectionName string) *AmqpConnection {
|
||||
return &AmqpConnection{
|
||||
connectionName: connectionName,
|
||||
lock: sync.RWMutex{},
|
||||
brokerUrl: config.BrokerUrl,
|
||||
username: config.Username,
|
||||
password: config.Password,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) NewReceiver(ctx context.Context, source string) (*AmqpReceiverSession, error) {
|
||||
if c.conn == nil {
|
||||
return nil, errors.New("connection is not initialized")
|
||||
}
|
||||
|
||||
if c.internalIsClosed() {
|
||||
return nil, ConnectionClosedError
|
||||
}
|
||||
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
// new session
|
||||
session, err := c.conn.NewSession(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new session: %w", err)
|
||||
}
|
||||
|
||||
// new receiver
|
||||
receiver, err := session.NewReceiver(ctx, source, nil)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("new internal receiver: %w", err)
|
||||
|
||||
closeErr := session.Close(ctx)
|
||||
if closeErr != nil {
|
||||
return nil, errors.Join(fmt.Errorf("close session: %w", err), err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AmqpReceiverSession{session, receiver}, nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) NewSender(ctx context.Context, topic string) (*AmqpSenderSession, error) {
|
||||
if c.conn == nil {
|
||||
return nil, errors.New("connection is not initialized")
|
||||
}
|
||||
|
||||
if c.internalIsClosed() {
|
||||
return nil, ConnectionClosedError
|
||||
}
|
||||
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
// new session
|
||||
newSession, err := c.conn.NewSession(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new session: %w", err)
|
||||
}
|
||||
|
||||
// new sender
|
||||
newSender, err := newSession.NewSender(ctx, topic, nil)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("new internal sender: %w", err)
|
||||
|
||||
closeErr := newSession.Close(ctx)
|
||||
if closeErr != nil {
|
||||
return nil, errors.Join(fmt.Errorf("close session: %w", err), err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AmqpSenderSession{newSession, newSender}, nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) ResetConnectionAndRetryIfErrorWithReturnValue(opName string, fn func(ctx context.Context) (any, error)) (any, error) {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
result, err := fn(ctx)
|
||||
cancelFn()
|
||||
|
||||
if err != nil {
|
||||
slog.Info(fmt.Sprintf("amqp connection: %s", opName), slog.Any("connection", c.connectionName), slog.Any("err", err))
|
||||
err := c.ResetConnection(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reset connection: %w", err)
|
||||
}
|
||||
|
||||
newCtx, closeFn := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer closeFn()
|
||||
|
||||
// Retry
|
||||
return fn(newCtx)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) ResetConnection(ctx context.Context) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
subCtx, cancel := context.WithTimeout(ctx, disconnectionTimeoutSeconds*time.Second)
|
||||
err := c.internalClose(subCtx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
slog.Warn("amqp connection: reset: failed to close amqp connection", slog.Any("err", err))
|
||||
}
|
||||
|
||||
subCtx, cancel = context.WithTimeout(ctx, connectionTimeoutSeconds*time.Second)
|
||||
err = c.internalConnect(subCtx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
slog.Warn("amqp connection: reset: failed to connect to amqp server, retry..", slog.Any("err", err))
|
||||
subCtx, cancel = context.WithTimeout(ctx, connectionTimeoutSeconds*time.Second)
|
||||
err = c.internalConnect(subCtx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("connect: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func As[T any](value any, err error) (*T, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
castedValue, isType := value.(*T)
|
||||
if !isType {
|
||||
return nil, fmt.Errorf("could not cast value: %T", value)
|
||||
}
|
||||
return castedValue, nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) Connect(ctx context.Context) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
subCtx, cancel := context.WithTimeout(ctx, connectionTimeoutSeconds*time.Second)
|
||||
defer cancel()
|
||||
return c.internalConnect(subCtx)
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) internalConnect(ctx context.Context) error {
|
||||
if c.conn == nil {
|
||||
// Set credentials if specified
|
||||
auth := amqp.SASLTypeAnonymous()
|
||||
if c.username != "" && c.password != "" {
|
||||
auth = amqp.SASLTypePlain(c.username, c.password)
|
||||
} else {
|
||||
slog.Debug("amqp connection: connect: using anonymous messaging")
|
||||
}
|
||||
options := &amqp.ConnOptions{
|
||||
SASLType: auth,
|
||||
}
|
||||
|
||||
// Initialize connection
|
||||
conn, err := amqp.Dial(ctx, c.brokerUrl, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dial: %w", err)
|
||||
}
|
||||
c.conn = conn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) Close(ctx context.Context) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
subCtx, cancel := context.WithTimeout(ctx, disconnectionTimeoutSeconds*time.Second)
|
||||
defer cancel()
|
||||
|
||||
return c.internalClose(subCtx)
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) internalClose(ctx context.Context) error {
|
||||
if c.conn != nil {
|
||||
closeErrors := make([]error, 0)
|
||||
closeErrors = c.internalCloseConnection(closeErrors)
|
||||
|
||||
// return errors or nil
|
||||
if len(closeErrors) > 0 {
|
||||
return errors.Join(closeErrors...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) internalCloseConnection(closeErrors []error) []error {
|
||||
err := c.conn.Close()
|
||||
if err != nil {
|
||||
closeErrors = append(closeErrors, err)
|
||||
}
|
||||
return closeErrors
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) IsClosed() bool {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
return c.internalIsClosed()
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) internalIsClosed() bool {
|
||||
if c.conn == nil {
|
||||
return true
|
||||
}
|
||||
select {
|
||||
case <-c.conn.Done():
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type AmqpConnectionPool struct {
|
||||
config AmqpConnectionPoolConfig
|
||||
connectionName string
|
||||
connections []*AmqpConnection
|
||||
handleOffset int
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
type ConnectionPoolHandle struct {
|
||||
connectionOffset int
|
||||
}
|
||||
|
||||
func NewAmqpConnectionPool(ctx context.Context, config AmqpConnectionPoolConfig, connectionName string) (*AmqpConnectionPool, error) {
|
||||
pool := &AmqpConnectionPool{
|
||||
config: config,
|
||||
connectionName: connectionName,
|
||||
connections: make([]*AmqpConnection, 0),
|
||||
handleOffset: 0,
|
||||
lock: sync.RWMutex{},
|
||||
}
|
||||
|
||||
if err := pool.initializeConnections(); err != nil {
|
||||
if closeErr := pool.Close(ctx); closeErr != nil {
|
||||
return nil, errors.Join(err, fmt.Errorf("initialize amqp connection: pool closed: %w", closeErr))
|
||||
}
|
||||
return nil, fmt.Errorf("initialize connections: %w", err)
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) initializeConnections() error {
|
||||
if len(p.connections) < p.config.PoolSize {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
numMissingConnections := p.config.PoolSize - len(p.connections)
|
||||
|
||||
for i := 0; i < numMissingConnections; i++ {
|
||||
if err := p.internalAddConnection(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) internalAddConnection() error {
|
||||
newConnection, err := p.internalNewConnection(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("new connection: %w", err)
|
||||
}
|
||||
p.connections = append(p.connections, newConnection)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) internalNewConnection(ctx context.Context) (*AmqpConnection, error) {
|
||||
conn := NewAmqpConnection(&p.config.Parameters, p.connectionName)
|
||||
if err := conn.Connect(ctx); err != nil {
|
||||
slog.Warn("amqp connection: failed to connect to amqp broker", slog.Any("err", err))
|
||||
|
||||
// retry
|
||||
if err = conn.Connect(ctx); err != nil {
|
||||
connectErr := fmt.Errorf("failed to connect to amqp broker: %w", err)
|
||||
if closeErr := conn.Close(ctx); closeErr != nil {
|
||||
return nil, errors.Join(connectErr, fmt.Errorf("close connection: %w", closeErr))
|
||||
}
|
||||
return nil, connectErr
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) Close(ctx context.Context) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
closeErrors := make([]error, 0)
|
||||
for _, conn := range p.connections {
|
||||
if err := conn.Close(ctx); err != nil {
|
||||
closeErrors = append(closeErrors, fmt.Errorf("connection: close: %w", err))
|
||||
}
|
||||
}
|
||||
if len(closeErrors) > 0 {
|
||||
return errors.Join(closeErrors...)
|
||||
}
|
||||
p.connections = make([]*AmqpConnection, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) NewHandle() *ConnectionPoolHandle {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
offset := p.handleOffset
|
||||
p.handleOffset += 1
|
||||
|
||||
offset = offset % p.config.PoolSize
|
||||
|
||||
return &ConnectionPoolHandle{
|
||||
connectionOffset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) GetConnection(handle *ConnectionPoolHandle) (*AmqpConnection, error) {
|
||||
// get the requested connection or another one
|
||||
conn, addConnection := p.nextConnectionFromQueue(handle)
|
||||
|
||||
// renew the requested connection if the request connection is closed
|
||||
if conn == nil || addConnection {
|
||||
p.lock.Lock()
|
||||
|
||||
// check that accessing the pool only with a valid index (out of bounds should only occur on shutdown)
|
||||
if handle.connectionOffset < len(p.connections) && p.connections[handle.connectionOffset] == nil {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), connectionTimeoutSeconds)
|
||||
connection, err := p.internalNewConnection(ctx)
|
||||
cancelFn()
|
||||
if err != nil {
|
||||
p.lock.Unlock()
|
||||
return nil, fmt.Errorf("renew connection: %w", err)
|
||||
}
|
||||
p.connections[handle.connectionOffset] = connection
|
||||
}
|
||||
p.lock.Unlock()
|
||||
}
|
||||
|
||||
// return the previously returned connection if it is not nil
|
||||
if conn != nil {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// try to return the renewed connection or another one
|
||||
conn, _ = p.nextConnectionFromQueue(handle)
|
||||
if conn == nil {
|
||||
return nil, errors.New("pool is empty")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) nextConnectionFromQueue(handle *ConnectionPoolHandle) (*AmqpConnection, bool) {
|
||||
// return the next possible index (including the retry offset)
|
||||
nextIndex := func(idx int) int {
|
||||
if idx+handle.connectionOffset >= p.config.PoolSize {
|
||||
return idx + handle.connectionOffset - p.config.PoolSize
|
||||
} else {
|
||||
return idx + handle.connectionOffset
|
||||
}
|
||||
}
|
||||
|
||||
// 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++ {
|
||||
|
||||
// get the next possible connection (considering the retry index)
|
||||
idx := nextIndex(i)
|
||||
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()
|
||||
|
||||
// remember that the requested is closed, retry with the next
|
||||
if conn == nil {
|
||||
addConnection = true
|
||||
continue
|
||||
}
|
||||
|
||||
// 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()
|
||||
continue
|
||||
}
|
||||
|
||||
return conn, addConnection
|
||||
}
|
||||
return nil, true
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/Azure/go-amqp"
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AmqpReceiverSession struct {
|
||||
session *amqp.Session
|
||||
receiver *amqp.Receiver
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) NewReceiverSession(source string) (*AmqpReceiverSession, error) {
|
||||
if c.IsClosed() {
|
||||
return nil, ConnectionClosedError
|
||||
}
|
||||
|
||||
return As[AmqpReceiverSession](c.ResetConnectionAndRetryIfErrorWithReturnValue("newReceiverSession", func(ctx context.Context) (any, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
return c.NewReceiver(ctx, source)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *AmqpReceiverSession) AcceptMessage(message *amqp.Message) error {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancelFn()
|
||||
return s.receiver.AcceptMessage(ctx, message)
|
||||
}
|
||||
|
||||
func (s *AmqpReceiverSession) Receive(opts *amqp.ReceiveOptions) (*amqp.Message, error) {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancelFn()
|
||||
message, err := s.receiver.Receive(ctx, opts)
|
||||
// return nil if the timeout exceeded
|
||||
if err != nil && errors.Is(err, context.DeadlineExceeded) {
|
||||
return nil, nil
|
||||
}
|
||||
return message, err
|
||||
}
|
||||
|
||||
func (s *AmqpReceiverSession) Close() error {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancelFn()
|
||||
|
||||
var closeErrors []error
|
||||
receiverErr := s.receiver.Close(ctx)
|
||||
if receiverErr != nil {
|
||||
closeErrors = append(closeErrors, receiverErr)
|
||||
}
|
||||
sessionErr := s.session.Close(ctx)
|
||||
if sessionErr != nil {
|
||||
closeErrors = append(closeErrors, sessionErr)
|
||||
}
|
||||
|
||||
if len(closeErrors) > 0 {
|
||||
return errors.Join(closeErrors...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AmqpReceiverSession) CloseSilently() {
|
||||
err := s.Close()
|
||||
if err != nil {
|
||||
slog.Error("error closing receiver session", slog.Any("err", err))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/log"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
config AmqpConnectionPoolConfig
|
||||
connection *AmqpConnection
|
||||
connectionPool *AmqpConnectionPool
|
||||
connectionPoolHandle *ConnectionPoolHandle
|
||||
senderCache map[string]*AmqpSenderSession
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
var _ Api = &AmqpApi{}
|
||||
|
||||
func NewAmqpApi(amqpConfig AmqpConnectionPoolConfig) (Api, error) {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancelFn()
|
||||
connectionPool, err := NewAmqpConnectionPool(ctx, amqpConfig, "sdk")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new amqp connection pool: %w", err)
|
||||
}
|
||||
|
||||
amqpApi := &AmqpApi{config: amqpConfig,
|
||||
connectionPool: connectionPool,
|
||||
connectionPoolHandle: connectionPool.NewHandle(),
|
||||
senderCache: make(map[string]*AmqpSenderSession),
|
||||
}
|
||||
|
||||
var messagingApi Api = amqpApi
|
||||
return messagingApi, nil
|
||||
}
|
||||
|
||||
// Send implements Api.Send.
|
||||
// If errors occur the connection to the messaging system will be closed and re-established.
|
||||
func (a *AmqpApi) Send(ctx context.Context, topic string, data []byte, contentType string, applicationProperties map[string]any) error {
|
||||
|
||||
a.lock.RLock()
|
||||
connectionIsClosed := a.connection == nil || a.connection.IsClosed()
|
||||
a.lock.RUnlock()
|
||||
if connectionIsClosed {
|
||||
connection, err := a.connectionPool.GetConnection(a.connectionPoolHandle)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get connection: %w", err)
|
||||
}
|
||||
a.lock.Lock()
|
||||
a.connection = connection
|
||||
a.lock.Unlock()
|
||||
}
|
||||
|
||||
a.lock.RLock()
|
||||
var sender = a.senderCache[topic]
|
||||
a.lock.RUnlock()
|
||||
if sender == nil {
|
||||
a.lock.RLock()
|
||||
ctx, cancelFn := context.WithTimeout(ctx, 10*time.Second)
|
||||
senderSession, err := a.connection.NewSender(ctx, topic)
|
||||
cancelFn()
|
||||
a.lock.RUnlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("new sender: %w", err)
|
||||
}
|
||||
a.lock.Lock()
|
||||
a.senderCache[topic] = senderSession
|
||||
a.lock.Unlock()
|
||||
sender = senderSession
|
||||
}
|
||||
|
||||
wrappedData := [][]byte{data}
|
||||
if err := sender.Send(topic, wrappedData, contentType, applicationProperties); err != nil {
|
||||
return fmt.Errorf("send: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements Api.Close
|
||||
func (a *AmqpApi) Close(ctx context.Context) error {
|
||||
log.AuditLogger.Info("close audit messaging connection")
|
||||
var closeErrors []error
|
||||
a.lock.RLock()
|
||||
numSenderCacheItems := len(a.senderCache)
|
||||
a.lock.RUnlock()
|
||||
if numSenderCacheItems > 0 {
|
||||
a.lock.Lock()
|
||||
for _, session := range a.senderCache {
|
||||
if err := session.Close(); err != nil {
|
||||
closeErrors = append(closeErrors, fmt.Errorf("close session: %w", err))
|
||||
}
|
||||
}
|
||||
clear(a.senderCache)
|
||||
a.lock.Unlock()
|
||||
}
|
||||
if a.connectionPool != nil {
|
||||
if err := a.connectionPool.Close(ctx); err != nil {
|
||||
closeErrors = append(closeErrors, fmt.Errorf("close pool: %w", err))
|
||||
}
|
||||
}
|
||||
if len(closeErrors) > 0 {
|
||||
return fmt.Errorf("close: %w", errors.Join(closeErrors...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
//type AmqpSessionMock struct {
|
||||
// mock.Mock
|
||||
//}
|
||||
//
|
||||
//func (m *AmqpSessionMock) NewSender(ctx context.Context, target string, opts *amqp.SenderOptions) (AmqpSender, error) {
|
||||
// args := m.Called(ctx, target, opts)
|
||||
// var sender AmqpSender = nil
|
||||
// if args.Get(0) != nil {
|
||||
// sender = args.Get(0).(AmqpSender)
|
||||
// }
|
||||
// err := args.Error(1)
|
||||
// return sender, err
|
||||
//}
|
||||
//
|
||||
//func (m *AmqpSessionMock) Close(ctx context.Context) error {
|
||||
// args := m.Called(ctx)
|
||||
// return args.Error(0)
|
||||
//}
|
||||
//
|
||||
//type AmqpSenderMock struct {
|
||||
// mock.Mock
|
||||
//}
|
||||
//
|
||||
//func (m *AmqpSenderMock) Send(ctx context.Context, msg *amqp.Message, opts *amqp.SendOptions) error {
|
||||
// args := m.Called(ctx, msg, opts)
|
||||
// return args.Error(0)
|
||||
//}
|
||||
//
|
||||
//func (m *AmqpSenderMock) Close(ctx context.Context) error {
|
||||
// args := m.Called(ctx)
|
||||
// return args.Error(0)
|
||||
//}
|
||||
|
||||
func Test_NewAmqpMessagingApi(t *testing.T) {
|
||||
_, err := NewAmqpApi(
|
||||
AmqpConnectionPoolConfig{
|
||||
Parameters: AmqpConnectionConfig{BrokerUrl: "not-handled-protocol://localhost:5672"},
|
||||
PoolSize: 1,
|
||||
})
|
||||
assert.EqualError(t, err, "new amqp connection pool: initialize connections: new connection: failed to connect to amqp broker: dial: unsupported scheme \"not-handled-protocol\"")
|
||||
}
|
||||
|
||||
func Test_AmqpMessagingApi_Send(t *testing.T) {
|
||||
// Specify test timeout
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
defer cancelFn()
|
||||
|
||||
// Start solace docker container
|
||||
solaceContainer, err := 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},
|
||||
PoolSize: 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = api.Send(ctx, "topic-name", []byte{}, "application/json", make(map[string]any))
|
||||
assert.EqualError(t, err, "send: topic \"topic-name\" name lacks mandatory prefix \"topic://\"")
|
||||
})
|
||||
|
||||
t.Run("Close connection without errors", func(t *testing.T) {
|
||||
defer solaceContainer.StopOnError()
|
||||
|
||||
// Initialize the solace queue
|
||||
topicSubscriptionTopicPattern := "auditlog/>"
|
||||
queueName := "close-connection-without-error"
|
||||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
topicName := fmt.Sprintf("topic://auditlog/%s", "amqp-close-connection")
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
api, err := NewAmqpApi(AmqpConnectionPoolConfig{
|
||||
Parameters: AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString},
|
||||
PoolSize: 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = api.Close(ctx)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
//t.Run("New sender call returns error", func(t *testing.T) {
|
||||
// defer solaceContainer.StopOnError()
|
||||
//
|
||||
// // Initialize the solace queue
|
||||
// topicSubscriptionTopicPattern := "auditlog/>"
|
||||
// queueName := "messaging-new-sender"
|
||||
// assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
// assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
// topicName := fmt.Sprintf("topic://auditlog/%s", "amqp-no-new-sender")
|
||||
// assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
//
|
||||
// api, err := NewAmqpApi(AmqpConnectionPoolConfig{
|
||||
// Parameters: AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString},
|
||||
// PoolSize: 1,
|
||||
// })
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// expectedError := errors.New("expected error")
|
||||
//
|
||||
// // Set mock session
|
||||
// sessionMock := AmqpSessionMock{}
|
||||
// sessionMock.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(nil, expectedError)
|
||||
// sessionMock.On("Close", mock.Anything).Return(nil)
|
||||
//
|
||||
// var amqpSession AmqpSession = &sessionMock
|
||||
// api.session = amqpSession
|
||||
//
|
||||
// // It's expected that the test succeeds.
|
||||
// // First the session is closed as it returns the expected error
|
||||
// // Then the retry mechanism restarts the connection and successfully sends the data
|
||||
// value := "test"
|
||||
// err = api.Send(ctx, topicName, []byte(value), "application/json", make(map[string]any))
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// // Check that the mock was called
|
||||
// assert.True(t, sessionMock.AssertNumberOfCalls(t, "NewSender", 1))
|
||||
// assert.True(t, sessionMock.AssertNumberOfCalls(t, "Close", 1))
|
||||
//
|
||||
// message, err := solaceContainer.NextMessage(ctx, fmt.Sprintf("queue://%s", queueName), true)
|
||||
// assert.NoError(t, err)
|
||||
// assert.Equal(t, value, string(message.Data[0]))
|
||||
// assert.Equal(t, topicName, *message.Properties.To)
|
||||
//})
|
||||
//
|
||||
//t.Run("Send call on sender returns error", func(t *testing.T) {
|
||||
// defer solaceContainer.StopOnError()
|
||||
//
|
||||
// // Initialize the solace queue
|
||||
// topicSubscriptionTopicPattern := "auditlog/>"
|
||||
// queueName := "messaging-sender-error"
|
||||
// assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
// assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
// topicName := fmt.Sprintf("topic://auditlog/%s", "amqp-sender-error")
|
||||
// assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
//
|
||||
// api := &AmqpApi{config: AmqpConfig{URL: solaceContainer.AmqpConnectionString}}
|
||||
// err := api.connect()
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// expectedError := errors.New("expected error")
|
||||
//
|
||||
// // Instantiate mock sender
|
||||
// senderMock := AmqpSenderMock{}
|
||||
// senderMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(expectedError)
|
||||
// senderMock.On("Close", mock.Anything).Return(nil)
|
||||
// var amqpSender AmqpSender = &senderMock
|
||||
//
|
||||
// // Set mock session
|
||||
// sessionMock := AmqpSessionMock{}
|
||||
// sessionMock.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(&amqpSender, nil)
|
||||
// sessionMock.On("Close", mock.Anything).Return(nil)
|
||||
//
|
||||
// var amqpSession AmqpSession = &sessionMock
|
||||
// api.session = amqpSession
|
||||
//
|
||||
// // It's expected that the test succeeds.
|
||||
// // First the sender and session are closed as the sender returns the expected error
|
||||
// // Then the retry mechanism restarts the connection and successfully sends the data
|
||||
// value := "test"
|
||||
// err = api.Send(ctx, topicName, []byte(value), "application/json", make(map[string]any))
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// // Check that the mocks were called
|
||||
// assert.True(t, sessionMock.AssertNumberOfCalls(t, "NewSender", 1))
|
||||
// assert.True(t, sessionMock.AssertNumberOfCalls(t, "Close", 1))
|
||||
// assert.True(t, senderMock.AssertNumberOfCalls(t, "Send", 1))
|
||||
// assert.True(t, senderMock.AssertNumberOfCalls(t, "Close", 1))
|
||||
//
|
||||
// message, err := solaceContainer.NextMessage(ctx, fmt.Sprintf("queue://%s", queueName), true)
|
||||
// assert.NoError(t, err)
|
||||
// assert.Equal(t, value, string(message.Data[0]))
|
||||
// assert.Equal(t, topicName, *message.Properties.To)
|
||||
//})
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.0
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: audit/v1/audit_event.proto
|
||||
|
||||
|
|
@ -15,6 +15,7 @@ import (
|
|||
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -520,8 +521,8 @@ type AuthenticationInfo struct {
|
|||
// The email address of the authenticated user.
|
||||
// Service accounts have email addresses that can be used.
|
||||
//
|
||||
// Required: true
|
||||
PrincipalEmail string `protobuf:"bytes,2,opt,name=principal_email,json=principalEmail,proto3" json:"principal_email,omitempty"`
|
||||
// Required: false
|
||||
PrincipalEmail *string `protobuf:"bytes,2,opt,name=principal_email,json=principalEmail,proto3,oneof" json:"principal_email,omitempty"`
|
||||
// The name of the service account used to create or exchange
|
||||
// credentials for authenticating the service account making the request.
|
||||
//
|
||||
|
|
@ -583,8 +584,8 @@ func (x *AuthenticationInfo) GetPrincipalId() string {
|
|||
}
|
||||
|
||||
func (x *AuthenticationInfo) GetPrincipalEmail() string {
|
||||
if x != nil {
|
||||
return x.PrincipalEmail
|
||||
if x != nil && x.PrincipalEmail != nil {
|
||||
return *x.PrincipalEmail
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
@ -1464,335 +1465,152 @@ func (x *ServiceAccountDelegationInfo_IdpPrincipal) GetServiceMetadata() *struct
|
|||
|
||||
var File_audit_v1_audit_event_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_audit_v1_audit_event_proto_rawDesc = []byte{
|
||||
0x0a, 0x1a, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74,
|
||||
0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x61, 0x75,
|
||||
0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69,
|
||||
0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x22, 0xe2, 0x04, 0x0a, 0x0d, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x45,
|
||||
0x6e, 0x74, 0x72, 0x79, 0x12, 0x78, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x5d, 0xba, 0x48, 0x5a, 0xc8, 0x01, 0x01, 0x72, 0x55,
|
||||
0x32, 0x53, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x2d, 0x5d, 0x2b, 0x2f, 0x5b, 0x61, 0x2d, 0x7a, 0x30,
|
||||
0x2d, 0x39, 0x2d, 0x5d, 0x2b, 0x2f, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x28, 0x3f, 0x3a, 0x61, 0x64,
|
||||
0x6d, 0x69, 0x6e, 0x2d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x7c, 0x73, 0x79, 0x73,
|
||||
0x74, 0x65, 0x6d, 0x2d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x7c, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79,
|
||||
0x2d, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x7c, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x61, 0x63, 0x63,
|
||||
0x65, 0x73, 0x73, 0x29, 0x24, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3f,
|
||||
0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01,
|
||||
0x01, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12,
|
||||
0x4c, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x2f, 0xba, 0x48, 0x2c, 0xc8, 0x01, 0x01, 0x72, 0x27, 0x32, 0x25, 0x5e, 0x5b,
|
||||
0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x2f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b,
|
||||
0x2f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b, 0x2f, 0x5b, 0x30, 0x2d, 0x39,
|
||||
0x5d, 0x2b, 0x24, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x49, 0x64, 0x12, 0x3b, 0x0a,
|
||||
0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e,
|
||||
0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x4c, 0x6f,
|
||||
0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x36, 0x0a, 0x0e, 0x63, 0x6f,
|
||||
0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x48, 0x00,
|
||||
0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88,
|
||||
0x01, 0x01, 0x12, 0x45, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18,
|
||||
0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
||||
0x70, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0xb2, 0x01, 0x02, 0x38, 0x01, 0x52, 0x09,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3e, 0x0a, 0x08, 0x73, 0x65, 0x76,
|
||||
0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x75,
|
||||
0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69,
|
||||
0x74, 0x79, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52,
|
||||
0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62,
|
||||
0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x3a, 0x02, 0x38, 0x01, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0xab, 0x06, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69,
|
||||
0x74, 0x4c, 0x6f, 0x67, 0x12, 0x2d, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8,
|
||||
0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x77, 0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x50, 0xba, 0x48, 0x4d,
|
||||
0xc8, 0x01, 0x01, 0x72, 0x48, 0x10, 0x01, 0x18, 0xff, 0x01, 0x32, 0x41, 0x5e, 0x73, 0x74, 0x61,
|
||||
0x63, 0x6b, 0x69, 0x74, 0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b,
|
||||
0x5c, 0x2e, 0x28, 0x3f, 0x3a, 0x76, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x5c, 0x2e, 0x29, 0x3f,
|
||||
0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x2e, 0x5d, 0x2b, 0x5c, 0x2e,
|
||||
0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b, 0x24, 0x52, 0x0d, 0x6f,
|
||||
0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x63, 0x0a, 0x0d,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x09, 0x42, 0x3e, 0xba, 0x48, 0x3b, 0xc8, 0x01, 0x01, 0x72, 0x36, 0x10, 0x01, 0x18,
|
||||
0xff, 0x01, 0x32, 0x2f, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x2b, 0x2f, 0x5b, 0x61, 0x2d, 0x7a,
|
||||
0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b, 0x28, 0x3f, 0x3a, 0x2f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d,
|
||||
0x39, 0x2d, 0x5d, 0x2b, 0x2f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5f, 0x5d, 0x2b,
|
||||
0x29, 0x2a, 0x24, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61, 0x6d,
|
||||
0x65, 0x12, 0x55, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
|
||||
0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x06, 0xba, 0x48,
|
||||
0x03, 0xc8, 0x01, 0x01, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4a, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68,
|
||||
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x05,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x52, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4c, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f,
|
||||
0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
|
||||
0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01,
|
||||
0x01, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x12, 0x36, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, 0x52, 0x07,
|
||||
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x88, 0x01, 0x01, 0x12, 0x4f, 0x0a, 0x11, 0x72, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18,
|
||||
0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x10, 0x72, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x38, 0x0a, 0x08, 0x72,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x01, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x88, 0x01, 0x01, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
|
||||
0x48, 0x02, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42,
|
||||
0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f,
|
||||
0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, 0x65, 0x74,
|
||||
0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf3, 0x02, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x0c,
|
||||
0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b,
|
||||
0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x0f, 0x70,
|
||||
0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x0a, 0xc8, 0x01, 0x01, 0x72, 0x05, 0x10, 0x01,
|
||||
0x18, 0xff, 0x01, 0x52, 0x0e, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x45, 0x6d,
|
||||
0x61, 0x69, 0x6c, 0x12, 0x6e, 0x0a, 0x14, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61,
|
||||
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x37, 0xba, 0x48, 0x34, 0x72, 0x32, 0x32, 0x30, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x2d,
|
||||
0x5d, 0x2b, 0x2f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b, 0x2f, 0x73, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x5b,
|
||||
0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b, 0x24, 0x48, 0x00, 0x52, 0x12, 0x73, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65,
|
||||
0x88, 0x01, 0x01, 0x12, 0x6d, 0x0a, 0x1f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61,
|
||||
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61,
|
||||
0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41,
|
||||
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x1c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e,
|
||||
0x66, 0x6f, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61,
|
||||
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xf2, 0x01, 0x0a, 0x11,
|
||||
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x12, 0x55, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x42, 0x39, 0xba, 0x48, 0x36, 0xc8, 0x01, 0x01, 0x72, 0x31, 0x32, 0x2f, 0x5e,
|
||||
0x5b, 0x61, 0x2d, 0x7a, 0x5d, 0x2b, 0x2f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d,
|
||||
0x2b, 0x28, 0x3f, 0x3a, 0x2f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b, 0x2f,
|
||||
0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5f, 0x5d, 0x2b, 0x29, 0x2a, 0x24, 0x52, 0x08,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d,
|
||||
0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xba, 0x48,
|
||||
0x24, 0x72, 0x22, 0x32, 0x20, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x2d, 0x5d, 0x2b, 0x28, 0x3f, 0x3a,
|
||||
0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x2d, 0x5d, 0x2b, 0x29, 0x2a, 0x5c, 0x2e, 0x5b, 0x61, 0x2d,
|
||||
0x7a, 0x2d, 0x5d, 0x2b, 0x24, 0x48, 0x00, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65,
|
||||
0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x07, 0x67, 0x72, 0x61, 0x6e, 0x74,
|
||||
0x65, 0x64, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x64,
|
||||
0x22, 0x89, 0x0b, 0x0a, 0x10, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x65, 0x78, 0x74, 0x1a, 0xa8, 0x01, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x49,
|
||||
0x0a, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x2b, 0xba, 0x48, 0x28, 0xc8, 0x01, 0x01, 0x72, 0x23, 0x32, 0x21, 0x5e, 0x5b, 0x61,
|
||||
0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2d, 0x25, 0x2e, 0x5d, 0x2b, 0x2f, 0x5b, 0x61,
|
||||
0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x2d, 0x25, 0x2e, 0x5d, 0x2b, 0x24, 0x52, 0x09,
|
||||
0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x64,
|
||||
0x69, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75,
|
||||
0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d,
|
||||
0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
|
||||
0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73,
|
||||
0x1a, 0xae, 0x04, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x13, 0x0a, 0x02,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x88, 0x01,
|
||||
0x01, 0x12, 0x4a, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x25, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x48, 0x74,
|
||||
0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01,
|
||||
0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x51, 0x0a,
|
||||
0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f,
|
||||
0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||
0x75, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42,
|
||||
0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
|
||||
0x12, 0x21, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d,
|
||||
0xba, 0x48, 0x0a, 0xc8, 0x01, 0x01, 0x72, 0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x52, 0x04, 0x70,
|
||||
0x61, 0x74, 0x68, 0x12, 0x1e, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x68,
|
||||
0x6f, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52,
|
||||
0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79,
|
||||
0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x88,
|
||||
0x01, 0x01, 0x12, 0x3b, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x0b, 0xba, 0x48,
|
||||
0x08, 0xc8, 0x01, 0x01, 0xb2, 0x01, 0x02, 0x38, 0x01, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12,
|
||||
0x26, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3b, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18,
|
||||
0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
|
||||
0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x04,
|
||||
0x61, 0x75, 0x74, 0x68, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45,
|
||||
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
|
||||
0x42, 0x05, 0x0a, 0x03, 0x5f, 0x69, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x71, 0x75, 0x65, 0x72,
|
||||
0x79, 0x1a, 0x87, 0x03, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57,
|
||||
0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x69,
|
||||
0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74,
|
||||
0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x07, 0xba, 0x48, 0x04, 0x22, 0x02, 0x28, 0x00,
|
||||
0x48, 0x00, 0x52, 0x10, 0x6e, 0x75, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x49,
|
||||
0x74, 0x65, 0x6d, 0x73, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x42, 0x07, 0xba, 0x48, 0x04, 0x22, 0x02, 0x28, 0x00, 0x48, 0x01, 0x52, 0x04, 0x73,
|
||||
0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x12, 0x4a, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
|
||||
0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74,
|
||||
0x65, 0x78, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61,
|
||||
0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65,
|
||||
0x72, 0x73, 0x12, 0x3b, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x0b, 0xba, 0x48,
|
||||
0x08, 0xc8, 0x01, 0x01, 0xb2, 0x01, 0x02, 0x38, 0x01, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x1a,
|
||||
0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
|
||||
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
|
||||
0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f,
|
||||
0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x69, 0x74, 0x65,
|
||||
0x6d, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x8e, 0x02, 0x0a, 0x0a,
|
||||
0x48, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x17, 0x48, 0x54,
|
||||
0x54, 0x50, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
|
||||
0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x48, 0x54, 0x54, 0x50, 0x5f,
|
||||
0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x10, 0x01, 0x12, 0x13,
|
||||
0x0a, 0x0f, 0x48, 0x54, 0x54, 0x50, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x47, 0x45,
|
||||
0x54, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x5f, 0x4d, 0x45, 0x54, 0x48,
|
||||
0x4f, 0x44, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x48, 0x54, 0x54,
|
||||
0x50, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x10, 0x04, 0x12,
|
||||
0x13, 0x0a, 0x0f, 0x48, 0x54, 0x54, 0x50, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x50,
|
||||
0x55, 0x54, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x5f, 0x4d, 0x45, 0x54,
|
||||
0x48, 0x4f, 0x44, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x06, 0x12, 0x17, 0x0a, 0x13,
|
||||
0x48, 0x54, 0x54, 0x50, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x4e,
|
||||
0x45, 0x43, 0x54, 0x10, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x48, 0x54, 0x54, 0x50, 0x5f, 0x4d, 0x45,
|
||||
0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x10, 0x08, 0x12, 0x15,
|
||||
0x0a, 0x11, 0x48, 0x54, 0x54, 0x50, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x54, 0x52,
|
||||
0x41, 0x43, 0x45, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x48, 0x54, 0x54, 0x50, 0x5f, 0x4d, 0x45,
|
||||
0x54, 0x48, 0x4f, 0x44, 0x5f, 0x50, 0x41, 0x54, 0x43, 0x48, 0x10, 0x0a, 0x22, 0xe1, 0x01, 0x0a,
|
||||
0x0f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x12, 0x27, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x70, 0x01, 0x52,
|
||||
0x08, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x70, 0x12, 0x4a, 0x0a, 0x1a, 0x63, 0x61, 0x6c,
|
||||
0x6c, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65,
|
||||
0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba,
|
||||
0x48, 0x0a, 0xc8, 0x01, 0x01, 0x72, 0x05, 0x10, 0x01, 0x18, 0xff, 0x01, 0x52, 0x17, 0x63, 0x61,
|
||||
0x6c, 0x6c, 0x65, 0x72, 0x53, 0x75, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72,
|
||||
0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x59, 0x0a, 0x12, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x22, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74,
|
||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x11, 0x72,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
|
||||
0x22, 0xb4, 0x02, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74,
|
||||
0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x48, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f,
|
||||
0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74,
|
||||
0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x1a,
|
||||
0x02, 0x28, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12,
|
||||
0x28, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x0d, 0x65, 0x72, 0x72,
|
||||
0x6f, 0x72, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x5c, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
|
||||
0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01,
|
||||
0x01, 0x52, 0x12, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69,
|
||||
0x62, 0x75, 0x74, 0x65, 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xba, 0x04, 0x0a, 0x1c, 0x53, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x63, 0x0a, 0x10, 0x73, 0x79, 0x73, 0x74,
|
||||
0x65, 0x6d, 0x5f, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65, 0x6c, 0x65,
|
||||
0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65,
|
||||
0x6d, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x79,
|
||||
0x73, 0x74, 0x65, 0x6d, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x5a, 0x0a,
|
||||
0x0d, 0x69, 0x64, 0x70, 0x5f, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x44, 0x65,
|
||||
0x6c, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x49, 0x64, 0x70,
|
||||
0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x0c, 0x69, 0x64, 0x70,
|
||||
0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x1a, 0x6f, 0x0a, 0x0f, 0x53, 0x79, 0x73,
|
||||
0x74, 0x65, 0x6d, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x47, 0x0a, 0x10,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48,
|
||||
0x00, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0xd3, 0x01, 0x0a, 0x0c, 0x49,
|
||||
0x64, 0x70, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x0c, 0x70,
|
||||
0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x70,
|
||||
0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x0f, 0x70, 0x72,
|
||||
0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x0a, 0xc8, 0x01, 0x01, 0x72, 0x05, 0x10, 0x01, 0x18,
|
||||
0xff, 0x01, 0x52, 0x0e, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x45, 0x6d, 0x61,
|
||||
0x69, 0x6c, 0x12, 0x47, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53,
|
||||
0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x42, 0x12, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x05, 0xba,
|
||||
0x48, 0x02, 0x08, 0x01, 0x2a, 0x96, 0x02, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x53, 0x65, 0x76, 0x65,
|
||||
0x72, 0x69, 0x74, 0x79, 0x12, 0x1c, 0x0a, 0x18, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x45, 0x56, 0x45,
|
||||
0x52, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
|
||||
0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49,
|
||||
0x54, 0x59, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x64, 0x12, 0x17, 0x0a, 0x12,
|
||||
0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x44, 0x45, 0x42,
|
||||
0x55, 0x47, 0x10, 0xc8, 0x01, 0x12, 0x16, 0x0a, 0x11, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x45, 0x56,
|
||||
0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0xac, 0x02, 0x12, 0x18, 0x0a,
|
||||
0x13, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x4e, 0x4f,
|
||||
0x54, 0x49, 0x43, 0x45, 0x10, 0x90, 0x03, 0x12, 0x19, 0x0a, 0x14, 0x4c, 0x4f, 0x47, 0x5f, 0x53,
|
||||
0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10,
|
||||
0xf4, 0x03, 0x12, 0x17, 0x0a, 0x12, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49,
|
||||
0x54, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0xd8, 0x04, 0x12, 0x1a, 0x0a, 0x15, 0x4c,
|
||||
0x4f, 0x47, 0x5f, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x43, 0x52, 0x49, 0x54,
|
||||
0x49, 0x43, 0x41, 0x4c, 0x10, 0xbc, 0x05, 0x12, 0x17, 0x0a, 0x12, 0x4c, 0x4f, 0x47, 0x5f, 0x53,
|
||||
0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x41, 0x4c, 0x45, 0x52, 0x54, 0x10, 0xa0, 0x06,
|
||||
0x12, 0x1b, 0x0a, 0x16, 0x4c, 0x4f, 0x47, 0x5f, 0x53, 0x45, 0x56, 0x45, 0x52, 0x49, 0x54, 0x59,
|
||||
0x5f, 0x45, 0x4d, 0x45, 0x52, 0x47, 0x45, 0x4e, 0x43, 0x59, 0x10, 0x84, 0x07, 0x42, 0x31, 0x0a,
|
||||
0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x63, 0x68, 0x77, 0x61, 0x72, 0x7a, 0x2e, 0x73, 0x74, 0x61,
|
||||
0x63, 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x50, 0x01, 0x5a,
|
||||
0x0f, 0x2e, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74, 0x3b, 0x61, 0x75, 0x64, 0x69, 0x74, 0x56, 0x31,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
const file_audit_v1_audit_event_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x1aaudit/v1/audit_event.proto\x12\baudit.v1\x1a\x1bbuf/validate/validate.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\xe2\x04\n" +
|
||||
"\rAuditLogEntry\x12x\n" +
|
||||
"\blog_name\x18\x01 \x01(\tB]\xbaHZ\xc8\x01\x01rU2S^[a-z-]+/[a-z0-9-]+/logs/(?:admin-activity|system-event|policy-denied|data-access)$R\alogName\x12?\n" +
|
||||
"\rproto_payload\x18\x02 \x01(\v2\x12.audit.v1.AuditLogB\x06\xbaH\x03\xc8\x01\x01R\fprotoPayload\x12L\n" +
|
||||
"\tinsert_id\x18\x03 \x01(\tB/\xbaH,\xc8\x01\x01r'2%^[0-9]+/[a-z0-9-]+/[a-z0-9-]+/[0-9]+$R\binsertId\x12;\n" +
|
||||
"\x06labels\x18\x04 \x03(\v2#.audit.v1.AuditLogEntry.LabelsEntryR\x06labels\x126\n" +
|
||||
"\x0ecorrelation_id\x18\x05 \x01(\tB\n" +
|
||||
"\xbaH\ar\x05\x10\x01\x18\xff\x01H\x00R\rcorrelationId\x88\x01\x01\x12E\n" +
|
||||
"\ttimestamp\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampB\v\xbaH\b\xc8\x01\x01\xb2\x01\x028\x01R\ttimestamp\x12>\n" +
|
||||
"\bseverity\x18\a \x01(\x0e2\x15.audit.v1.LogSeverityB\v\xbaH\b\xc8\x01\x01\x82\x01\x02\x10\x01R\bseverity\x1a9\n" +
|
||||
"\vLabelsEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x11\n" +
|
||||
"\x0f_correlation_id\"\xb3\x06\n" +
|
||||
"\bAuditLog\x125\n" +
|
||||
"\fservice_name\x18\x01 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" +
|
||||
"\x10\x012\x06.*\\S.*R\vserviceName\x12w\n" +
|
||||
"\x0eoperation_name\x18\x02 \x01(\tBP\xbaHM\xc8\x01\x01rH\x10\x01\x18\xff\x012A^stackit\\.[a-z0-9-]+\\.(?:v[0-9]+\\.)?(?:[a-z0-9-.]+\\.)?[a-z0-9-]+$R\roperationName\x12c\n" +
|
||||
"\rresource_name\x18\x03 \x01(\tB>\xbaH;\xc8\x01\x01r6\x10\x01\x18\xff\x012/^[a-z]+/[a-z0-9-]+(?:/[a-z0-9-]+/[a-z0-9-_]+)*$R\fresourceName\x12U\n" +
|
||||
"\x13authentication_info\x18\x04 \x01(\v2\x1c.audit.v1.AuthenticationInfoB\x06\xbaH\x03\xc8\x01\x01R\x12authenticationInfo\x12J\n" +
|
||||
"\x12authorization_info\x18\x05 \x03(\v2\x1b.audit.v1.AuthorizationInfoR\x11authorizationInfo\x12L\n" +
|
||||
"\x10request_metadata\x18\x06 \x01(\v2\x19.audit.v1.RequestMetadataB\x06\xbaH\x03\xc8\x01\x01R\x0frequestMetadata\x126\n" +
|
||||
"\arequest\x18\a \x01(\v2\x17.google.protobuf.StructH\x00R\arequest\x88\x01\x01\x12O\n" +
|
||||
"\x11response_metadata\x18\b \x01(\v2\x1a.audit.v1.ResponseMetadataB\x06\xbaH\x03\xc8\x01\x01R\x10responseMetadata\x128\n" +
|
||||
"\bresponse\x18\t \x01(\v2\x17.google.protobuf.StructH\x01R\bresponse\x88\x01\x01\x128\n" +
|
||||
"\bmetadata\x18\n" +
|
||||
" \x01(\v2\x17.google.protobuf.StructH\x02R\bmetadata\x88\x01\x01B\n" +
|
||||
"\n" +
|
||||
"\b_requestB\v\n" +
|
||||
"\t_responseB\v\n" +
|
||||
"\t_metadata\"\x93\x03\n" +
|
||||
"\x12AuthenticationInfo\x125\n" +
|
||||
"\fprincipal_id\x18\x01 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" +
|
||||
"\x10\x012\x06.*\\S.*R\vprincipalId\x12:\n" +
|
||||
"\x0fprincipal_email\x18\x02 \x01(\tB\f\xbaH\tr\a\x10\x05\x18\xff\x01`\x01H\x00R\x0eprincipalEmail\x88\x01\x01\x12n\n" +
|
||||
"\x14service_account_name\x18\x03 \x01(\tB7\xbaH4r220^[a-z-]+/[a-z0-9-]+/service-accounts/[a-z0-9-]+$H\x01R\x12serviceAccountName\x88\x01\x01\x12m\n" +
|
||||
"\x1fservice_account_delegation_info\x18\x04 \x03(\v2&.audit.v1.ServiceAccountDelegationInfoR\x1cserviceAccountDelegationInfoB\x12\n" +
|
||||
"\x10_principal_emailB\x17\n" +
|
||||
"\x15_service_account_name\"\xf2\x01\n" +
|
||||
"\x11AuthorizationInfo\x12U\n" +
|
||||
"\bresource\x18\x01 \x01(\tB9\xbaH6\xc8\x01\x01r12/^[a-z]+/[a-z0-9-]+(?:/[a-z0-9-]+/[a-z0-9-_]+)*$R\bresource\x12L\n" +
|
||||
"\n" +
|
||||
"permission\x18\x02 \x01(\tB'\xbaH$r\"2 ^[a-z-]+(?:\\.[a-z-]+)*\\.[a-z-]+$H\x00R\n" +
|
||||
"permission\x88\x01\x01\x12\x1d\n" +
|
||||
"\agranted\x18\x03 \x01(\bH\x01R\agranted\x88\x01\x01B\r\n" +
|
||||
"\v_permissionB\n" +
|
||||
"\n" +
|
||||
"\b_granted\"\xaa\v\n" +
|
||||
"\x10AttributeContext\x1a\xa9\x01\n" +
|
||||
"\x04Auth\x12J\n" +
|
||||
"\tprincipal\x18\x01 \x01(\tB,\xbaH)\xc8\x01\x01r$2\"^[a-zA-Z0-9-%._]+/[a-zA-Z0-9-%.]+$R\tprincipal\x12\x1c\n" +
|
||||
"\taudiences\x18\x02 \x03(\tR\taudiences\x127\n" +
|
||||
"\x06claims\x18\x03 \x01(\v2\x17.google.protobuf.StructB\x06\xbaH\x03\xc8\x01\x01R\x06claims\x1a\xce\x04\n" +
|
||||
"\aRequest\x12\x13\n" +
|
||||
"\x02id\x18\x01 \x01(\tH\x00R\x02id\x88\x01\x01\x12J\n" +
|
||||
"\x06method\x18\x02 \x01(\x0e2%.audit.v1.AttributeContext.HttpMethodB\v\xbaH\b\xc8\x01\x01\x82\x01\x02\x10\x01R\x06method\x12Q\n" +
|
||||
"\aheaders\x18\x03 \x03(\v2/.audit.v1.AttributeContext.Request.HeadersEntryB\x06\xbaH\x03\xc8\x01\x01R\aheaders\x12)\n" +
|
||||
"\x04path\x18\x04 \x01(\tB\x15\xbaH\x12\xc8\x01\x01r\r\x10\x01\x18\xff\x012\x06.*\\S.*R\x04path\x12&\n" +
|
||||
"\x04host\x18\x05 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" +
|
||||
"\x10\x012\x06.*\\S.*R\x04host\x12*\n" +
|
||||
"\x06scheme\x18\x06 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" +
|
||||
"\x10\x012\x06.*\\S.*R\x06scheme\x12\x19\n" +
|
||||
"\x05query\x18\a \x01(\tH\x01R\x05query\x88\x01\x01\x12;\n" +
|
||||
"\x04time\x18\b \x01(\v2\x1a.google.protobuf.TimestampB\v\xbaH\b\xc8\x01\x01\xb2\x01\x028\x01R\x04time\x12.\n" +
|
||||
"\bprotocol\x18\t \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" +
|
||||
"\x10\x012\x06.*\\S.*R\bprotocol\x12;\n" +
|
||||
"\x04auth\x18\n" +
|
||||
" \x01(\v2\x1f.audit.v1.AttributeContext.AuthB\x06\xbaH\x03\xc8\x01\x01R\x04auth\x1a:\n" +
|
||||
"\fHeadersEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x05\n" +
|
||||
"\x03_idB\b\n" +
|
||||
"\x06_query\x1a\x87\x03\n" +
|
||||
"\bResponse\x12W\n" +
|
||||
"\x12num_response_items\x18\x01 \x01(\v2\x1b.google.protobuf.Int64ValueB\a\xbaH\x04\"\x02(\x00H\x00R\x10numResponseItems\x88\x01\x01\x12=\n" +
|
||||
"\x04size\x18\x02 \x01(\v2\x1b.google.protobuf.Int64ValueB\a\xbaH\x04\"\x02(\x00H\x01R\x04size\x88\x01\x01\x12J\n" +
|
||||
"\aheaders\x18\x03 \x03(\v20.audit.v1.AttributeContext.Response.HeadersEntryR\aheaders\x12;\n" +
|
||||
"\x04time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampB\v\xbaH\b\xc8\x01\x01\xb2\x01\x028\x01R\x04time\x1a:\n" +
|
||||
"\fHeadersEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x15\n" +
|
||||
"\x13_num_response_itemsB\a\n" +
|
||||
"\x05_size\"\x8e\x02\n" +
|
||||
"\n" +
|
||||
"HttpMethod\x12\x1b\n" +
|
||||
"\x17HTTP_METHOD_UNSPECIFIED\x10\x00\x12\x15\n" +
|
||||
"\x11HTTP_METHOD_OTHER\x10\x01\x12\x13\n" +
|
||||
"\x0fHTTP_METHOD_GET\x10\x02\x12\x14\n" +
|
||||
"\x10HTTP_METHOD_HEAD\x10\x03\x12\x14\n" +
|
||||
"\x10HTTP_METHOD_POST\x10\x04\x12\x13\n" +
|
||||
"\x0fHTTP_METHOD_PUT\x10\x05\x12\x16\n" +
|
||||
"\x12HTTP_METHOD_DELETE\x10\x06\x12\x17\n" +
|
||||
"\x13HTTP_METHOD_CONNECT\x10\a\x12\x17\n" +
|
||||
"\x13HTTP_METHOD_OPTIONS\x10\b\x12\x15\n" +
|
||||
"\x11HTTP_METHOD_TRACE\x10\t\x12\x15\n" +
|
||||
"\x11HTTP_METHOD_PATCH\x10\n" +
|
||||
"\"\xe9\x01\n" +
|
||||
"\x0fRequestMetadata\x12'\n" +
|
||||
"\tcaller_ip\x18\x01 \x01(\tB\n" +
|
||||
"\xbaH\a\xc8\x01\x01r\x02p\x01R\bcallerIp\x12R\n" +
|
||||
"\x1acaller_supplied_user_agent\x18\x02 \x01(\tB\x15\xbaH\x12\xc8\x01\x01r\r\x10\x01\x18\xff\x012\x06.*\\S.*R\x17callerSuppliedUserAgent\x12Y\n" +
|
||||
"\x12request_attributes\x18\x03 \x01(\v2\".audit.v1.AttributeContext.RequestB\x06\xbaH\x03\xc8\x01\x01R\x11requestAttributes\"\xb4\x02\n" +
|
||||
"\x10ResponseMetadata\x12H\n" +
|
||||
"\vstatus_code\x18\x01 \x01(\v2\x1b.google.protobuf.Int32ValueB\n" +
|
||||
"\xbaH\a\xc8\x01\x01\x1a\x02(\x00R\n" +
|
||||
"statusCode\x12(\n" +
|
||||
"\rerror_message\x18\x02 \x01(\tH\x00R\ferrorMessage\x88\x01\x01\x12<\n" +
|
||||
"\rerror_details\x18\x03 \x03(\v2\x17.google.protobuf.StructR\ferrorDetails\x12\\\n" +
|
||||
"\x13response_attributes\x18\x04 \x01(\v2#.audit.v1.AttributeContext.ResponseB\x06\xbaH\x03\xc8\x01\x01R\x12responseAttributesB\x10\n" +
|
||||
"\x0e_error_message\"\xca\x04\n" +
|
||||
"\x1cServiceAccountDelegationInfo\x12c\n" +
|
||||
"\x10system_principal\x18\x01 \x01(\v26.audit.v1.ServiceAccountDelegationInfo.SystemPrincipalH\x00R\x0fsystemPrincipal\x12Z\n" +
|
||||
"\ridp_principal\x18\x02 \x01(\v23.audit.v1.ServiceAccountDelegationInfo.IdpPrincipalH\x00R\fidpPrincipal\x1ao\n" +
|
||||
"\x0fSystemPrincipal\x12G\n" +
|
||||
"\x10service_metadata\x18\x01 \x01(\v2\x17.google.protobuf.StructH\x00R\x0fserviceMetadata\x88\x01\x01B\x13\n" +
|
||||
"\x11_service_metadata\x1a\xe3\x01\n" +
|
||||
"\fIdpPrincipal\x125\n" +
|
||||
"\fprincipal_id\x18\x01 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" +
|
||||
"\x10\x012\x06.*\\S.*R\vprincipalId\x12>\n" +
|
||||
"\x0fprincipal_email\x18\x02 \x01(\tB\x15\xbaH\x12\xc8\x01\x01r\r\x10\x01\x18\xff\x012\x06.*\\S.*R\x0eprincipalEmail\x12G\n" +
|
||||
"\x10service_metadata\x18\x03 \x01(\v2\x17.google.protobuf.StructH\x00R\x0fserviceMetadata\x88\x01\x01B\x13\n" +
|
||||
"\x11_service_metadataB\x12\n" +
|
||||
"\tauthority\x12\x05\xbaH\x02\b\x01*\x96\x02\n" +
|
||||
"\vLogSeverity\x12\x1c\n" +
|
||||
"\x18LOG_SEVERITY_UNSPECIFIED\x10\x00\x12\x18\n" +
|
||||
"\x14LOG_SEVERITY_DEFAULT\x10d\x12\x17\n" +
|
||||
"\x12LOG_SEVERITY_DEBUG\x10\xc8\x01\x12\x16\n" +
|
||||
"\x11LOG_SEVERITY_INFO\x10\xac\x02\x12\x18\n" +
|
||||
"\x13LOG_SEVERITY_NOTICE\x10\x90\x03\x12\x19\n" +
|
||||
"\x14LOG_SEVERITY_WARNING\x10\xf4\x03\x12\x17\n" +
|
||||
"\x12LOG_SEVERITY_ERROR\x10\xd8\x04\x12\x1a\n" +
|
||||
"\x15LOG_SEVERITY_CRITICAL\x10\xbc\x05\x12\x17\n" +
|
||||
"\x12LOG_SEVERITY_ALERT\x10\xa0\x06\x12\x1b\n" +
|
||||
"\x16LOG_SEVERITY_EMERGENCY\x10\x84\aB1\n" +
|
||||
"\x1ccom.schwarz.stackit.audit.v1P\x01Z\x0f./audit;auditV1b\x06proto3"
|
||||
|
||||
var (
|
||||
file_audit_v1_audit_event_proto_rawDescOnce sync.Once
|
||||
file_audit_v1_audit_event_proto_rawDescData = file_audit_v1_audit_event_proto_rawDesc
|
||||
file_audit_v1_audit_event_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_audit_v1_audit_event_proto_rawDescGZIP() []byte {
|
||||
file_audit_v1_audit_event_proto_rawDescOnce.Do(func() {
|
||||
file_audit_v1_audit_event_proto_rawDescData = protoimpl.X.CompressGZIP(file_audit_v1_audit_event_proto_rawDescData)
|
||||
file_audit_v1_audit_event_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_audit_v1_audit_event_proto_rawDesc), len(file_audit_v1_audit_event_proto_rawDesc)))
|
||||
})
|
||||
return file_audit_v1_audit_event_proto_rawDescData
|
||||
}
|
||||
|
|
@ -1882,7 +1700,7 @@ func file_audit_v1_audit_event_proto_init() {
|
|||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_audit_v1_audit_event_proto_rawDesc,
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_audit_v1_audit_event_proto_rawDesc), len(file_audit_v1_audit_event_proto_rawDesc)),
|
||||
NumEnums: 2,
|
||||
NumMessages: 16,
|
||||
NumExtensions: 0,
|
||||
|
|
@ -1894,7 +1712,6 @@ func file_audit_v1_audit_event_proto_init() {
|
|||
MessageInfos: file_audit_v1_audit_event_proto_msgTypes,
|
||||
}.Build()
|
||||
File_audit_v1_audit_event_proto = out.File
|
||||
file_audit_v1_audit_event_proto_rawDesc = nil
|
||||
file_audit_v1_audit_event_proto_goTypes = nil
|
||||
file_audit_v1_audit_event_proto_depIdxs = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ type AuditLogEntryMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AuditLogEntryMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -466,7 +466,7 @@ type AuditLogMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AuditLogMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -554,8 +554,6 @@ func (m *AuthenticationInfo) validate(all bool) error {
|
|||
|
||||
// no validation rules for PrincipalId
|
||||
|
||||
// no validation rules for PrincipalEmail
|
||||
|
||||
for idx, item := range m.GetServiceAccountDelegationInfo() {
|
||||
_, _ = idx, item
|
||||
|
||||
|
|
@ -590,6 +588,10 @@ func (m *AuthenticationInfo) validate(all bool) error {
|
|||
|
||||
}
|
||||
|
||||
if m.PrincipalEmail != nil {
|
||||
// no validation rules for PrincipalEmail
|
||||
}
|
||||
|
||||
if m.ServiceAccountName != nil {
|
||||
// no validation rules for ServiceAccountName
|
||||
}
|
||||
|
|
@ -608,7 +610,7 @@ type AuthenticationInfoMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AuthenticationInfoMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -720,7 +722,7 @@ type AuthorizationInfoMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AuthorizationInfoMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -822,7 +824,7 @@ type AttributeContextMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AttributeContextMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -955,7 +957,7 @@ type RequestMetadataMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m RequestMetadataMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -1151,7 +1153,7 @@ type ResponseMetadataMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m ResponseMetadataMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -1338,7 +1340,7 @@ type ServiceAccountDelegationInfoMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m ServiceAccountDelegationInfoMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -1472,7 +1474,7 @@ type AttributeContext_AuthMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AttributeContext_AuthMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -1652,7 +1654,7 @@ type AttributeContext_RequestMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AttributeContext_RequestMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -1851,7 +1853,7 @@ type AttributeContext_ResponseMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AttributeContext_ResponseMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -1989,7 +1991,7 @@ type ServiceAccountDelegationInfo_SystemPrincipalMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m ServiceAccountDelegationInfo_SystemPrincipalMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -2133,7 +2135,7 @@ type ServiceAccountDelegationInfo_IdpPrincipalMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m ServiceAccountDelegationInfo_IdpPrincipalMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.0
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: audit/v1/routable_event.proto
|
||||
|
||||
|
|
@ -12,6 +12,7 @@ import (
|
|||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -410,83 +411,53 @@ func (*RoutableAuditEvent_EncryptedData) isRoutableAuditEvent_Data() {}
|
|||
|
||||
var File_audit_v1_routable_event_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_audit_v1_routable_event_proto_rawDesc = []byte{
|
||||
0x0a, 0x1d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
||||
0x08, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76,
|
||||
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5f, 0x0a, 0x10, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
|
||||
0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x0a, 0x69, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b,
|
||||
0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0a, 0x69, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10,
|
||||
0x01, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xc5, 0x01, 0x0a, 0x0d, 0x45, 0x6e, 0x63, 0x72,
|
||||
0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x04, 0x64, 0x61, 0x74,
|
||||
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x7a,
|
||||
0x02, 0x10, 0x01, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x0d, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||
0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0c, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x12, 0x65, 0x6e,
|
||||
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02,
|
||||
0x10, 0x01, 0x52, 0x11, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x28, 0x0a, 0x0b, 0x6b, 0x65, 0x79, 0x5f, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x42, 0x07, 0xba, 0x48, 0x04, 0x1a,
|
||||
0x02, 0x28, 0x01, 0x52, 0x0a, 0x6b, 0x65, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22,
|
||||
0x62, 0x0a, 0x0f, 0x55, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61,
|
||||
0x74, 0x61, 0x12, 0x1e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x7a, 0x02, 0x10, 0x01, 0x52, 0x04, 0x64, 0x61,
|
||||
0x74, 0x61, 0x12, 0x2f, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5f, 0x74,
|
||||
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01,
|
||||
0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x54,
|
||||
0x79, 0x70, 0x65, 0x22, 0xb5, 0x03, 0x0a, 0x12, 0x52, 0x6f, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x72, 0x0a, 0x0e, 0x6f, 0x70,
|
||||
0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x42, 0x4b, 0xba, 0x48, 0x48, 0xc8, 0x01, 0x01, 0x72, 0x43, 0x32, 0x41, 0x5e, 0x73,
|
||||
0x74, 0x61, 0x63, 0x6b, 0x69, 0x74, 0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d,
|
||||
0x5d, 0x2b, 0x5c, 0x2e, 0x28, 0x3f, 0x3a, 0x76, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x5c, 0x2e,
|
||||
0x29, 0x3f, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x2e, 0x5d, 0x2b,
|
||||
0x5c, 0x2e, 0x29, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x2d, 0x5d, 0x2b, 0x24, 0x52,
|
||||
0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41,
|
||||
0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x69,
|
||||
0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01,
|
||||
0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74,
|
||||
0x79, 0x12, 0x4f, 0x0a, 0x11, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61,
|
||||
0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01,
|
||||
0x52, 0x10, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
|
||||
0x65, 0x72, 0x12, 0x46, 0x0a, 0x10, 0x75, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
|
||||
0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61,
|
||||
0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
|
||||
0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x6e, 0x63,
|
||||
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x0e, 0x65, 0x6e,
|
||||
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e,
|
||||
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0d, 0x65,
|
||||
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, 0x0a, 0x04,
|
||||
0x64, 0x61, 0x74, 0x61, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x2a, 0x57, 0x0a, 0x0a, 0x56,
|
||||
0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x49, 0x53,
|
||||
0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
|
||||
0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c,
|
||||
0x49, 0x54, 0x59, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12,
|
||||
0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x52, 0x49, 0x56, 0x41,
|
||||
0x54, 0x45, 0x10, 0x02, 0x42, 0x31, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x63, 0x68, 0x77,
|
||||
0x61, 0x72, 0x7a, 0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x75, 0x64, 0x69,
|
||||
0x74, 0x2e, 0x76, 0x31, 0x50, 0x01, 0x5a, 0x0f, 0x2e, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74, 0x3b,
|
||||
0x61, 0x75, 0x64, 0x69, 0x74, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
const file_audit_v1_routable_event_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x1daudit/v1/routable_event.proto\x12\baudit.v1\x1a\x1bbuf/validate/validate.proto\"_\n" +
|
||||
"\x10ObjectIdentifier\x12+\n" +
|
||||
"\n" +
|
||||
"identifier\x18\x01 \x01(\tB\v\xbaH\b\xc8\x01\x01r\x03\xb0\x01\x01R\n" +
|
||||
"identifier\x12\x1e\n" +
|
||||
"\x04type\x18\x02 \x01(\tB\n" +
|
||||
"\xbaH\a\xc8\x01\x01r\x02\x10\x01R\x04type\"\xc5\x01\n" +
|
||||
"\rEncryptedData\x12\x1e\n" +
|
||||
"\x04data\x18\x01 \x01(\fB\n" +
|
||||
"\xbaH\a\xc8\x01\x01z\x02\x10\x01R\x04data\x12/\n" +
|
||||
"\rprotobuf_type\x18\x02 \x01(\tB\n" +
|
||||
"\xbaH\a\xc8\x01\x01r\x02\x10\x01R\fprotobufType\x129\n" +
|
||||
"\x12encrypted_password\x18\x03 \x01(\tB\n" +
|
||||
"\xbaH\a\xc8\x01\x01r\x02\x10\x01R\x11encryptedPassword\x12(\n" +
|
||||
"\vkey_version\x18\x04 \x01(\x05B\a\xbaH\x04\x1a\x02(\x01R\n" +
|
||||
"keyVersion\"b\n" +
|
||||
"\x0fUnencryptedData\x12\x1e\n" +
|
||||
"\x04data\x18\x01 \x01(\fB\n" +
|
||||
"\xbaH\a\xc8\x01\x01z\x02\x10\x01R\x04data\x12/\n" +
|
||||
"\rprotobuf_type\x18\x02 \x01(\tB\n" +
|
||||
"\xbaH\a\xc8\x01\x01r\x02\x10\x01R\fprotobufType\"\xb5\x03\n" +
|
||||
"\x12RoutableAuditEvent\x12r\n" +
|
||||
"\x0eoperation_name\x18\x01 \x01(\tBK\xbaHH\xc8\x01\x01rC2A^stackit\\.[a-z0-9-]+\\.(?:v[0-9]+\\.)?(?:[a-z0-9-.]+\\.)?[a-z0-9-]+$R\roperationName\x12A\n" +
|
||||
"\n" +
|
||||
"visibility\x18\x02 \x01(\x0e2\x14.audit.v1.VisibilityB\v\xbaH\b\xc8\x01\x01\x82\x01\x02\x10\x01R\n" +
|
||||
"visibility\x12O\n" +
|
||||
"\x11object_identifier\x18\x03 \x01(\v2\x1a.audit.v1.ObjectIdentifierB\x06\xbaH\x03\xc8\x01\x01R\x10objectIdentifier\x12F\n" +
|
||||
"\x10unencrypted_data\x18\x04 \x01(\v2\x19.audit.v1.UnencryptedDataH\x00R\x0funencryptedData\x12@\n" +
|
||||
"\x0eencrypted_data\x18\x05 \x01(\v2\x17.audit.v1.EncryptedDataH\x00R\rencryptedDataB\r\n" +
|
||||
"\x04data\x12\x05\xbaH\x02\b\x01*W\n" +
|
||||
"\n" +
|
||||
"Visibility\x12\x1a\n" +
|
||||
"\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x15\n" +
|
||||
"\x11VISIBILITY_PUBLIC\x10\x01\x12\x16\n" +
|
||||
"\x12VISIBILITY_PRIVATE\x10\x02B1\n" +
|
||||
"\x1ccom.schwarz.stackit.audit.v1P\x01Z\x0f./audit;auditV1b\x06proto3"
|
||||
|
||||
var (
|
||||
file_audit_v1_routable_event_proto_rawDescOnce sync.Once
|
||||
file_audit_v1_routable_event_proto_rawDescData = file_audit_v1_routable_event_proto_rawDesc
|
||||
file_audit_v1_routable_event_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_audit_v1_routable_event_proto_rawDescGZIP() []byte {
|
||||
file_audit_v1_routable_event_proto_rawDescOnce.Do(func() {
|
||||
file_audit_v1_routable_event_proto_rawDescData = protoimpl.X.CompressGZIP(file_audit_v1_routable_event_proto_rawDescData)
|
||||
file_audit_v1_routable_event_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_audit_v1_routable_event_proto_rawDesc), len(file_audit_v1_routable_event_proto_rawDesc)))
|
||||
})
|
||||
return file_audit_v1_routable_event_proto_rawDescData
|
||||
}
|
||||
|
|
@ -525,7 +496,7 @@ func file_audit_v1_routable_event_proto_init() {
|
|||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_audit_v1_routable_event_proto_rawDesc,
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_audit_v1_routable_event_proto_rawDesc), len(file_audit_v1_routable_event_proto_rawDesc)),
|
||||
NumEnums: 1,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
|
|
@ -537,7 +508,6 @@ func file_audit_v1_routable_event_proto_init() {
|
|||
MessageInfos: file_audit_v1_routable_event_proto_msgTypes,
|
||||
}.Build()
|
||||
File_audit_v1_routable_event_proto = out.File
|
||||
file_audit_v1_routable_event_proto_rawDesc = nil
|
||||
file_audit_v1_routable_event_proto_goTypes = nil
|
||||
file_audit_v1_routable_event_proto_depIdxs = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ type ObjectIdentifierMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m ObjectIdentifierMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ type EncryptedDataMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m EncryptedDataMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -287,7 +287,7 @@ type UnencryptedDataMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m UnencryptedDataMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
@ -507,7 +507,7 @@ type RoutableAuditEventMultiError []error
|
|||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m RoutableAuditEventMultiError) Error() string {
|
||||
var msgs []string
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
|
|
|
|||
113
go.mod
113
go.mod
|
|
@ -1,83 +1,88 @@
|
|||
module dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git
|
||||
|
||||
go 1.22.7
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1
|
||||
github.com/Azure/go-amqp v1.3.0
|
||||
github.com/bufbuild/protovalidate-go v0.8.0
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20251209175733-2a1774d88802.1
|
||||
buf.build/go/protovalidate v1.1.0
|
||||
github.com/Azure/go-amqp v1.5.1
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.3
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/testcontainers/testcontainers-go v0.34.0
|
||||
go.opentelemetry.io/otel v1.33.0
|
||||
go.opentelemetry.io/otel/trace v1.33.0
|
||||
google.golang.org/protobuf v1.36.0
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.6
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/testcontainers/testcontainers-go v0.40.0
|
||||
go.opentelemetry.io/otel v1.39.0
|
||||
go.opentelemetry.io/otel/trace v1.39.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.18.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
cel.dev/expr v0.25.1 // 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.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/containerd/containerd v1.7.18 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker v27.1.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/cel-go v0.22.1 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/google/cel-go v0.26.1 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httprc v1.0.6 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.2.0 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/morikuni/aec v1.1.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/segmentio/asm v1.2.1 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.12 // 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
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.1 // indirect
|
||||
github.com/stretchr/objx v0.5.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.39.0 // indirect
|
||||
golang.org/x/crypto v0.46.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
|
|||
319
go.sum
319
go.sum
|
|
@ -1,25 +1,33 @@
|
|||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1 h1:ntAj16eF7AtUyzOOAFk5gvbAO52QmUKPKk7GmsIEORo=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1/go.mod h1:AxRT+qTj5PJCz2nyQzsR/qxAcveW5USRhJTt/edTO5w=
|
||||
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
|
||||
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-amqp v1.3.0 h1://1rikYhoIQNXJFXyoO/Rlb4+4EkHYfJceNtLlys2/4=
|
||||
github.com/Azure/go-amqp v1.3.0/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20251209175733-2a1774d88802.1 h1:j9yeqTWEFrtimt8Nng2MIeRrpoCvQzM9/g25XTvqUGg=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20251209175733-2a1774d88802.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM=
|
||||
buf.build/go/protovalidate v1.1.0 h1:pQqEQRpOo4SqS60qkvmhLTTQU9JwzEvdyiqAtXa5SeY=
|
||||
buf.build/go/protovalidate v1.1.0/go.mod h1:bGZcPiAQDC3ErCHK3t74jSoJDFOs2JH3d7LWuTEIdss=
|
||||
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
|
||||
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
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.5.1 h1:WyiPTz2C3zVvDL7RLAqwWdeoYhMtX62MZzQoP09fzsU=
|
||||
github.com/Azure/go-amqp v1.5.1/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/bufbuild/protovalidate-go v0.8.0 h1:Xs3kCLCJ4tQiogJ0iOXm+ClKw/KviW3nLAryCGW2I3Y=
|
||||
github.com/bufbuild/protovalidate-go v0.8.0/go.mod h1:JPWZInGm2y2NBg3vKDKdDIkvDjyLv31J3hLH5GIFc/Q=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao=
|
||||
github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4=
|
||||
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/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=
|
||||
github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=
|
||||
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/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
|
|
@ -32,215 +40,188 @@ github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
|
||||
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
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/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/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=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40=
|
||||
github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ=
|
||||
github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
|
||||
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
|
||||
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
|
||||
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.3 h1:Ud4lb2QuxRClYAmRleF50KrbKIoM1TddXgBrneT5/Jo=
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.3/go.mod h1:q6uFgbgZfEmQrfJfrCo90QcQOcXFMfbI/fO0NqRtvZo=
|
||||
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-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
|
||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
|
||||
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
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/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/rodaine/protogofakeit v0.1.1 h1:ZKouljuRM3A+TArppfBqnH8tGZHOwM/pjvtXe9DaXH8=
|
||||
github.com/rodaine/protogofakeit v0.1.1/go.mod h1:pXn/AstBYMaSfc1/RqH3N82pBuxtWgejz1AlYpY1mI0=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
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.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
|
||||
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
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=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
|
||||
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
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.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo=
|
||||
github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
|
||||
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=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU=
|
||||
github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
|
||||
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
|
||||
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
|
|
|
|||
|
|
@ -2,72 +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"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// 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-/]+`)
|
||||
|
||||
// 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
|
||||
|
|
@ -86,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)
|
||||
|
|
@ -117,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
|
||||
|
|
@ -139,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 {
|
||||
|
|
@ -168,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
|
||||
}
|
||||
|
|
@ -222,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
|
||||
}
|
||||
|
|
@ -243,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
|
||||
|
|
@ -260,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
|
||||
}
|
||||
477
internal/audit/api/api_common_test.go
Normal file
477
internal/audit/api/api_common_test.go
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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 {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MessagingApiMock) Send(
|
||||
ctx context.Context,
|
||||
topic string,
|
||||
data []byte,
|
||||
contentType string,
|
||||
applicationProperties map[string]any,
|
||||
) error {
|
||||
|
||||
args := m.Called(ctx, topic, data, contentType, applicationProperties)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MessagingApiMock) Close(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type TopicNameResolverMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
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) pkgAuditCommon.ProtobufValidator {
|
||||
validator, err := protovalidate.New()
|
||||
var protoValidator pkgAuditCommon.ProtobufValidator = validator
|
||||
assert.NoError(t, err)
|
||||
|
||||
return protoValidator
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_EventNil(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
_, err := ValidateAndSerializePartially(
|
||||
validator, nil, auditV1.Visibility_VISIBILITY_PUBLIC, nil)
|
||||
|
||||
assert.ErrorIs(t, err, pkgAuditCommon.ErrEventNil)
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_AuditEventValidationFailed(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event, objectIdentifier := NewOrganizationAuditEvent(nil)
|
||||
event.LogName = ""
|
||||
|
||||
_, err := ValidateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.EqualError(t, err, "validation error: log_name: value is required")
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_RoutableEventValidationFailed(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event, objectIdentifier := NewOrganizationAuditEvent(nil)
|
||||
_, err := ValidateAndSerializePartially(validator, event, 3, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.EqualError(t, err, "validation error: visibility: value must be one of the defined enum values")
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_CheckVisibility_Event(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event, objectIdentifier := NewOrganizationAuditEvent(nil)
|
||||
|
||||
t.Run("Visibility public - object identifier nil", func(t *testing.T) {
|
||||
_, err := ValidateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil)
|
||||
|
||||
assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierNil)
|
||||
})
|
||||
|
||||
t.Run("Visibility private - object identifier nil", func(t *testing.T) {
|
||||
_, err := ValidateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil)
|
||||
|
||||
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, pkgAuditCommon.RoutableSystemIdentifier)
|
||||
|
||||
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, 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, pkgAuditCommon.RoutableSystemIdentifier)
|
||||
|
||||
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, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, routableEvent)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_CheckVisibility_SystemEvent(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event := NewSystemAuditEvent(nil)
|
||||
|
||||
t.Run("Visibility public - object identifier nil", func(t *testing.T) {
|
||||
_, err := ValidateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil)
|
||||
|
||||
assert.ErrorIs(t, err, pkgAuditCommon.ErrObjectIdentifierNil)
|
||||
})
|
||||
|
||||
t.Run("Visibility private - object identifier nil", func(t *testing.T) {
|
||||
_, err := ValidateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil)
|
||||
|
||||
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, pkgAuditCommon.RoutableSystemIdentifier)
|
||||
|
||||
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, pkgAuditCommon.NewRoutableIdentifier(
|
||||
&auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(pkgAuditCommon.ObjectTypeOrganization)}))
|
||||
|
||||
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, 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, pkgAuditCommon.NewRoutableIdentifier(
|
||||
&auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(pkgAuditCommon.ObjectTypeOrganization)}))
|
||||
|
||||
assert.ErrorIs(t, err, pkgAuditCommon.ErrInvalidRoutableIdentifierForSystemEvent)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_UnsupportedIdentifierType(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event, objectIdentifier := NewFolderAuditEvent(nil)
|
||||
objectIdentifier.Type = "invalid"
|
||||
|
||||
_, err := ValidateAndSerializePartially(
|
||||
validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.ErrorIs(t, err, pkgAuditCommon.ErrUnsupportedRoutableType)
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_LogNameIdentifierMismatch(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
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, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
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, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.ErrorIs(t, err, pkgAuditCommon.ErrAttributeIdentifierInvalid)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_ResourceNameIdentifierMismatch(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
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, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
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, pkgAuditCommon.NewRoutableIdentifier(objectIdentifier))
|
||||
|
||||
assert.ErrorIs(t, err, pkgAuditCommon.ErrAttributeIdentifierInvalid)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ValidateAndSerializePartially_SystemEvent(t *testing.T) {
|
||||
validator := NewValidator(t)
|
||||
|
||||
event := NewSystemAuditEvent(nil)
|
||||
|
||||
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", 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, pkgAuditCommon.ErrTopicNameResolverNil)
|
||||
}
|
||||
|
||||
func Test_Send_TopicNameResolutionError(t *testing.T) {
|
||||
expectedError := errors.New("expected error")
|
||||
|
||||
topicNameResolverMock := TopicNameResolverMock{}
|
||||
topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", expectedError)
|
||||
var topicNameResolver pkgAuditCommon.TopicNameResolver = &topicNameResolverMock
|
||||
|
||||
var cloudEvent = pkgAuditCommon.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 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 pkgAuditCommon.TopicNameResolver = &pkgAuditCommon.StaticTopicNameTestResolver{TopicName: "test"}
|
||||
var messagingApi pkgMessagingApi.Api = &pkgMessagingApi.AmqpApi{}
|
||||
|
||||
err := Send(topicNameResolver, messagingApi, context.Background(), nil, nil)
|
||||
assert.ErrorIs(t, err, pkgAuditCommon.ErrCloudEventNil)
|
||||
}
|
||||
|
||||
func Test_Send_ObjectIdentifierNil(t *testing.T) {
|
||||
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, pkgAuditCommon.ErrObjectIdentifierNil)
|
||||
}
|
||||
|
||||
func Test_Send_UnsupportedObjectIdentifierType(t *testing.T) {
|
||||
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(), 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 pkgAuditCommon.TopicNameResolver = &topicNameResolverMock
|
||||
|
||||
messagingApiMock := MessagingApiMock{}
|
||||
messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
var messagingApi pkgMessagingApi.Api = &messagingApiMock
|
||||
|
||||
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 pkgAuditCommon.TopicNameResolver = &topicNameResolverMock
|
||||
|
||||
messagingApiMock := MessagingApiMock{}
|
||||
messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
var messagingApi pkgMessagingApi.Api = &messagingApiMock
|
||||
|
||||
traceState := "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"
|
||||
traceParent := "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
|
||||
expectedTime := time.Now()
|
||||
var cloudEvent = pkgAuditCommon.CloudEvent{
|
||||
SpecVersion: "1.0",
|
||||
Source: "resourcemanager",
|
||||
Id: "id",
|
||||
Time: expectedTime,
|
||||
DataContentType: pkgAuditCommon.ContentTypeCloudEventsProtobuf,
|
||||
DataType: "type",
|
||||
Subject: "subject",
|
||||
TraceParent: &traceParent,
|
||||
TraceState: &traceState,
|
||||
}
|
||||
assert.NoError(t, Send(topicNameResolver, messagingApi, context.Background(), pkgAuditCommon.RoutableSystemIdentifier, &cloudEvent))
|
||||
assert.True(t, messagingApiMock.AssertNumberOfCalls(t, "Send", 1))
|
||||
|
||||
arguments := messagingApiMock.Calls[0].Arguments
|
||||
topic := arguments.Get(1).(string)
|
||||
assert.Equal(t, "topic", topic)
|
||||
|
||||
contentType := arguments.Get(3).(string)
|
||||
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, 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"])
|
||||
assert.Equal(t, traceState, applicationProperties["cloudEvents:tracestate"])
|
||||
messagingApiMock.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func Test_SendWithoutOptionalHeadersSet(t *testing.T) {
|
||||
topicNameResolverMock := TopicNameResolverMock{}
|
||||
topicNameResolverMock.On("Resolve", mock.Anything).Return("topic", nil)
|
||||
var topicNameResolver pkgAuditCommon.TopicNameResolver = &topicNameResolverMock
|
||||
|
||||
messagingApiMock := MessagingApiMock{}
|
||||
messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
var messagingApi pkgMessagingApi.Api = &messagingApiMock
|
||||
|
||||
expectedTime := time.Now()
|
||||
var cloudEvent = pkgAuditCommon.CloudEvent{
|
||||
SpecVersion: "1.0",
|
||||
Source: "resourcemanager",
|
||||
Id: "id",
|
||||
Time: expectedTime,
|
||||
DataContentType: pkgAuditCommon.ContentTypeCloudEventsProtobuf,
|
||||
DataType: "type",
|
||||
Subject: "subject",
|
||||
}
|
||||
assert.NoError(t, Send(topicNameResolver, messagingApi, context.Background(), pkgAuditCommon.RoutableSystemIdentifier, &cloudEvent))
|
||||
assert.True(t, messagingApiMock.AssertNumberOfCalls(t, "Send", 1))
|
||||
|
||||
arguments := messagingApiMock.Calls[0].Arguments
|
||||
topic := arguments.Get(1).(string)
|
||||
assert.Equal(t, "topic", topic)
|
||||
|
||||
contentType := arguments.Get(3).(string)
|
||||
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, 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"])
|
||||
assert.Equal(t, nil, applicationProperties["cloudEvents:tracestate"])
|
||||
messagingApiMock.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func Test_ValidateTopicNames(t *testing.T) {
|
||||
t.Run("conway", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/conway/v1.0/service-name/events"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("eu01", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1.0/service-name/events"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("eu02", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu02/v1.0/service-name/events"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("sx-stoi01", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/sx-stoi01/v1.0/service-name/events"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("version without decimals", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/service-name/events"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("version as uppercase", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/V1.0/service-name/events"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("service name without dash", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1.0/service/events"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("multiple additional parts", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1.0/service-name/multiple/additional/parts"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("additional parts with dash", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1.0/service-name/multiple-additional/parts"
|
||||
assert.True(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("topic prefix missing", func(t *testing.T) {
|
||||
topicName := "stackit-platform/t/swz/audit-log/eu01/v1.0/service-name/events"
|
||||
assert.False(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("invalid region", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/invalid/v1.0/service-name/events"
|
||||
assert.False(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
|
||||
t.Run("additional parts missing", func(t *testing.T) {
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1.0/service-name"
|
||||
assert.False(t, TopicNamePattern.MatchString(topicName))
|
||||
})
|
||||
}
|
||||
|
|
@ -8,29 +8,32 @@ 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) {
|
||||
|
||||
// Event type
|
||||
var eventType string
|
||||
if strings.HasSuffix(event.LogName, string(EventTypeAdminActivity)) {
|
||||
switch {
|
||||
case strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeAdminActivity)):
|
||||
eventType = "ADMIN_ACTIVITY"
|
||||
} else if strings.HasSuffix(event.LogName, string(EventTypeSystemEvent)) {
|
||||
case strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeSystemEvent)):
|
||||
eventType = "SYSTEM_EVENT"
|
||||
} else if strings.HasSuffix(event.LogName, string(EventTypePolicyDenied)) {
|
||||
case strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypePolicyDenied)):
|
||||
eventType = "POLICY_DENIED"
|
||||
} else if strings.HasSuffix(event.LogName, string(EventTypeDataAccess)) {
|
||||
return nil, ErrUnsupportedEventTypeDataAccess
|
||||
} else {
|
||||
case strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeDataAccess)):
|
||||
return nil, pkgAuditCommon.ErrUnsupportedEventTypeDataAccess
|
||||
default:
|
||||
return nil, errors.New("unsupported event type")
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +49,7 @@ func convertAndSerializeIntoLegacyFormat(
|
|||
}
|
||||
|
||||
// Principals
|
||||
var serviceAccountDelegationInfo *LegacyAuditEventServiceAccountDelegationInfo = nil
|
||||
var serviceAccountDelegationInfo *LegacyAuditEventServiceAccountDelegationInfo
|
||||
if len(event.ProtoPayload.AuthenticationInfo.ServiceAccountDelegationInfo) > 0 {
|
||||
var principals []LegacyAuditEventPrincipal
|
||||
for _, principal := range event.ProtoPayload.AuthenticationInfo.ServiceAccountDelegationInfo {
|
||||
|
|
@ -73,7 +76,7 @@ func convertAndSerializeIntoLegacyFormat(
|
|||
Endpoint: "none",
|
||||
}
|
||||
} else {
|
||||
var parameters map[string]interface{} = nil
|
||||
var parameters map[string]interface{}
|
||||
if event.ProtoPayload.RequestMetadata.RequestAttributes.Path != "" &&
|
||||
event.ProtoPayload.RequestMetadata.RequestAttributes.Query != nil &&
|
||||
*event.ProtoPayload.RequestMetadata.RequestAttributes.Query != "" {
|
||||
|
|
@ -95,11 +98,11 @@ func convertAndSerializeIntoLegacyFormat(
|
|||
}
|
||||
}
|
||||
|
||||
var body map[string]interface{} = nil
|
||||
var body map[string]interface{}
|
||||
if event.ProtoPayload.Request != nil {
|
||||
body = event.ProtoPayload.Request.AsMap()
|
||||
}
|
||||
var headers map[string]interface{} = nil
|
||||
var headers map[string]interface{}
|
||||
if event.ProtoPayload.RequestMetadata.RequestAttributes.Headers != nil {
|
||||
headers = map[string]interface{}{}
|
||||
for key, value := range event.ProtoPayload.RequestMetadata.RequestAttributes.Headers {
|
||||
|
|
@ -116,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
|
||||
|
|
@ -152,6 +155,8 @@ func convertAndSerializeIntoLegacyFormat(
|
|||
visibility = "PUBLIC"
|
||||
case auditV1.Visibility_VISIBILITY_PRIVATE:
|
||||
visibility = "PRIVATE"
|
||||
case auditV1.Visibility_VISIBILITY_UNSPECIFIED:
|
||||
visibility = ""
|
||||
}
|
||||
|
||||
// Details
|
||||
|
|
@ -171,23 +176,16 @@ func convertAndSerializeIntoLegacyFormat(
|
|||
// Severity
|
||||
var severity string
|
||||
switch event.Severity {
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_DEFAULT:
|
||||
fallthrough
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_DEBUG:
|
||||
fallthrough
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_INFO:
|
||||
fallthrough
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_NOTICE:
|
||||
fallthrough
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_WARNING:
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_DEFAULT,
|
||||
auditV1.LogSeverity_LOG_SEVERITY_DEBUG,
|
||||
auditV1.LogSeverity_LOG_SEVERITY_INFO,
|
||||
auditV1.LogSeverity_LOG_SEVERITY_NOTICE,
|
||||
auditV1.LogSeverity_LOG_SEVERITY_WARNING:
|
||||
severity = "INFO"
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_ERROR:
|
||||
fallthrough
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_CRITICAL:
|
||||
fallthrough
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_ALERT:
|
||||
fallthrough
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_EMERGENCY:
|
||||
case auditV1.LogSeverity_LOG_SEVERITY_ERROR,
|
||||
auditV1.LogSeverity_LOG_SEVERITY_CRITICAL,
|
||||
auditV1.LogSeverity_LOG_SEVERITY_ALERT,
|
||||
auditV1.LogSeverity_LOG_SEVERITY_EMERGENCY:
|
||||
severity = "ERROR"
|
||||
default:
|
||||
return nil, ErrUnsupportedSeverity
|
||||
|
|
@ -204,7 +202,7 @@ func convertAndSerializeIntoLegacyFormat(
|
|||
UserAgent: userAgent,
|
||||
Initiator: LegacyAuditEventPrincipal{
|
||||
Id: event.ProtoPayload.AuthenticationInfo.PrincipalId,
|
||||
Email: &event.ProtoPayload.AuthenticationInfo.PrincipalEmail,
|
||||
Email: event.ProtoPayload.AuthenticationInfo.PrincipalEmail,
|
||||
},
|
||||
ServiceAccountDelegationInfo: serviceAccountDelegationInfo,
|
||||
Request: request,
|
||||
|
|
@ -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.Compile(".*/(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.
|
||||
|
|
@ -145,7 +83,7 @@ type AuditResponse struct {
|
|||
// elsewhere in the log record.
|
||||
//
|
||||
// Required: false
|
||||
ResponseBodyBytes *[]byte
|
||||
ResponseBodyBytes []byte
|
||||
|
||||
// The http or gRPC status code.
|
||||
//
|
||||
|
|
@ -294,7 +232,7 @@ func NewAuditLogEntry(
|
|||
auditResponse AuditResponse,
|
||||
|
||||
// Optional map that is added as "details" to the message
|
||||
eventMetadata *map[string]interface{},
|
||||
eventMetadata map[string]interface{},
|
||||
|
||||
// Required metadata
|
||||
auditMetadata AuditMetadata,
|
||||
|
|
@ -309,9 +247,9 @@ func NewAuditLogEntry(
|
|||
if err != nil {
|
||||
return nil, errors.Join(err, ErrInvalidResponse)
|
||||
}
|
||||
var responseLength *int64 = nil
|
||||
var responseLength *int64
|
||||
if responseBody != nil {
|
||||
length := int64(len(*auditResponse.ResponseBodyBytes))
|
||||
length := int64(len(auditResponse.ResponseBodyBytes))
|
||||
responseLength = &length
|
||||
}
|
||||
|
||||
|
|
@ -332,7 +270,7 @@ func NewAuditLogEntry(
|
|||
scheme := auditRequest.Request.Scheme
|
||||
|
||||
// Initialize authorization info if available
|
||||
var authorizationInfo []*auditV1.AuthorizationInfo = nil
|
||||
var authorizationInfo []*auditV1.AuthorizationInfo
|
||||
if auditMetadata.AuditPermission != nil && auditMetadata.AuditPermissionGranted != nil {
|
||||
authorizationInfo = []*auditV1.AuthorizationInfo{
|
||||
NewAuthorizationInfo(
|
||||
|
|
@ -342,15 +280,15 @@ func NewAuditLogEntry(
|
|||
}
|
||||
|
||||
// Initialize labels if available
|
||||
var labels map[string]string = nil
|
||||
var labels map[string]string
|
||||
if auditMetadata.AuditLabels != nil {
|
||||
labels = *auditMetadata.AuditLabels
|
||||
}
|
||||
|
||||
// Initialize metadata/details
|
||||
var metadata *structpb.Struct = nil
|
||||
var metadata *structpb.Struct
|
||||
if eventMetadata != nil {
|
||||
metadataStruct, err := structpb.NewStruct(*eventMetadata)
|
||||
metadataStruct, err := structpb.NewStruct(eventMetadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
@ -480,7 +400,7 @@ func NewRequestAttributes(
|
|||
) *auditV1.AttributeContext_Request {
|
||||
|
||||
rawQuery := request.URL.RawQuery
|
||||
var query *string = nil
|
||||
var query *string
|
||||
if rawQuery != nil && *rawQuery != "" {
|
||||
escapedQuery := url.QueryEscape(*rawQuery)
|
||||
query = &escapedQuery
|
||||
|
|
@ -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,
|
||||
|
|
@ -505,7 +425,7 @@ func NewRequestAttributes(
|
|||
}
|
||||
|
||||
// NewAuthorizationInfo returns protobuf AuthorizationInfo for the given parameters.
|
||||
func NewAuthorizationInfo(resourceName string, permission string, granted bool) *auditV1.AuthorizationInfo {
|
||||
func NewAuthorizationInfo(resourceName, permission string, granted bool) *auditV1.AuthorizationInfo {
|
||||
return &auditV1.AuthorizationInfo{
|
||||
Resource: resourceName,
|
||||
Permission: &permission,
|
||||
|
|
@ -514,14 +434,14 @@ func NewAuthorizationInfo(resourceName string, permission string, granted bool)
|
|||
}
|
||||
|
||||
// NewInsertId returns a correctly formatted insert id.
|
||||
func NewInsertId(insertTime time.Time, location string, workerId string, eventSequenceNumber uint64) string {
|
||||
func NewInsertId(insertTime time.Time, location, workerId string, eventSequenceNumber uint64) string {
|
||||
return fmt.Sprintf("%d/%s/%s/%d", insertTime.UnixNano(), location, workerId, eventSequenceNumber)
|
||||
}
|
||||
|
||||
// NewResponseMetadata returns protobuf response status with status code and short message.
|
||||
func NewResponseMetadata(statusCode int, numResponseItems *int64, responseSize *int64, headers map[string]string, responseTime time.Time) *auditV1.ResponseMetadata {
|
||||
func NewResponseMetadata(statusCode int, numResponseItems, responseSize *int64, headers map[string]string, responseTime time.Time) *auditV1.ResponseMetadata {
|
||||
|
||||
var message *string = nil
|
||||
var message *string
|
||||
if statusCode >= 400 && statusCode < 500 {
|
||||
text := "Client error"
|
||||
message = &text
|
||||
|
|
@ -530,7 +450,7 @@ func NewResponseMetadata(statusCode int, numResponseItems *int64, responseSize *
|
|||
message = &text
|
||||
}
|
||||
|
||||
var size *wrapperspb.Int64Value = nil
|
||||
var size *wrapperspb.Int64Value
|
||||
if responseSize != nil {
|
||||
size = wrapperspb.Int64(*responseSize)
|
||||
}
|
||||
|
|
@ -548,26 +468,26 @@ func NewResponseMetadata(statusCode int, numResponseItems *int64, responseSize *
|
|||
}
|
||||
|
||||
// NewResponseBody converts the JSON byte response into a protobuf struct.
|
||||
func NewResponseBody(response *[]byte) (*structpb.Struct, error) {
|
||||
func NewResponseBody(response []byte) (*structpb.Struct, error) {
|
||||
|
||||
// Return if nil
|
||||
if response == nil || len(*response) == 0 {
|
||||
if len(response) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Convert to protobuf struct
|
||||
return byteArrayToPbStruct(*response)
|
||||
return byteArrayToPbStruct(response)
|
||||
}
|
||||
|
||||
// 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 request.Body == nil || len(*request.Body) == 0 {
|
||||
if len(request.Body) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Convert to protobuf struct
|
||||
return byteArrayToPbStruct(*request.Body)
|
||||
return byteArrayToPbStruct(request.Body)
|
||||
}
|
||||
|
||||
// byteArrayToPbStruct converts a given json byte array into a protobuf struct.
|
||||
|
|
@ -582,12 +502,12 @@ func byteArrayToPbStruct(bytes []byte) (*structpb.Struct, error) {
|
|||
}
|
||||
|
||||
// FilterAndMergeHeaders filters ":authority", "Authorization", "B3" and "Host" headers as well as
|
||||
// all headers starting with the prefixes "X-" and "STACKIT-".
|
||||
// all headers starting with the prefixes "X-", "STACKIT-" and "grpcgateway-".
|
||||
// Headers are merged if there is more than one value for a given name.
|
||||
func FilterAndMergeHeaders(headers map[string][]string) map[string]string {
|
||||
var resultMap = make(map[string]string)
|
||||
skipHeaders := []string{":authority", "authorization", "b3", "host"}
|
||||
skipPrefixHeaders := []string{"x-", "stackit-"}
|
||||
skipPrefixHeaders := []string{"x-", "stackit-", "grpcgateway-"}
|
||||
|
||||
if len(headers) == 0 {
|
||||
return 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,
|
||||
|
|
@ -636,14 +556,17 @@ func AuditAttributesFromAuthorizationHeader(request *ApiRequest) (
|
|||
error,
|
||||
) {
|
||||
|
||||
var principalId = "none"
|
||||
var principalEmail = EmailAddressDoNotReplyAtStackItDotCloud
|
||||
emptyClaims, _ := structpb.NewStruct(make(map[string]interface{}))
|
||||
var auditClaims = emptyClaims
|
||||
var authenticationPrincipal = "none/none"
|
||||
var serviceAccountName *string = nil
|
||||
var principalId = "none"
|
||||
var principalEmail *string
|
||||
emptyClaims, err := structpb.NewStruct(make(map[string]interface{}))
|
||||
if err != nil {
|
||||
return nil, authenticationPrincipal, nil, nil, err
|
||||
}
|
||||
var auditClaims = emptyClaims
|
||||
var serviceAccountName *string
|
||||
audiences := make([]string, 0)
|
||||
var delegationInfo []*auditV1.ServiceAccountDelegationInfo = nil
|
||||
var delegationInfo []*auditV1.ServiceAccountDelegationInfo
|
||||
|
||||
authorizationHeaders := request.Header["Authorization"]
|
||||
if len(authorizationHeaders) == 0 {
|
||||
|
|
@ -652,7 +575,7 @@ func AuditAttributesFromAuthorizationHeader(request *ApiRequest) (
|
|||
}
|
||||
authorizationHeader := strings.Join(authorizationHeaders, ",")
|
||||
trimmedAuthorizationHeader := strings.TrimSpace(authorizationHeader)
|
||||
if len(trimmedAuthorizationHeader) > 0 {
|
||||
if trimmedAuthorizationHeader != "" {
|
||||
|
||||
// Parse claims
|
||||
token, err := parseToken(trimmedAuthorizationHeader)
|
||||
|
|
@ -712,9 +635,8 @@ func getTokenClaim(token jwt.Token, claimName string) *string {
|
|||
if claimExists {
|
||||
claimString := fmt.Sprintf("%s", claim)
|
||||
return &claimString
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractAuthenticationPrincipal(token jwt.Token) string {
|
||||
|
|
@ -792,9 +714,8 @@ func extractServiceAccountDelegationInfoDetails(actClaims map[string]interface{}
|
|||
nestedDelegations := extractServiceAccountDelegationInfo(actClaims)
|
||||
if len(nestedDelegations) > 0 {
|
||||
return append(delegations, nestedDelegations...)
|
||||
} else {
|
||||
return delegations
|
||||
}
|
||||
return delegations
|
||||
}
|
||||
|
||||
func extractServiceAccountDelegationInfo(claims map[string]interface{}) []*auditV1.ServiceAccountDelegationInfo {
|
||||
|
|
@ -820,144 +741,15 @@ func extractSubjectAndEmailFromActClaims(actClaim map[string]interface{}) (strin
|
|||
return principalId, principalEmail
|
||||
}
|
||||
|
||||
func extractSubjectAndEmail(token jwt.Token) (string, string) {
|
||||
var principalEmail string
|
||||
func extractSubjectAndEmail(token jwt.Token) (string, *string) {
|
||||
var principalEmail *string
|
||||
principalId := token.Subject()
|
||||
emailClaim, hasEmail := token.Get("email")
|
||||
if !hasEmail {
|
||||
principalEmail = EmailAddressDoNotReplyAtStackItDotCloud
|
||||
} else {
|
||||
principalEmail = fmt.Sprintf("%s", emailClaim)
|
||||
if hasEmail {
|
||||
trimmedEmail := strings.TrimSpace(fmt.Sprintf("%s", emailClaim))
|
||||
if trimmedEmail != "" {
|
||||
principalEmail = &trimmedEmail
|
||||
}
|
||||
}
|
||||
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 string, 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 len(operation) > 0 {
|
||||
method := StringToHttpMethod(requestMethod)
|
||||
var action string
|
||||
switch method {
|
||||
case auditV1.AttributeContext_HTTP_METHOD_PUT:
|
||||
fallthrough
|
||||
case 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
|
||||
} else {
|
||||
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,
|
||||
|
|
@ -331,6 +293,7 @@ func Test_FilterAndMergeRequestHeaders(t *testing.T) {
|
|||
headers := make(map[string][]string)
|
||||
headers["X-Forwarded-Proto"] = []string{"https"}
|
||||
headers["Stackit-test"] = []string{"test"}
|
||||
headers["grpcgateway-authorization"] = []string{userToken}
|
||||
|
||||
filteredHeaders := FilterAndMergeHeaders(headers)
|
||||
assert.Equal(t, 0, len(filteredHeaders))
|
||||
|
|
@ -376,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)
|
||||
|
|
@ -386,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)
|
||||
|
|
@ -396,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)
|
||||
|
|
@ -406,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)
|
||||
|
|
@ -415,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)
|
||||
|
|
@ -441,7 +404,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) {
|
|||
assert.Equal(t, []string{"stackit-resource-manager-dev"}, audiences)
|
||||
|
||||
assert.Equal(t, "stackit-resource-manager-dev", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail)
|
||||
assert.Nil(t, authenticationInfo.PrincipalEmail)
|
||||
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
|
|
@ -450,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)
|
||||
|
|
@ -479,7 +442,47 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) {
|
|||
assert.Equal(t, []string{"stackit", "api"}, audiences)
|
||||
|
||||
assert.Equal(t, "10f38b01-534b-47bb-a03a-e294ca2be4de", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail)
|
||||
|
||||
assert.Equal(t,
|
||||
"projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/10f38b01-534b-47bb-a03a-e294ca2be4de",
|
||||
*authenticationInfo.ServiceAccountName)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
})
|
||||
|
||||
t.Run("service account access token with underscore in subject", func(t *testing.T) {
|
||||
headers := make(map[string][]string)
|
||||
headers["Authorization"] = []string{serviceAccountTokenUnderscoreSubject}
|
||||
request := pkgAuditCommon.ApiRequest{Header: headers}
|
||||
|
||||
auditClaims, authenticationPrincipal, audiences, authenticationInfo, err :=
|
||||
AuditAttributesFromAuthorizationHeader(&request)
|
||||
assert.Nil(t, err)
|
||||
|
||||
auditClaimsMap := auditClaims.AsMap()
|
||||
assert.Len(t, auditClaimsMap, 12)
|
||||
assert.Equal(t, []interface{}{"stackit", "api"}, auditClaimsMap["aud"])
|
||||
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", auditClaimsMap["azp"])
|
||||
assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", auditClaimsMap["email"])
|
||||
assert.Equal(t, "2024-08-03 07:15:43 +0000 UTC", auditClaimsMap["exp"])
|
||||
assert.Equal(t, "2024-08-02 07:15:43 +0000 UTC", auditClaimsMap["iat"])
|
||||
assert.Equal(t, "stackit/serviceaccount", auditClaimsMap["iss"])
|
||||
assert.Equal(t, "84c30a46-1001-436f-859f-89c0ba19be1e", auditClaimsMap["jti"])
|
||||
assert.Equal(t, "api", auditClaimsMap["stackit/serviceaccount/namespace"])
|
||||
assert.Equal(t, "10f38b01-534b-47bb-a03a-e294ca2be4de", auditClaimsMap[TokenClaimStackitServiceAccountId])
|
||||
assert.Equal(t, "legacy", auditClaimsMap["stackit/serviceaccount/token.source"])
|
||||
assert.Equal(t, "dacc7830-843e-4c5e-86ff-aa0fb51d636f", auditClaimsMap[TokenClaimStackitProjectId])
|
||||
assert.Equal(t, "10f38b01_534b_47bb_a03a_e294ca2be4de", auditClaimsMap["sub"])
|
||||
|
||||
principal := fmt.Sprintf("%s/%s",
|
||||
url.QueryEscape("10f38b01_534b_47bb_a03a_e294ca2be4de"),
|
||||
url.QueryEscape("stackit/serviceaccount"))
|
||||
assert.Equal(t, principal, authenticationPrincipal)
|
||||
|
||||
assert.Equal(t, []string{"stackit", "api"}, audiences)
|
||||
|
||||
assert.Equal(t, "10f38b01_534b_47bb_a03a_e294ca2be4de", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail)
|
||||
|
||||
assert.Equal(t,
|
||||
"projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/10f38b01-534b-47bb-a03a-e294ca2be4de",
|
||||
|
|
@ -490,7 +493,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)
|
||||
|
|
@ -523,7 +526,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) {
|
|||
assert.Equal(t, []string{"stackit", "api"}, audiences)
|
||||
|
||||
assert.Equal(t, "f45009b2-6433-43c1-b6c7-618c44359e71", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "service-account-2-tj9srt1@sa.stackit.cloud", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "service-account-2-tj9srt1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail)
|
||||
|
||||
assert.Equal(t,
|
||||
"projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/f45009b2-6433-43c1-b6c7-618c44359e71",
|
||||
|
|
@ -538,7 +541,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)
|
||||
|
|
@ -574,7 +577,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) {
|
|||
assert.Equal(t, []string{"stackit", "api"}, audiences)
|
||||
|
||||
assert.Equal(t, "1734b4b6-1d5e-4819-9b50-29917a1b9ad5", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "service-account-3-fghsxw1@sa.stackit.cloud", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "service-account-3-fghsxw1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail)
|
||||
|
||||
assert.Equal(t,
|
||||
"projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/1734b4b6-1d5e-4819-9b50-29917a1b9ad5",
|
||||
|
|
@ -591,7 +594,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)
|
||||
|
|
@ -619,7 +622,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) {
|
|||
assert.Equal(t, []string{"stackit-portal-login-dev-client-id"}, audiences)
|
||||
|
||||
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail)
|
||||
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
|
|
@ -628,7 +631,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)
|
||||
|
|
@ -654,7 +657,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) {
|
|||
assert.Equal(t, []string{"https://stackit-service-account-dev.apps.01.cf.eu01.stackit.cloud"}, audiences)
|
||||
|
||||
assert.Equal(t, "5e426aed-c487-4c48-af25-87f69cf9cdd4", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "Lukas.Schmitt@stackit.cloud", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "Lukas.Schmitt@stackit.cloud", *authenticationInfo.PrincipalEmail)
|
||||
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
|
|
@ -670,9 +673,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",
|
||||
|
|
@ -699,7 +702,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)
|
||||
|
|
@ -753,7 +756,7 @@ func Test_NewAuditLogEntry(t *testing.T) {
|
|||
authenticationInfo := payload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
|
||||
|
|
@ -779,14 +782,14 @@ 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",
|
||||
Header: requestHeaders,
|
||||
Body: &requestBodyBytes,
|
||||
Body: requestBodyBytes,
|
||||
}
|
||||
|
||||
clientIp := "127.0.0.1"
|
||||
|
|
@ -813,7 +816,7 @@ func Test_NewAuditLogEntry(t *testing.T) {
|
|||
responseTime := time.Now().UTC()
|
||||
|
||||
auditResponse := AuditResponse{
|
||||
ResponseBodyBytes: &responseBody,
|
||||
ResponseBodyBytes: responseBody,
|
||||
ResponseStatusCode: responseStatusCode,
|
||||
ResponseHeaders: responseHeader,
|
||||
ResponseNumItems: &responseNumItems,
|
||||
|
|
@ -823,7 +826,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)
|
||||
|
|
@ -852,7 +855,7 @@ func Test_NewAuditLogEntry(t *testing.T) {
|
|||
logEntry, _ := NewAuditLogEntry(
|
||||
auditRequest,
|
||||
auditResponse,
|
||||
&eventMetadata,
|
||||
eventMetadata,
|
||||
auditMetadata)
|
||||
|
||||
assert.Equal(t, logName, logEntry.LogName)
|
||||
|
|
@ -886,7 +889,7 @@ func Test_NewAuditLogEntry(t *testing.T) {
|
|||
authenticationInfo := payload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
|
||||
|
|
@ -920,231 +923,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"),
|
||||
|
|
@ -37,7 +40,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.OperationName = ""
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - operation_name: value is required [required]")
|
||||
assert.EqualError(t, err, "validation error: operation_name: value is required")
|
||||
})
|
||||
|
||||
t.Run("invalid operation name", func(t *testing.T) {
|
||||
|
|
@ -45,7 +48,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.OperationName = "stackit.resource-manager.v1.INVALID.organizations.create"
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - operation_name: value does not match regex pattern `^stackit\\.[a-z0-9-]+\\.(?:v[0-9]+\\.)?(?:[a-z0-9-.]+\\.)?[a-z0-9-]+$` [string.pattern]")
|
||||
assert.EqualError(t, err, "validation error: operation_name: value does not match regex pattern `^stackit\\.[a-z0-9-]+\\.(?:v[0-9]+\\.)?(?:[a-z0-9-.]+\\.)?[a-z0-9-]+$`")
|
||||
})
|
||||
|
||||
t.Run("visibility invalid", func(t *testing.T) {
|
||||
|
|
@ -53,7 +56,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.Visibility = -1
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - visibility: value must be one of the defined enum values [enum.defined_only]")
|
||||
assert.EqualError(t, err, "validation error: visibility: value must be one of the defined enum values")
|
||||
})
|
||||
|
||||
t.Run("visibility unspecified", func(t *testing.T) {
|
||||
|
|
@ -61,7 +64,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.Visibility = auditV1.Visibility_VISIBILITY_UNSPECIFIED
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - visibility: value is required [required]")
|
||||
assert.EqualError(t, err, "validation error: visibility: value is required")
|
||||
})
|
||||
|
||||
t.Run("object identifier nil", func(t *testing.T) {
|
||||
|
|
@ -69,7 +72,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.ObjectIdentifier = nil
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - object_identifier: value is required [required]")
|
||||
assert.EqualError(t, err, "validation error: object_identifier: value is required")
|
||||
})
|
||||
|
||||
t.Run("object identifier id empty", func(t *testing.T) {
|
||||
|
|
@ -77,7 +80,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.ObjectIdentifier.Identifier = ""
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - object_identifier.identifier: value is required [required]")
|
||||
assert.EqualError(t, err, "validation error: object_identifier.identifier: value is required")
|
||||
})
|
||||
|
||||
t.Run("object identifier id not uuid", func(t *testing.T) {
|
||||
|
|
@ -85,7 +88,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.ObjectIdentifier.Identifier = "invalid"
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - object_identifier.identifier: value must be a valid UUID [string.uuid]")
|
||||
assert.EqualError(t, err, "validation error: object_identifier.identifier: value must be a valid UUID")
|
||||
})
|
||||
|
||||
t.Run("object identifier type empty", func(t *testing.T) {
|
||||
|
|
@ -93,7 +96,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.ObjectIdentifier.Type = ""
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - object_identifier.type: value is required [required]")
|
||||
assert.EqualError(t, err, "validation error: object_identifier.type: value is required")
|
||||
})
|
||||
|
||||
t.Run("data nil", func(t *testing.T) {
|
||||
|
|
@ -101,7 +104,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
event.Data = nil
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - data: exactly one field is required in oneof [required]")
|
||||
assert.EqualError(t, err, "validation error: data: exactly one field is required in oneof")
|
||||
})
|
||||
|
||||
t.Run("data empty", func(t *testing.T) {
|
||||
|
|
@ -112,7 +115,7 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
}}
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - unencrypted_data.data: value is required [required]")
|
||||
assert.EqualError(t, err, "validation error: unencrypted_data.data: value is required")
|
||||
})
|
||||
|
||||
t.Run("data protobuf type empty", func(t *testing.T) {
|
||||
|
|
@ -123,6 +126,59 @@ func Test_RoutableAuditEvent(t *testing.T) {
|
|||
}}
|
||||
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error:\n - unencrypted_data.protobuf_type: value is required [required]")
|
||||
assert.EqualError(t, err, "validation error: unencrypted_data.protobuf_type: value is required")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AuthenticationInfo(t *testing.T) {
|
||||
|
||||
validator, err := protovalidate.New()
|
||||
assert.NoError(t, err)
|
||||
|
||||
email := "x@x.x"
|
||||
newEvent := func() auditV1.AuthenticationInfo {
|
||||
return auditV1.AuthenticationInfo{
|
||||
PrincipalId: "1234567890",
|
||||
PrincipalEmail: &email,
|
||||
ServiceAccountName: nil,
|
||||
ServiceAccountDelegationInfo: nil,
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("valid event", func(t *testing.T) {
|
||||
event := newEvent()
|
||||
err := validator.Validate(&event)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("valid event without email", func(t *testing.T) {
|
||||
event := newEvent()
|
||||
event.PrincipalEmail = nil
|
||||
err := validator.Validate(&event)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("principal id contains only whitespace", func(t *testing.T) {
|
||||
event := newEvent()
|
||||
event.PrincipalId = " "
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error: principal_id: value does not match regex pattern `.*\\S.*`")
|
||||
})
|
||||
|
||||
t.Run("principal email contains only whitespace", func(t *testing.T) {
|
||||
event := newEvent()
|
||||
whitespaceEmail := " "
|
||||
event.PrincipalEmail = &whitespaceEmail
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error: principal_email: value must be a valid email address")
|
||||
})
|
||||
|
||||
t.Run("missing host in email", func(t *testing.T) {
|
||||
event := newEvent()
|
||||
invalidEmail := "@test.com"
|
||||
event.PrincipalEmail = &invalidEmail
|
||||
err := validator.Validate(&event)
|
||||
assert.EqualError(t, err, "validation error: principal_email: value must be a valid email address")
|
||||
})
|
||||
|
||||
}
|
||||
|
|
@ -4,16 +4,17 @@ 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"
|
||||
const serviceAccountTokenUnderscoreSubject = "Bearer eyJraWQiOiJaVFJqWlRNek5tSmlNRGt3TldJMU5USTRZVGxpT1RjMllUWXlZVE16WldNIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiIxMGYzOGIwMV81MzRiXzQ3YmJfYTAzYV9lMjk0Y2EyYmU0ZGUiLCJhdWQiOlsic3RhY2tpdCIsImFwaSJdLCJzdGFja2l0L3NlcnZpY2VhY2NvdW50L3Rva2VuLnNvdXJjZSI6ImxlZ2FjeSIsInN0YWNraXQvc2VydmljZWFjY291bnQvbmFtZXNwYWNlIjoiYXBpIiwic3RhY2tpdC9wcm9qZWN0L3Byb2plY3QuaWQiOiJkYWNjNzgzMC04NDNlLTRjNWUtODZmZi1hYTBmYjUxZDYzNmYiLCJhenAiOiJjZDk0ZjAxYS1kZjJlLTQ0NTYtOTAyZS00OGY1ZTU3ZjBiNjMiLCJpc3MiOiJzdGFja2l0L3NlcnZpY2VhY2NvdW50Iiwic3RhY2tpdC9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMTBmMzhiMDEtNTM0Yi00N2JiLWEwM2EtZTI5NGNhMmJlNGRlIiwiZXhwIjoxNzIyNjY5MzQzLCJpYXQiOjE3MjI1ODI5NDMsImVtYWlsIjoibXktc2VydmljZS15aWZjOWUxQHNhLnN0YWNraXQuY2xvdWQiLCJqdGkiOiI4NGMzMGE0Ni0xMDAxLTQzNmYtODU5Zi04OWMwYmExOWJlMWUifQ.bfD2TxfioqaKbqFJvnV_gq5zY_aoKVD2qzySMQjubaLQ5Vx_Tj95HU0q7gdNczNgcT0tBRyUp0pE4g4bwaPpB2MtYtUUunzpwG8sOX_OBchkorhcC4N50cdF5TR2pg0SMp3L6QBo3coHVbjHvaipshCj1NvyXYzARb4dSR0adrsIGnqy3IaScty1A2XQ7PN6SX_OVmxO5swpL0I-afKvCOffnChI3qmFAL5t6sFxm8PoaCWLIrkoxdtqxw5ZqsPPOJ0qDhssTuc3nE4JrQnzX8fZH5FiBVVHGT76KUNgPFd26UsVzbGqBXK20pn3pbIQHwbRiVOh6qanjr9kvHBXpQ"
|
||||
const serviceAccountTokenRepeatedlyImpersonated = "Bearer eyJraWQiOiJaVFJqWlRNek5tSmlNRGt3TldJMU5USTRZVGxpT1RjMllUWXlZVE16WldNIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiIxNzM0YjRiNi0xZDVlLTQ4MTktOWI1MC0yOTkxN2ExYjlhZDUiLCJpc3MiOiJzdGFja2l0L3NlcnZpY2VhY2NvdW50IiwiYXVkIjpbInN0YWNraXQiLCJhcGkiXSwic3RhY2tpdC9zZXJ2aWNlYWNjb3VudC90b2tlbi5zb3VyY2UiOiJvYXV0aDIiLCJhY3QiOnsic3ViIjoiZjQ1MDA5YjItNjQzMy00M2MxLWI2YzctNjE4YzQ0MzU5ZTcxIiwiYWN0Ijp7InN1YiI6IjAyYWVmNTE2LTMxN2YtNGVjMS1hMWRmLTFhY2JkNGQ0OWZlMyJ9fSwic3RhY2tpdC9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJhcGkiLCJzdGFja2l0L3Byb2plY3QvcHJvamVjdC5pZCI6ImRhY2M3ODMwLTg0M2UtNGM1ZS04NmZmLWFhMGZiNTFkNjM2ZiIsImF6cCI6ImY0NTAwOWIyLTY0MzMtNDNjMS1iNmM3LTYxOGM0NDM1OWU3MSIsInN0YWNraXQvc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjE3MzRiNGI2LTFkNWUtNDgxOS05YjUwLTI5OTE3YTFiOWFkNSIsImV4cCI6MTcyNDA2Mjk2MywiaWF0IjoxNzI0MDU5MzYzLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC0zLWZnaHN4dzFAc2Euc3RhY2tpdC5jbG91ZCIsImp0aSI6IjFmN2YxZWZjLTMzNDktNDExYS1hNWQ3LTIyNTVlMGE1YThhZSJ9.c1ae17bAtyOdmwXQbK37W-NTyOxo7iER5aHS_C0fU1qKl2BjOz708GLjH-_vxx9eKPeYznfI21_xlTaAvuG4Aco9f5YDK7fooTVHnDaOSSggqcEaDzDPrNXhhKEDxotJeq9zRMVCEStcbirjTounnLbuULRbO5GSY5jo-8n2UKxSZ2j5G_SjFHajdJwmzwvOttp08tdL8ck1uDdgVNBfcm0VIdb6WmgrCIUq5rmoa-cRPkdEurNtIEgEB_9U0Xh-SpmmsvFsWWeNIKz0e_5RCIyJonm_wMkGmblGegemkYL76ypeMNXTQsly1RozDIePfzHuZOWbySHSCd-vKQa2kw"
|
||||
const serviceAccountTokenImpersonated = "Bearer eyJraWQiOiJaVFJqWlRNek5tSmlNRGt3TldJMU5USTRZVGxpT1RjMllUWXlZVE16WldNIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiJmNDUwMDliMi02NDMzLTQzYzEtYjZjNy02MThjNDQzNTllNzEiLCJpc3MiOiJzdGFja2l0L3NlcnZpY2VhY2NvdW50IiwiYXVkIjpbInN0YWNraXQiLCJhcGkiXSwic3RhY2tpdC9zZXJ2aWNlYWNjb3VudC90b2tlbi5zb3VyY2UiOiJvYXV0aDIiLCJhY3QiOnsic3ViIjoiMDJhZWY1MTYtMzE3Zi00ZWMxLWExZGYtMWFjYmQ0ZDQ5ZmUzIn0sInN0YWNraXQvc2VydmljZWFjY291bnQvbmFtZXNwYWNlIjoiYXBpIiwic3RhY2tpdC9wcm9qZWN0L3Byb2plY3QuaWQiOiJkYWNjNzgzMC04NDNlLTRjNWUtODZmZi1hYTBmYjUxZDYzNmYiLCJhenAiOiIwMmFlZjUxNi0zMTdmLTRlYzEtYTFkZi0xYWNiZDRkNDlmZTMiLCJzdGFja2l0L3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJmNDUwMDliMi02NDMzLTQzYzEtYjZjNy02MThjNDQzNTllNzEiLCJleHAiOjE3MjQwNjI5MDcsImlhdCI6MTcyNDA1OTMwNywiZW1haWwiOiJzZXJ2aWNlLWFjY291bnQtMi10ajlzcnQxQHNhLnN0YWNraXQuY2xvdWQiLCJqdGkiOiIzNzU1NTE4My0wMWI5LTQyNzAtYmRjMS02OWI0ZmNmZDVlZTkifQ.auBvvsIesFMAlWOCPCPC77DrrHF7gSKZwKs_Zry5KFvu2bpZZC1BcSXOc8b9eh0SzANI9M9aGJBhOzOm39-ZZ5XOQ-6_y1aWuEenYQ6kT5D3GzCUTMDzSi1lcZ4IG5nFMa_AAlVEN_7AMv7LHGtz49bWLJnAgeTo1cvof-OgP4mCQ5O6E0iyAq-5u8V8NJL7HIZy7BDe4J1mjfYhwKagrN7QFWu4fhN4TNS7d922X_6V489BhjRFRYjLW_qDnv912JorbGRz_XwNy_dPA81EkdMyKE0BJUezguJUEKEG2_JEi9O64Flcoi6x8cFHYhaDuMMSLipzePaHdyk2lQtH7Q"
|
||||
const serviceAccountToken = "Bearer eyJraWQiOiJaVFJqWlRNek5tSmlNRGt3TldJMU5USTRZVGxpT1RjMllUWXlZVE16WldNIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiIxMGYzOGIwMS01MzRiLTQ3YmItYTAzYS1lMjk0Y2EyYmU0ZGUiLCJhdWQiOlsic3RhY2tpdCIsImFwaSJdLCJzdGFja2l0L3NlcnZpY2VhY2NvdW50L3Rva2VuLnNvdXJjZSI6ImxlZ2FjeSIsInN0YWNraXQvc2VydmljZWFjY291bnQvbmFtZXNwYWNlIjoiYXBpIiwic3RhY2tpdC9wcm9qZWN0L3Byb2plY3QuaWQiOiJkYWNjNzgzMC04NDNlLTRjNWUtODZmZi1hYTBmYjUxZDYzNmYiLCJhenAiOiJjZDk0ZjAxYS1kZjJlLTQ0NTYtOTAyZS00OGY1ZTU3ZjBiNjMiLCJpc3MiOiJzdGFja2l0L3NlcnZpY2VhY2NvdW50Iiwic3RhY2tpdC9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMTBmMzhiMDEtNTM0Yi00N2JiLWEwM2EtZTI5NGNhMmJlNGRlIiwiZXhwIjoxNzIyNjY5MzQzLCJpYXQiOjE3MjI1ODI5NDMsImVtYWlsIjoibXktc2VydmljZS15aWZjOWUxQHNhLnN0YWNraXQuY2xvdWQiLCJqdGkiOiI4NGMzMGE0Ni0xMDAxLTQzNmYtODU5Zi04OWMwYmExOWJlMWUifQ.hb8X9VKc9xViHgNMyFHT9ePj_lyEwTV1D2es8E278WtoCJ9-4GPPQGjhcLGGrigjnvpRYV2LKzNqpQslerT5lFT_pHACsryaAE0ImYjmoe-nutA7BBpYuM_JN6pk5VIjVFLTqRKeIvFexPacqS2Vo3YoK1GvxPB8WPWBbGIsBtMl-PTm8OTwwzooBOoCRhhMR-E1lFbAymLsc1JI4yDQKLLomvhEopgmocCnQ-P1QkiKMqdkNxiD_YYLLYTOApg6d62BhqpH66ziqx493AStdZ8d5Kjvf3e1knDhaxVwNCghQj7lSo2kNAqZe__g2tiXpiZNTXBFJ_5HgQMLh67wng"
|
||||
|
|
@ -21,8 +22,9 @@ const userToken = "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My
|
|||
const userTokenWithSimpleAudience = "Bearer eyJhbGciOiJSUzUxMiIsImtpZCI6InNlcnZpY2UtYWNjb3VudC1mMDdiZjZhOC02MjA3LTRmOGItYjNlOS03M2VkMGJlYjg4ZjUiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL3N0YWNraXQtc2VydmljZS1hY2NvdW50LWRldi5hcHBzLjAxLmNmLmV1MDEuc3RhY2tpdC5jbG91ZCIsImVtYWlsIjoiTHVrYXMuU2NobWl0dEBzdGFja2l0LmNsb3VkIiwiZXhwIjoxNzMyMTgyMDM1LCJpYXQiOjE3MzIxNzg0MzUsImlzcyI6Imh0dHBzOi8vYXBpLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiYzJiZTE2NTEtMWU1NC00ZTZlLWJhYzMtZWYwNzJiM2YwMTQ5IiwibmJmIjoxNzMyMTc4NDE4LCJyb2xlcyI6bnVsbCwic2NvcGUiOiJvcGVuaWQgZW1haWwgcG9ydGFsLWJmZiIsInN1YiI6IjVlNDI2YWVkLWM0ODctNGM0OC1hZjI1LTg3ZjY5Y2Y5Y2RkNCIsInVzZXJfaWQiOiIiLCJ4X2NsaWVudF9pZCI6IiIsInppZCI6IiJ9.notavailable"
|
||||
|
||||
var TestHeaders = map[string][]string{"user-agent": {"custom"}, "authorization": {userToken}}
|
||||
var TestHeadersSa = map[string][]string{"user-agent": {"custom"}, "authorization": {serviceAccountTokenUnderscoreSubject}}
|
||||
|
||||
func newOrganizationAuditEvent(
|
||||
func NewOrganizationAuditEvent(
|
||||
customization *func(
|
||||
*auditV1.AuditLogEntry,
|
||||
*auditV1.ObjectIdentifier,
|
||||
|
|
@ -41,20 +43,21 @@ func newOrganizationAuditEvent(
|
|||
headers["Content-Type"] = "application/json"
|
||||
labels := make(map[string]string)
|
||||
labels["label1"] = "value1"
|
||||
email := "user@example.com"
|
||||
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",
|
||||
PrincipalEmail: &email,
|
||||
ServiceAccountName: nil,
|
||||
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 +105,7 @@ func newOrganizationAuditEvent(
|
|||
|
||||
objectIdentifier := &auditV1.ObjectIdentifier{
|
||||
Identifier: identifier.String(),
|
||||
Type: string(ObjectTypeOrganization),
|
||||
Type: string(pkgAuditCommon.ObjectTypeOrganization),
|
||||
}
|
||||
|
||||
if customization != nil {
|
||||
|
|
@ -112,7 +115,7 @@ func newOrganizationAuditEvent(
|
|||
return auditEvent, objectIdentifier
|
||||
}
|
||||
|
||||
func newFolderAuditEvent(
|
||||
func NewFolderAuditEvent(
|
||||
customization *func(
|
||||
*auditV1.AuditLogEntry,
|
||||
*auditV1.ObjectIdentifier,
|
||||
|
|
@ -131,20 +134,21 @@ func newFolderAuditEvent(
|
|||
headers["Content-Type"] = "application/json"
|
||||
labels := make(map[string]string)
|
||||
labels["label1"] = "value1"
|
||||
email := "user@example.com"
|
||||
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",
|
||||
PrincipalEmail: &email,
|
||||
ServiceAccountName: nil,
|
||||
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 +196,7 @@ func newFolderAuditEvent(
|
|||
|
||||
objectIdentifier := &auditV1.ObjectIdentifier{
|
||||
Identifier: identifier.String(),
|
||||
Type: string(ObjectTypeFolder),
|
||||
Type: string(pkgAuditCommon.ObjectTypeFolder),
|
||||
}
|
||||
|
||||
if customization != nil {
|
||||
|
|
@ -202,7 +206,7 @@ func newFolderAuditEvent(
|
|||
return auditEvent, objectIdentifier
|
||||
}
|
||||
|
||||
func newProjectAuditEvent(
|
||||
func NewProjectAuditEvent(
|
||||
customization *func(
|
||||
*auditV1.AuditLogEntry,
|
||||
*auditV1.ObjectIdentifier,
|
||||
|
|
@ -221,20 +225,21 @@ func newProjectAuditEvent(
|
|||
headers["Content-Type"] = "application/json"
|
||||
labels := make(map[string]string)
|
||||
labels["label1"] = "value1"
|
||||
email := "user@example.com"
|
||||
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",
|
||||
PrincipalEmail: &email,
|
||||
ServiceAccountName: nil,
|
||||
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 +287,7 @@ func newProjectAuditEvent(
|
|||
|
||||
objectIdentifier := &auditV1.ObjectIdentifier{
|
||||
Identifier: identifier.String(),
|
||||
Type: string(ObjectTypeProject),
|
||||
Type: string(pkgAuditCommon.ObjectTypeProject),
|
||||
}
|
||||
|
||||
if customization != nil {
|
||||
|
|
@ -292,7 +297,7 @@ func newProjectAuditEvent(
|
|||
return auditEvent, objectIdentifier
|
||||
}
|
||||
|
||||
func newProjectSystemAuditEvent(
|
||||
func NewProjectSystemAuditEvent(
|
||||
customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry {
|
||||
|
||||
identifier := uuid.New()
|
||||
|
|
@ -306,20 +311,21 @@ func newProjectSystemAuditEvent(
|
|||
serviceAccountId := uuid.NewString()
|
||||
serviceAccountName := fmt.Sprintf("projects/%s/service-accounts/%s", identifier, serviceAccountId)
|
||||
delegationPrincipal := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_SystemPrincipal_{}}
|
||||
email := "service-account@sa.stackit.cloud"
|
||||
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",
|
||||
PrincipalEmail: &email,
|
||||
ServiceAccountName: &serviceAccountName,
|
||||
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 +378,7 @@ func newProjectSystemAuditEvent(
|
|||
return auditEvent
|
||||
}
|
||||
|
||||
func newSystemAuditEvent(
|
||||
func NewSystemAuditEvent(
|
||||
customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry {
|
||||
|
||||
identifier := uuid.Nil
|
||||
|
|
@ -386,20 +392,21 @@ func newSystemAuditEvent(
|
|||
serviceAccountId := uuid.NewString()
|
||||
serviceAccountName := fmt.Sprintf("projects/%s/service-accounts/%s", identifier, serviceAccountId)
|
||||
delegationPrincipal := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_SystemPrincipal_{}}
|
||||
email := "service-account@sa.stackit.cloud"
|
||||
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",
|
||||
PrincipalEmail: &email,
|
||||
ServiceAccountName: &serviceAccountName,
|
||||
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"
|
||||
)
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ func TraceParentAndStateFromContext(ctx context.Context) (string, string) {
|
|||
}
|
||||
|
||||
// AddTraceParentAndStateToContext adds trace and state related information to the given context.
|
||||
func AddTraceParentAndStateToContext(ctx context.Context, traceParent string, traceState string) context.Context {
|
||||
func AddTraceParentAndStateToContext(ctx context.Context, traceParent, traceState string) context.Context {
|
||||
mapCarrier := propagation.MapCarrier{}
|
||||
mapCarrier[traceParentHeader] = traceParent
|
||||
mapCarrier[traceStateHeader] = traceState
|
||||
|
|
@ -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) {
|
||||
237
internal/messaging/amqp_connection.go
Normal file
237
internal/messaging/amqp_connection.go
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func newDefaultAmqpConn(conn *amqp.Conn) *defaultAmqpConn {
|
||||
return &defaultAmqpConn{
|
||||
Conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return newDefaultAmqpSession(session), nil
|
||||
}
|
||||
|
||||
func (d defaultAmqpConn) Close() error {
|
||||
return d.Conn.Close()
|
||||
}
|
||||
|
||||
func (d defaultAmqpConn) Done() <-chan struct{} {
|
||||
return d.Conn.Done()
|
||||
}
|
||||
|
||||
var _ AmqpConn = (*defaultAmqpConn)(nil)
|
||||
|
||||
type amqpDial interface {
|
||||
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)
|
||||
Close(ctx context.Context) error
|
||||
}
|
||||
|
||||
type defaultAmqpSession struct {
|
||||
Session *amqp.Session
|
||||
}
|
||||
|
||||
func newDefaultAmqpSession(session *amqp.Session) *defaultAmqpSession {
|
||||
return &defaultAmqpSession{
|
||||
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) Close(ctx context.Context) error {
|
||||
return s.Session.Close(ctx)
|
||||
}
|
||||
|
||||
var _ AmqpSession = (*defaultAmqpSession)(nil)
|
||||
|
||||
type defaultAmqpDialer struct{}
|
||||
|
||||
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
|
||||
}
|
||||
return newDefaultAmqpConn(dial), nil
|
||||
}
|
||||
|
||||
var _ amqpDial = (*defaultAmqpDialer)(nil)
|
||||
|
||||
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{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) NewSender(ctx context.Context, topic string) (*AmqpSenderSession, error) {
|
||||
if c.Conn == nil {
|
||||
return nil, errors.New("connection is not initialized")
|
||||
}
|
||||
|
||||
if c.internalIsClosed() {
|
||||
return nil, ErrConnectionClosed
|
||||
}
|
||||
|
||||
c.Lock.RLock()
|
||||
defer c.Lock.RUnlock()
|
||||
|
||||
// new session
|
||||
newSession, err := c.Conn.NewSession(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new session: %w", err)
|
||||
}
|
||||
|
||||
// new sender
|
||||
newSender, err := newSession.NewSender(ctx, topic, nil)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("new internal sender: %w", err)
|
||||
|
||||
closeErr := newSession.Close(ctx)
|
||||
if closeErr != nil {
|
||||
return nil, errors.Join(err, fmt.Errorf("close session: %w", closeErr))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AmqpSenderSession{newSession, newSender}, nil
|
||||
}
|
||||
|
||||
func As[T any](value any, err error) (*T, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
castedValue, isType := value.(*T)
|
||||
if !isType {
|
||||
return nil, fmt.Errorf("could not cast value: %T", value)
|
||||
}
|
||||
return castedValue, nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) Connect() error {
|
||||
c.Lock.Lock()
|
||||
defer c.Lock.Unlock()
|
||||
|
||||
subCtx, cancel := context.WithTimeout(context.Background(), connectionTimeoutSeconds*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := c.internalConnect(subCtx); err != nil {
|
||||
return fmt.Errorf("internal connect: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) internalConnect(ctx context.Context) error {
|
||||
if c.Conn == nil {
|
||||
// Set credentials if specified
|
||||
auth := amqp.SASLTypeAnonymous()
|
||||
if c.Username != "" && c.Password != "" {
|
||||
auth = amqp.SASLTypePlain(c.Username, c.Password)
|
||||
} else {
|
||||
slog.Debug("amqp connection: connect: using anonymous messaging")
|
||||
}
|
||||
options := &amqp.ConnOptions{
|
||||
SASLType: auth,
|
||||
}
|
||||
|
||||
// Initialize connection
|
||||
conn, err := c.Dialer.Dial(ctx, c.BrokerUrl, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dial: %w", err)
|
||||
}
|
||||
c.Conn = conn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) Close() error {
|
||||
c.Lock.Lock()
|
||||
defer c.Lock.Unlock()
|
||||
|
||||
if err := c.internalClose(); err != nil {
|
||||
return fmt.Errorf("internal close: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) internalClose() error {
|
||||
if c.Conn != nil {
|
||||
if err := c.Conn.Close(); err != nil {
|
||||
return fmt.Errorf("connection close: %w", err)
|
||||
}
|
||||
c.Conn = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) IsClosed() bool {
|
||||
c.Lock.RLock()
|
||||
defer c.Lock.RUnlock()
|
||||
|
||||
return c.internalIsClosed()
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) internalIsClosed() bool {
|
||||
if c.Conn == nil {
|
||||
return true
|
||||
}
|
||||
select {
|
||||
case <-c.Conn.Done():
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
231
internal/messaging/amqp_connection_pool.go
Normal file
231
internal/messaging/amqp_connection_pool.go
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
pkgCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common"
|
||||
)
|
||||
|
||||
type connectionProvider interface {
|
||||
NewAmqpConnection(config pkgCommon.AmqpConnectionConfig, connectionName string) *AmqpConnection
|
||||
}
|
||||
|
||||
type defaultAmqpConnectionProvider struct{}
|
||||
|
||||
func (p defaultAmqpConnectionProvider) NewAmqpConnection(config pkgCommon.AmqpConnectionConfig, connectionName string) *AmqpConnection {
|
||||
return NewAmqpConnection(config, connectionName)
|
||||
}
|
||||
|
||||
var _ connectionProvider = (*defaultAmqpConnectionProvider)(nil)
|
||||
|
||||
type ConnectionPool interface {
|
||||
Close() error
|
||||
NewHandle() *ConnectionPoolHandle
|
||||
GetConnection(handle *ConnectionPoolHandle) (*AmqpConnection, error)
|
||||
}
|
||||
|
||||
type AmqpConnectionPool struct {
|
||||
Config pkgCommon.AmqpConnectionPoolConfig
|
||||
ConnectionName string
|
||||
Connections []*AmqpConnection
|
||||
ConnectionProvider connectionProvider
|
||||
HandleOffset int
|
||||
Lock sync.RWMutex
|
||||
}
|
||||
|
||||
type ConnectionPoolHandle struct {
|
||||
ConnectionOffset int
|
||||
}
|
||||
|
||||
func NewDefaultAmqpConnectionPool(config pkgCommon.AmqpConnectionConfig, connectionName string) (ConnectionPool, error) {
|
||||
poolConfig := pkgCommon.AmqpConnectionPoolConfig{
|
||||
Parameters: config,
|
||||
PoolSize: 2,
|
||||
}
|
||||
return NewAmqpConnectionPool(poolConfig, connectionName)
|
||||
}
|
||||
|
||||
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{},
|
||||
}
|
||||
|
||||
if err := pool.initializeConnections(); err != nil {
|
||||
if closeErr := pool.Close(); closeErr != nil {
|
||||
return nil, errors.Join(err, fmt.Errorf("initialize amqp connection: pool closed: %w", closeErr))
|
||||
}
|
||||
return nil, fmt.Errorf("initialize connections: %w", err)
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) initializeConnections() error {
|
||||
if len(p.Connections) < p.Config.PoolSize {
|
||||
p.Lock.Lock()
|
||||
defer p.Lock.Unlock()
|
||||
|
||||
numMissingConnections := p.Config.PoolSize - len(p.Connections)
|
||||
|
||||
for i := 0; i < numMissingConnections; i++ {
|
||||
if err := p.internalAddConnection(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) internalAddConnection() error {
|
||||
newConnection, err := p.internalNewConnection()
|
||||
if err != nil {
|
||||
return fmt.Errorf("new connection: %w", err)
|
||||
}
|
||||
p.Connections = append(p.Connections, newConnection)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) internalNewConnection() (*AmqpConnection, error) {
|
||||
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))
|
||||
|
||||
// retry
|
||||
if err = conn.Connect(); err != nil {
|
||||
connectErr := fmt.Errorf("new internal connection: %w", err)
|
||||
if closeErr := conn.Close(); closeErr != nil {
|
||||
// this case should never happen as the inner connection should always be null, therefore
|
||||
// it should not have to be closed, i.e. be able to return errors.
|
||||
return nil, errors.Join(connectErr, fmt.Errorf("close connection: %w", closeErr))
|
||||
}
|
||||
return nil, connectErr
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) Close() error {
|
||||
p.Lock.Lock()
|
||||
defer p.Lock.Unlock()
|
||||
|
||||
closeErrors := make([]error, 0)
|
||||
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)
|
||||
if len(closeErrors) > 0 {
|
||||
return errors.Join(closeErrors...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) NewHandle() *ConnectionPoolHandle {
|
||||
p.Lock.Lock()
|
||||
defer p.Lock.Unlock()
|
||||
|
||||
offset := p.HandleOffset
|
||||
p.HandleOffset++
|
||||
|
||||
offset %= p.Config.PoolSize
|
||||
|
||||
return &ConnectionPoolHandle{
|
||||
ConnectionOffset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) GetConnection(handle *ConnectionPoolHandle) (*AmqpConnection, error) {
|
||||
// get the requested connection or another one
|
||||
conn, addConnection := p.nextConnectionForHandle(handle)
|
||||
|
||||
// renew the requested connection if the request connection is closed
|
||||
if conn == nil || addConnection {
|
||||
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 {
|
||||
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()
|
||||
return nil, fmt.Errorf("renew connection: %w", err)
|
||||
}
|
||||
|
||||
// case: connection could not be renewed but another connection will be returned
|
||||
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
|
||||
conn = connection
|
||||
}
|
||||
}
|
||||
p.Lock.Unlock()
|
||||
}
|
||||
|
||||
if conn == nil {
|
||||
return nil, fmt.Errorf("amqp connection pool: get connection: failed to obtain connection")
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) nextConnectionForHandle(handle *ConnectionPoolHandle) (*AmqpConnection, bool) {
|
||||
// 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++ {
|
||||
|
||||
// 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]
|
||||
} else {
|
||||
// handle the edge case that the pool is empty on shutdown
|
||||
conn = nil
|
||||
}
|
||||
p.Lock.RUnlock()
|
||||
|
||||
// remember that the requested is closed, retry with the next
|
||||
if conn == nil {
|
||||
addConnection = true
|
||||
continue
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
addConnection = true
|
||||
continue
|
||||
}
|
||||
|
||||
return conn, addConnection
|
||||
}
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func (p *AmqpConnectionPool) connectionIndex(handle *ConnectionPoolHandle, iteration int) int {
|
||||
if iteration+handle.ConnectionOffset >= p.Config.PoolSize {
|
||||
return (iteration + handle.ConnectionOffset) % p.Config.PoolSize
|
||||
}
|
||||
return iteration + handle.ConnectionOffset
|
||||
}
|
||||
581
internal/messaging/amqp_connection_pool_test.go
Normal file
581
internal/messaging/amqp_connection_pool_test.go
Normal file
|
|
@ -0,0 +1,581 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"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 pkgMessagingCommon.AmqpConnectionConfig, connectionName string) *AmqpConnection {
|
||||
args := p.Called(config, connectionName)
|
||||
return args.Get(0).(*AmqpConnection)
|
||||
}
|
||||
|
||||
var _ connectionProvider = (*connectionProviderMock)(nil)
|
||||
|
||||
func Test_AmqpConnectionPool_GetHandle(t *testing.T) {
|
||||
|
||||
t.Run("next handle", func(t *testing.T) {
|
||||
pool := AmqpConnectionPool{
|
||||
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)
|
||||
})
|
||||
|
||||
t.Run("next handle high offset", func(t *testing.T) {
|
||||
pool := AmqpConnectionPool{
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnectionPool_internalAddConnection(t *testing.T) {
|
||||
|
||||
t.Run("internal add connection", func(t *testing.T) {
|
||||
conn := &amqpConnMock{}
|
||||
|
||||
dialer := &amqpDialMock{}
|
||||
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
||||
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Dialer: dialer,
|
||||
}
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
||||
pool := AmqpConnectionPool{
|
||||
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))
|
||||
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1)
|
||||
dialer.AssertNumberOfCalls(t, "Dial", 1)
|
||||
})
|
||||
|
||||
t.Run("dialer error", func(t *testing.T) {
|
||||
conn := &amqpConnMock{}
|
||||
|
||||
dialer := &amqpDialMock{}
|
||||
var c *amqpConnMock = nil
|
||||
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error")).Once()
|
||||
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
||||
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Dialer: dialer,
|
||||
}
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
||||
pool := AmqpConnectionPool{
|
||||
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))
|
||||
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1)
|
||||
dialer.AssertNumberOfCalls(t, "Dial", 2)
|
||||
})
|
||||
|
||||
t.Run("repetitive dialer error", func(t *testing.T) {
|
||||
dialer := &amqpDialMock{}
|
||||
var c *amqpConnMock = nil
|
||||
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error"))
|
||||
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Dialer: dialer,
|
||||
}
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
||||
pool := AmqpConnectionPool{
|
||||
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))
|
||||
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 1)
|
||||
dialer.AssertNumberOfCalls(t, "Dial", 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnectionPool_initializeConnections(t *testing.T) {
|
||||
|
||||
t.Run("initialize connections successfully", func(t *testing.T) {
|
||||
|
||||
conn := &amqpConnMock{}
|
||||
dialer := &amqpDialMock{}
|
||||
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
||||
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Dialer: dialer,
|
||||
}
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
||||
pool := AmqpConnectionPool{
|
||||
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))
|
||||
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 5)
|
||||
})
|
||||
|
||||
t.Run("fail initialization of connections", func(t *testing.T) {
|
||||
|
||||
var c *amqpConnMock = nil
|
||||
failingDialer := &amqpDialMock{}
|
||||
failingDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error"))
|
||||
|
||||
failingConnection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Dialer: failingDialer,
|
||||
}
|
||||
|
||||
conn := &amqpConnMock{}
|
||||
successfulDialer := &amqpDialMock{}
|
||||
successfulDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
||||
|
||||
successfulConnection := &AmqpConnection{
|
||||
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: 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))
|
||||
connectionProvider.AssertNumberOfCalls(t, "NewAmqpConnection", 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnectionPool_Close(t *testing.T) {
|
||||
|
||||
t.Run("close connection successfully", func(t *testing.T) {
|
||||
// add 5 connections to the pool
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Close").Return(nil)
|
||||
|
||||
dialer := &amqpDialMock{}
|
||||
dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil)
|
||||
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Dialer: dialer,
|
||||
}
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
||||
pool := AmqpConnectionPool{
|
||||
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))
|
||||
|
||||
// close the pool
|
||||
err = pool.Close()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(pool.Connections))
|
||||
for _, c := range pool.Connections {
|
||||
assert.Nil(t, c)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("close connection fail", func(t *testing.T) {
|
||||
// add 5 connections to the pool
|
||||
failingConn := &amqpConnMock{}
|
||||
failingConn.On("Close").Return(errors.New("test error"))
|
||||
|
||||
failingDialer := &amqpDialMock{}
|
||||
failingDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(failingConn, nil)
|
||||
|
||||
failingConnection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Dialer: failingDialer,
|
||||
}
|
||||
|
||||
successfulConn := &amqpConnMock{}
|
||||
successfulConn.On("Close").Return(nil)
|
||||
successfulDialer := &amqpDialMock{}
|
||||
successfulDialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(successfulConn, nil)
|
||||
|
||||
successfulConnection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Dialer: successfulDialer,
|
||||
}
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(successfulConnection).Times(2)
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(failingConnection).Times(2)
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(successfulConnection).Times(1)
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
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))
|
||||
|
||||
// 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.Nil(t, c)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnectionPool_nextConnectionForHandle(t *testing.T) {
|
||||
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
||||
return channel
|
||||
}
|
||||
|
||||
newActiveConnection := func() *AmqpConnection {
|
||||
channel := make(chan struct{})
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Done", mock.Anything).Return(channelReceiver(channel))
|
||||
return &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
newClosedConnection := func() *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,
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("next connection for requested handle", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
for i := 0; i < 5; i++ {
|
||||
connections = append(connections, newActiveConnection())
|
||||
}
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
}
|
||||
|
||||
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 1})
|
||||
assert.NotNil(t, connection)
|
||||
assert.False(t, addConnection)
|
||||
})
|
||||
|
||||
t.Run("nil connection for requested handle", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
connections = append(connections, newActiveConnection())
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, newActiveConnection())
|
||||
connections = append(connections, newActiveConnection())
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
}
|
||||
|
||||
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 1})
|
||||
assert.NotNil(t, connection)
|
||||
assert.True(t, addConnection)
|
||||
})
|
||||
|
||||
t.Run("closed connection for requested handle", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
connections = append(connections, newActiveConnection())
|
||||
connections = append(connections, newClosedConnection())
|
||||
connections = append(connections, newClosedConnection())
|
||||
connections = append(connections, newActiveConnection())
|
||||
connections = append(connections, newActiveConnection())
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
}
|
||||
|
||||
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 1})
|
||||
assert.NotNil(t, connection)
|
||||
assert.True(t, addConnection)
|
||||
})
|
||||
|
||||
t.Run("no connection for requested handle", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
}
|
||||
|
||||
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 1})
|
||||
assert.Nil(t, connection)
|
||||
assert.True(t, addConnection)
|
||||
})
|
||||
|
||||
t.Run("connection for requested handle with large index", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, newActiveConnection())
|
||||
connections = append(connections, nil)
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
}
|
||||
|
||||
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 23})
|
||||
assert.NotNil(t, connection)
|
||||
assert.False(t, addConnection)
|
||||
})
|
||||
|
||||
t.Run("connection for requested handle nil with large index", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, newActiveConnection())
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
}
|
||||
|
||||
connection, addConnection := pool.nextConnectionForHandle(&ConnectionPoolHandle{ConnectionOffset: 23})
|
||||
assert.NotNil(t, connection)
|
||||
assert.True(t, addConnection)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnectionPool_GetConnection(t *testing.T) {
|
||||
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
||||
return channel
|
||||
}
|
||||
|
||||
newActiveConnection := func() *AmqpConnection {
|
||||
channel := make(chan struct{})
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Done", mock.Anything).Return(channelReceiver(channel))
|
||||
return &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("get connection for requested handle", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
for i := 0; i < 5; i++ {
|
||||
connections = append(connections, newActiveConnection())
|
||||
}
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
}
|
||||
|
||||
connection, err := pool.GetConnection(&ConnectionPoolHandle{ConnectionOffset: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, connection)
|
||||
assert.Equal(t, connections[1], connection)
|
||||
assert.Equal(t, 5, len(connections))
|
||||
})
|
||||
|
||||
t.Run("add connection if missing", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 5)
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(newActiveConnection())
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
ConnectionProvider: connectionProvider,
|
||||
}
|
||||
|
||||
connection, err := pool.GetConnection(&ConnectionPoolHandle{ConnectionOffset: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, connection)
|
||||
assert.Equal(t, connections[1], connection)
|
||||
assert.Equal(t, 5, len(connections))
|
||||
})
|
||||
|
||||
t.Run("add connection fails returns alternative connection", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
connections = append(connections, newActiveConnection())
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, newActiveConnection())
|
||||
connections = append(connections, newActiveConnection())
|
||||
connections = append(connections, newActiveConnection())
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
|
||||
dialer := &amqpDialMock{}
|
||||
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,
|
||||
}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
ConnectionProvider: connectionProvider,
|
||||
}
|
||||
|
||||
connection, err := pool.GetConnection(&ConnectionPoolHandle{ConnectionOffset: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, connection)
|
||||
assert.Nil(t, connections[1])
|
||||
assert.Equal(t, connections[2], connection)
|
||||
assert.Equal(t, 5, len(connections))
|
||||
})
|
||||
|
||||
t.Run("add connection fails", func(t *testing.T) {
|
||||
connections := make([]*AmqpConnection, 0)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
connections = append(connections, nil)
|
||||
|
||||
connectionProvider := &connectionProviderMock{}
|
||||
|
||||
dialer := &amqpDialMock{}
|
||||
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,
|
||||
}
|
||||
connectionProvider.On("NewAmqpConnection", mock.Anything, mock.Anything).Return(connection)
|
||||
|
||||
pool := AmqpConnectionPool{
|
||||
Config: pkgMessagingCommon.AmqpConnectionPoolConfig{PoolSize: 5},
|
||||
HandleOffset: 0,
|
||||
Lock: sync.RWMutex{},
|
||||
Connections: connections,
|
||||
ConnectionProvider: connectionProvider,
|
||||
}
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
309
internal/messaging/amqp_connection_test.go
Normal file
309
internal/messaging/amqp_connection_test.go
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-amqp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
pkgCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/common"
|
||||
)
|
||||
|
||||
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) (AmqpSession, error) {
|
||||
args := m.Called(ctx, opts)
|
||||
return args.Get(0).(AmqpSession), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *amqpConnMock) Close() error {
|
||||
args := m.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
var _ AmqpConn = (*amqpConnMock)(nil)
|
||||
|
||||
type amqpDialMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
var _ amqpDial = (*amqpDialMock)(nil)
|
||||
|
||||
type amqpSessionMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (m *amqpSessionMock) Close(ctx context.Context) error {
|
||||
args := m.Called(ctx)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
var _ AmqpSession = (*amqpSessionMock)(nil)
|
||||
|
||||
func Test_AmqpConnection_IsClosed(t *testing.T) {
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
}
|
||||
|
||||
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
||||
return channel
|
||||
}
|
||||
|
||||
t.Run("is closed - connection nil", func(t *testing.T) {
|
||||
assert.True(t, connection.IsClosed())
|
||||
})
|
||||
|
||||
t.Run("is closed", func(t *testing.T) {
|
||||
channel := make(chan struct{})
|
||||
close(channel)
|
||||
amqpConnMock := &amqpConnMock{}
|
||||
amqpConnMock.On("Done").Return(channelReceiver(channel))
|
||||
connection.Conn = amqpConnMock
|
||||
|
||||
assert.True(t, connection.IsClosed())
|
||||
})
|
||||
|
||||
t.Run("is not closed", func(t *testing.T) {
|
||||
channel := make(chan struct{})
|
||||
amqpConnMock := &amqpConnMock{}
|
||||
amqpConnMock.On("Done").Return(channelReceiver(channel))
|
||||
connection.Conn = amqpConnMock
|
||||
|
||||
assert.False(t, connection.IsClosed())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnection_Close(t *testing.T) {
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
}
|
||||
|
||||
t.Run("already closed", func(t *testing.T) {
|
||||
assert.NoError(t, connection.Close())
|
||||
})
|
||||
|
||||
t.Run("close error", func(t *testing.T) {
|
||||
err := errors.New("test error")
|
||||
|
||||
amqpConnMock := &amqpConnMock{}
|
||||
amqpConnMock.On("Close").Return(err)
|
||||
connection.Conn = amqpConnMock
|
||||
|
||||
assert.EqualError(t, connection.Close(), "internal close: connection close: test error")
|
||||
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
|
||||
|
||||
assert.Nil(t, connection.Close())
|
||||
assert.Nil(t, connection.Conn)
|
||||
amqpConnMock.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnection_Connect(t *testing.T) {
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
}
|
||||
|
||||
t.Run("already connected", func(t *testing.T) {
|
||||
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"
|
||||
|
||||
amqpDialMock := &amqpDialMock{}
|
||||
var c *amqpConnMock = nil
|
||||
amqpDialMock.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(c, errors.New("test error"))
|
||||
connection.Dialer = amqpDialMock
|
||||
|
||||
assert.EqualError(t, connection.Connect(), "internal connect: dial: test error")
|
||||
assert.Nil(t, connection.Conn)
|
||||
})
|
||||
|
||||
t.Run("connect without error", func(t *testing.T) {
|
||||
connection.Conn = nil
|
||||
|
||||
amqpDialMock := &amqpDialMock{}
|
||||
amqpConn := &amqpConnMock{}
|
||||
amqpDialMock.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(amqpConn, nil)
|
||||
connection.Dialer = amqpDialMock
|
||||
|
||||
assert.NoError(t, connection.Connect())
|
||||
assert.Equal(t, amqpConn, connection.Conn)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnection_NewSender(t *testing.T) {
|
||||
connection := &AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
}
|
||||
|
||||
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
||||
return channel
|
||||
}
|
||||
|
||||
t.Run("connection not initialized", func(t *testing.T) {
|
||||
sender, err := connection.NewSender(context.Background(), "topic")
|
||||
assert.EqualError(t, err, "connection is not initialized")
|
||||
assert.Nil(t, sender)
|
||||
})
|
||||
|
||||
t.Run("connection is closed", func(t *testing.T) {
|
||||
channel := make(chan struct{})
|
||||
close(channel)
|
||||
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Done").Return(channelReceiver(channel))
|
||||
connection.Conn = conn
|
||||
|
||||
sender, err := connection.NewSender(context.Background(), "topic")
|
||||
assert.EqualError(t, err, "amqp connection is closed")
|
||||
assert.Nil(t, sender)
|
||||
})
|
||||
|
||||
t.Run("session error", func(t *testing.T) {
|
||||
channel := make(chan struct{})
|
||||
|
||||
var session *amqpSessionMock = nil
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("NewSession", mock.Anything, mock.Anything).Return(session, errors.New("test error"))
|
||||
conn.On("Done").Return(channelReceiver(channel))
|
||||
connection.Conn = conn
|
||||
|
||||
sender, err := connection.NewSender(context.Background(), "topic")
|
||||
assert.EqualError(t, err, "new session: test error")
|
||||
assert.Nil(t, sender)
|
||||
})
|
||||
|
||||
t.Run("sender error", func(t *testing.T) {
|
||||
channel := make(chan struct{})
|
||||
|
||||
sessionMock := &amqpSessionMock{}
|
||||
var amqpSender *amqp.Sender = nil
|
||||
sessionMock.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(amqpSender, errors.New("test error"))
|
||||
sessionMock.On("Close", mock.Anything).Return(nil)
|
||||
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Done").Return(channelReceiver(channel))
|
||||
conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil)
|
||||
connection.Conn = conn
|
||||
|
||||
sender, err := connection.NewSender(context.Background(), "topic")
|
||||
assert.EqualError(t, err, "new internal sender: test error")
|
||||
assert.Nil(t, sender)
|
||||
})
|
||||
|
||||
t.Run("session close error", func(t *testing.T) {
|
||||
channel := make(chan struct{})
|
||||
|
||||
sessionMock := &amqpSessionMock{}
|
||||
var amqpSender *amqp.Sender = nil
|
||||
sessionMock.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(amqpSender, errors.New("test error"))
|
||||
sessionMock.On("Close", mock.Anything).Return(errors.New("close error"))
|
||||
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Done").Return(channelReceiver(channel))
|
||||
conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil)
|
||||
connection.Conn = conn
|
||||
|
||||
sender, err := connection.NewSender(context.Background(), "topic")
|
||||
assert.EqualError(t, err, "new internal sender: test error\nclose session: close error")
|
||||
assert.Nil(t, sender)
|
||||
})
|
||||
|
||||
t.Run("get sender", func(t *testing.T) {
|
||||
channel := make(chan struct{})
|
||||
|
||||
amqpSender := &amqp.Sender{}
|
||||
sessionMock := &amqpSessionMock{}
|
||||
sessionMock.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(amqpSender, nil)
|
||||
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Done").Return(channelReceiver(channel))
|
||||
conn.On("NewSession", mock.Anything, mock.Anything).Return(sessionMock, nil)
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpConnection_NewAmqpConnection(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
func Test_As(t *testing.T) {
|
||||
|
||||
t.Run("error", func(t *testing.T) {
|
||||
value, err := As[amqp.Message](nil, errors.New("test error"))
|
||||
assert.EqualError(t, err, "test error")
|
||||
assert.Nil(t, value)
|
||||
})
|
||||
|
||||
t.Run("value nil", func(t *testing.T) {
|
||||
value, err := As[amqp.Message](nil, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, value)
|
||||
})
|
||||
|
||||
t.Run("value not not type", func(t *testing.T) {
|
||||
value, err := As[amqp.Message](struct{}{}, nil)
|
||||
assert.EqualError(t, err, "could not cast value: struct {}")
|
||||
assert.Nil(t, value)
|
||||
})
|
||||
|
||||
t.Run("cast", func(t *testing.T) {
|
||||
var sessionAny any = &amqpSessionMock{}
|
||||
value, err := As[amqpSessionMock](sessionAny, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, value)
|
||||
})
|
||||
}
|
||||
|
|
@ -4,28 +4,22 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Azure/go-amqp"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-amqp"
|
||||
)
|
||||
|
||||
type AmqpSenderSession struct {
|
||||
session *amqp.Session
|
||||
sender *amqp.Sender
|
||||
const amqpTopicPrefix = "topic://"
|
||||
|
||||
type AmqpSender interface {
|
||||
Send(ctx context.Context, msg *amqp.Message, opts *amqp.SendOptions) error
|
||||
Close(ctx context.Context) error
|
||||
}
|
||||
|
||||
func (c *AmqpConnection) NewSenderSession(source string) (*AmqpSenderSession, error) {
|
||||
if c.IsClosed() {
|
||||
return nil, ConnectionClosedError
|
||||
}
|
||||
|
||||
return As[AmqpSenderSession](c.ResetConnectionAndRetryIfErrorWithReturnValue("newSenderSession", func(ctx context.Context) (any, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
return c.NewSender(ctx, source)
|
||||
}))
|
||||
type AmqpSenderSession struct {
|
||||
Session AmqpSession
|
||||
Sender AmqpSender
|
||||
}
|
||||
|
||||
func (s *AmqpSenderSession) Send(
|
||||
|
|
@ -35,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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -63,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 {
|
||||
|
|
@ -71,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)
|
||||
}
|
||||
|
|
@ -85,10 +79,3 @@ func (s *AmqpSenderSession) Close() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AmqpSenderSession) CloseSilently() {
|
||||
err := s.Close()
|
||||
if err != nil {
|
||||
slog.Error("error closing sender session", slog.Any("err", err))
|
||||
}
|
||||
}
|
||||
187
internal/messaging/amqp_sender_session_test.go
Normal file
187
internal/messaging/amqp_sender_session_test.go
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
package messaging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-amqp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
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 _ AmqpSender = (*amqpSenderMock)(nil)
|
||||
|
||||
func Test_AmqpSenderSession_Close(t *testing.T) {
|
||||
|
||||
t.Run("close without errors", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Close", mock.Anything).Return(nil)
|
||||
session := &amqpSessionMock{}
|
||||
session.On("Close", mock.Anything).Return(nil)
|
||||
|
||||
senderSession := &AmqpSenderSession{
|
||||
Sender: sender,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
err := senderSession.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Close", 1)
|
||||
session.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
|
||||
t.Run("close with sender error", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Close", mock.Anything).Return(errors.New("sender error"))
|
||||
session := &amqpSessionMock{}
|
||||
session.On("Close", mock.Anything).Return(nil)
|
||||
|
||||
senderSession := &AmqpSenderSession{
|
||||
Sender: sender,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
err := senderSession.Close()
|
||||
assert.EqualError(t, err, "sender error")
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Close", 1)
|
||||
session.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
|
||||
t.Run("close with session error", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Close", mock.Anything).Return(nil)
|
||||
session := &amqpSessionMock{}
|
||||
session.On("Close", mock.Anything).Return(errors.New("session error"))
|
||||
|
||||
senderSession := &AmqpSenderSession{
|
||||
Sender: sender,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
err := senderSession.Close()
|
||||
assert.EqualError(t, err, "session error")
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Close", 1)
|
||||
session.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
|
||||
t.Run("close with sender and session error", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Close", mock.Anything).Return(errors.New("sender error"))
|
||||
session := &amqpSessionMock{}
|
||||
session.On("Close", mock.Anything).Return(errors.New("session error"))
|
||||
|
||||
senderSession := &AmqpSenderSession{
|
||||
Sender: sender,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
err := senderSession.Close()
|
||||
assert.EqualError(t, err, "sender error\nsession error")
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Close", 1)
|
||||
session.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpSenderSession_Send(t *testing.T) {
|
||||
|
||||
t.Run("invalid topic name", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
session := &amqpSessionMock{}
|
||||
|
||||
senderSession := &AmqpSenderSession{
|
||||
Sender: sender,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
data := [][]byte{[]byte("data")}
|
||||
err := senderSession.Send("invalid", data, "application/json", map[string]interface{}{})
|
||||
assert.EqualError(t, err, "topic \"invalid\" name lacks mandatory prefix \"topic://\"")
|
||||
})
|
||||
|
||||
t.Run("content type missing", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
session := &amqpSessionMock{}
|
||||
|
||||
senderSession := &AmqpSenderSession{
|
||||
Sender: sender,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
data := [][]byte{[]byte("data")}
|
||||
err := senderSession.Send("topic://some/name", data, "", map[string]interface{}{})
|
||||
assert.EqualError(t, err, "content-type is required")
|
||||
})
|
||||
|
||||
t.Run("send", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Send", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
session := &amqpSessionMock{}
|
||||
|
||||
senderSession := &AmqpSenderSession{
|
||||
Sender: sender,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
data := [][]byte{[]byte("data")}
|
||||
applicationProperties := map[string]interface{}{}
|
||||
applicationProperties["key"] = "value"
|
||||
err := senderSession.Send("topic://some/name", data, "application/json", applicationProperties)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Send", 1)
|
||||
calls := sender.Calls
|
||||
assert.Equal(t, 1, len(calls))
|
||||
|
||||
ctx, isCtx := calls[0].Arguments[0].(context.Context)
|
||||
assert.True(t, isCtx)
|
||||
assert.NotNil(t, ctx)
|
||||
|
||||
message, isMsg := calls[0].Arguments[1].(*amqp.Message)
|
||||
assert.True(t, isMsg)
|
||||
assert.True(t, message.Header.Durable)
|
||||
assert.Equal(t, "topic://some/name", *message.Properties.To)
|
||||
assert.Equal(t, "application/json", *message.Properties.ContentType)
|
||||
assert.Equal(t, applicationProperties, message.ApplicationProperties)
|
||||
assert.Equal(t, data, message.Data)
|
||||
|
||||
senderOptions, isSenderOptions := calls[0].Arguments[2].(*amqp.SendOptions)
|
||||
assert.True(t, isSenderOptions)
|
||||
assert.Nil(t, senderOptions)
|
||||
})
|
||||
|
||||
t.Run("send fails", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Send", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("send fail"))
|
||||
session := &amqpSessionMock{}
|
||||
|
||||
senderSession := &AmqpSenderSession{
|
||||
Sender: sender,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
data := [][]byte{[]byte("data")}
|
||||
applicationProperties := map[string]interface{}{}
|
||||
applicationProperties["key"] = "value"
|
||||
|
||||
err := senderSession.Send("topic://some/name", data, "application/json", applicationProperties)
|
||||
assert.EqualError(t, err, "send fail")
|
||||
sender.AssertNumberOfCalls(t, "Send", 1)
|
||||
})
|
||||
}
|
||||
|
|
@ -2,31 +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"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"fmt"
|
||||
"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
|
||||
}
|
||||
|
||||
|
|
@ -34,33 +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")
|
||||
}
|
||||
var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: 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 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"),
|
||||
|
|
@ -75,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)
|
||||
|
|
@ -92,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
|
||||
|
|
@ -116,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,
|
||||
|
|
@ -141,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)
|
||||
|
|
@ -135,17 +137,23 @@ func (a *DynamicLegacyAuditApi) Send(
|
|||
return ErrNoTopicNameProvided
|
||||
}
|
||||
topicName := fmt.Sprintf("%s", rawTopicName)
|
||||
if len(topicName) == 0 {
|
||||
if topicName == "" {
|
||||
return ErrTopicNameEmpty
|
||||
}
|
||||
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)
|
||||
|
|
@ -41,7 +45,7 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
validator, err := protovalidate.New()
|
||||
assert.NoError(t, err)
|
||||
|
||||
topicSubscriptionTopicPattern := "audit-log/>"
|
||||
topicSubscriptionTopicPattern := "stackit-platform/t/swz/audit-log/>"
|
||||
|
||||
// Check that event-type data-access is rejected as it is currently
|
||||
// not supported by downstream services
|
||||
|
|
@ -53,29 +57,29 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-rejected"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/organization-rejected"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.ErrorIs(t, auditApi.Log(
|
||||
ctx,
|
||||
ctxWithTopic,
|
||||
event,
|
||||
visibility,
|
||||
NewRoutableIdentifier(objectIdentifier),
|
||||
), ErrUnsupportedEventTypeDataAccess)
|
||||
pkgAuditCommon.NewRoutableIdentifier(objectIdentifier),
|
||||
), pkgAuditCommon.ErrUnsupportedEventTypeDataAccess)
|
||||
})
|
||||
|
||||
// Check logging of organization events
|
||||
|
|
@ -87,27 +91,27 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/organization-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t, auditApi.Log(
|
||||
ctx,
|
||||
ctxWithTopic,
|
||||
event,
|
||||
visibility,
|
||||
NewRoutableIdentifier(objectIdentifier),
|
||||
pkgAuditCommon.NewRoutableIdentifier(objectIdentifier),
|
||||
))
|
||||
|
||||
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
|
||||
|
|
@ -124,27 +128,27 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/organization-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t, auditApi.Log(
|
||||
ctx,
|
||||
ctxWithTopic,
|
||||
event,
|
||||
visibility,
|
||||
NewRoutableIdentifier(objectIdentifier),
|
||||
pkgAuditCommon.NewRoutableIdentifier(objectIdentifier),
|
||||
))
|
||||
|
||||
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
|
||||
|
|
@ -162,27 +166,27 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/folder-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/folder-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t, auditApi.Log(
|
||||
ctx,
|
||||
ctxWithTopic,
|
||||
event,
|
||||
visibility,
|
||||
NewRoutableIdentifier(objectIdentifier),
|
||||
pkgAuditCommon.NewRoutableIdentifier(objectIdentifier),
|
||||
))
|
||||
|
||||
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
|
||||
|
|
@ -199,27 +203,27 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/folder-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/folder-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t, auditApi.Log(
|
||||
ctx,
|
||||
ctxWithTopic,
|
||||
event,
|
||||
visibility,
|
||||
NewRoutableIdentifier(objectIdentifier),
|
||||
pkgAuditCommon.NewRoutableIdentifier(objectIdentifier),
|
||||
))
|
||||
|
||||
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
|
||||
|
|
@ -237,27 +241,27 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/project-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/project-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t, auditApi.Log(
|
||||
ctx,
|
||||
ctxWithTopic,
|
||||
event,
|
||||
visibility,
|
||||
NewRoutableIdentifier(objectIdentifier),
|
||||
pkgAuditCommon.NewRoutableIdentifier(objectIdentifier),
|
||||
))
|
||||
|
||||
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
|
||||
|
|
@ -274,27 +278,27 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/project-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/project-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t, auditApi.Log(
|
||||
ctx,
|
||||
ctxWithTopic,
|
||||
event,
|
||||
visibility,
|
||||
NewRoutableIdentifier(objectIdentifier),
|
||||
pkgAuditCommon.NewRoutableIdentifier(objectIdentifier),
|
||||
))
|
||||
|
||||
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
|
||||
|
|
@ -311,28 +315,28 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/project-system-changed"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/project-system-changed"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t,
|
||||
auditApi.Log(
|
||||
ctx,
|
||||
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)
|
||||
|
|
@ -367,28 +371,28 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/system-changed"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/system-changed"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t,
|
||||
auditApi.Log(
|
||||
ctx,
|
||||
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)
|
||||
|
|
@ -422,29 +426,29 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/organization-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
|
||||
// Log the event to solace
|
||||
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
|
||||
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
ctxWithTopic := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||
assert.NoError(t, auditApi.Log(
|
||||
ctx,
|
||||
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)
|
||||
|
|
@ -42,7 +46,7 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
validator, err := protovalidate.New()
|
||||
assert.NoError(t, err)
|
||||
|
||||
topicSubscriptionTopicPattern := "audit-log/>"
|
||||
topicSubscriptionTopicPattern := "stackit-platform/t/swz/audit-log/>"
|
||||
|
||||
// Check that event-type data-access is rejected as it is currently
|
||||
// not supported by downstream services
|
||||
|
|
@ -54,28 +58,28 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-rejected"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/organization-rejected"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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
|
||||
|
|
@ -87,19 +91,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/organization-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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)
|
||||
|
|
@ -124,19 +128,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/organization-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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)
|
||||
|
|
@ -162,19 +166,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/folder-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/folder-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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)
|
||||
|
|
@ -199,19 +203,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/folder-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/folder-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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)
|
||||
|
|
@ -237,19 +241,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/project-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/project-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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)
|
||||
|
|
@ -274,19 +278,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/project-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/project-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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)
|
||||
|
|
@ -311,19 +315,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/project-system-changed"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/project-system-changed"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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)
|
||||
|
|
@ -367,19 +371,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/system-changed"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/system-changed"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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)
|
||||
|
|
@ -422,19 +426,19 @@ func TestLegacyAuditApi(t *testing.T) {
|
|||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
|
||||
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
|
||||
topicName := "topic://stackit-platform/t/swz/audit-log/eu01/v1/resource-manager/organization-created"
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
// 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) {
|
||||
event, objectIdentifier := newOrganizationAuditEvent(nil)
|
||||
event.LogName = strings.Replace(event.LogName, string(EventTypeAdminActivity), string(EventTypeDataAccess), 1)
|
||||
routableObjectIdentifier := NewRoutableIdentifier(objectIdentifier)
|
||||
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(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routableObjectIdentifier),
|
||||
ErrUnsupportedEventTypeDataAccess)
|
||||
context.Background(), orgEvent, auditV1.Visibility_VISIBILITY_PUBLIC, rtIdentifier),
|
||||
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 {
|
||||
|
|
@ -41,11 +43,11 @@ func ToBase64(
|
|||
}
|
||||
|
||||
base64Str := base64.StdEncoding.EncodeToString(serializedEvent)
|
||||
base64Str = base64Str + base64AuditEventV1
|
||||
base64Str += base64AuditEventV1
|
||||
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,16 +151,25 @@ 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
|
||||
}
|
||||
|
||||
// GetApiRequest returns the api request details
|
||||
func (builder *AuditLogEntryBuilder) GetApiRequest() *pkgAuditCommon.ApiRequest {
|
||||
return builder.auditRequest.Request
|
||||
}
|
||||
|
||||
func (builder *AuditLogEntryBuilder) GetApiRequestBody() *pkgAuditCommon.ApiRequest {
|
||||
return builder.auditRequest.Request
|
||||
}
|
||||
|
||||
// WithRequiredLocation adds the region and optional zone id. If both, separated with a - (dash).
|
||||
// Example: eu01
|
||||
func (builder *AuditLogEntryBuilder) WithRequiredLocation(location string) *AuditLogEntryBuilder {
|
||||
|
|
@ -209,7 +222,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 +279,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
|
||||
}
|
||||
|
|
@ -296,7 +309,7 @@ func (builder *AuditLogEntryBuilder) WithResponseBody(responseBody any) *AuditLo
|
|||
}
|
||||
|
||||
// WithResponseBodyBytes adds the response body as bytes (serialized json or protobuf message expected)
|
||||
func (builder *AuditLogEntryBuilder) WithResponseBodyBytes(responseBody *[]byte) *AuditLogEntryBuilder {
|
||||
func (builder *AuditLogEntryBuilder) WithResponseBodyBytes(responseBody []byte) *AuditLogEntryBuilder {
|
||||
builder.auditResponse.ResponseBodyBytes = responseBody
|
||||
return builder
|
||||
}
|
||||
|
|
@ -346,26 +359,26 @@ 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
|
||||
|
||||
var details *map[string]interface{} = nil
|
||||
var details map[string]interface{}
|
||||
if len(builder.auditParams.Details) > 0 {
|
||||
details = &builder.auditParams.Details
|
||||
details = builder.auditParams.Details
|
||||
}
|
||||
|
||||
// Instantiate the audit event
|
||||
return NewAuditLogEntry(
|
||||
return internalAuditApi.NewAuditLogEntry(
|
||||
builder.auditRequest,
|
||||
builder.auditResponse,
|
||||
details,
|
||||
|
|
@ -378,7 +391,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 +400,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 +413,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,11 +463,16 @@ 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
|
||||
}
|
||||
|
||||
// GetApiRequest returns the api request details
|
||||
func (builder *AuditEventBuilder) GetApiRequest() *pkgAuditCommon.ApiRequest {
|
||||
return builder.auditLogEntryBuilder.GetApiRequest()
|
||||
}
|
||||
|
||||
// WithRequiredRequestClientIp adds the client ip
|
||||
func (builder *AuditEventBuilder) WithRequiredRequestClientIp(requestClientIp string) *AuditEventBuilder {
|
||||
builder.auditLogEntryBuilder.WithRequiredRequestClientIp(requestClientIp)
|
||||
|
|
@ -488,7 +506,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 +563,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
|
||||
}
|
||||
|
|
@ -575,7 +593,7 @@ func (builder *AuditEventBuilder) WithResponseBody(responseBody any) *AuditEvent
|
|||
}
|
||||
|
||||
// WithResponseBodyBytes adds the response body as bytes (serialized json or protobuf message expected)
|
||||
func (builder *AuditEventBuilder) WithResponseBodyBytes(responseBody *[]byte) *AuditEventBuilder {
|
||||
func (builder *AuditEventBuilder) WithResponseBodyBytes(responseBody []byte) *AuditEventBuilder {
|
||||
builder.auditLogEntryBuilder.WithResponseBodyBytes(responseBody)
|
||||
return builder
|
||||
}
|
||||
|
|
@ -621,7 +639,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 +650,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)
|
||||
|
|
@ -86,23 +90,23 @@ func Test_AuditLogEntryBuilder(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
err = validator.Validate(logEntry)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "validation error:\n - proto_payload.service_name: value is required [required]\n - proto_payload.operation_name: value is required [required]\n - proto_payload.request_metadata.caller_supplied_user_agent: value is required [required]\n - proto_payload.request_metadata.request_attributes.method: value is required [required]\n - proto_payload.request_metadata.request_attributes.headers: value is required [required]\n - proto_payload.request_metadata.request_attributes.path: value is required [required]\n - proto_payload.request_metadata.request_attributes.host: value is required [required]\n - proto_payload.request_metadata.request_attributes.scheme: value is required [required]\n - proto_payload.request_metadata.request_attributes.protocol: value is required [required]\n - insert_id: value does not match regex pattern `^[0-9]+/[a-z0-9-]+/[a-z0-9-]+/[0-9]+$` [string.pattern]", err.Error())
|
||||
assert.Equal(t, "validation errors:\n - proto_payload.service_name: value is required\n - proto_payload.operation_name: value is required\n - proto_payload.request_metadata.caller_supplied_user_agent: value is required\n - proto_payload.request_metadata.request_attributes.method: value is required\n - proto_payload.request_metadata.request_attributes.headers: value is required\n - proto_payload.request_metadata.request_attributes.path: value is required\n - proto_payload.request_metadata.request_attributes.host: value is required\n - proto_payload.request_metadata.request_attributes.scheme: value is required\n - proto_payload.request_metadata.request_attributes.protocol: value is required\n - insert_id: value does not match regex pattern `^[0-9]+/[a-z0-9-]+/[a-z0-9-]+/[0-9]+$`", err.Error())
|
||||
})
|
||||
|
||||
t.Run("required only", func(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,
|
||||
},
|
||||
|
|
@ -126,7 +130,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) {
|
|||
|
||||
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
|
|
@ -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").
|
||||
|
|
@ -242,7 +246,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) {
|
|||
|
||||
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
|
|
@ -306,11 +310,105 @@ func Test_AuditLogEntryBuilder(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("with service account token", func(t *testing.T) {
|
||||
builder := NewAuditLogEntryBuilder().
|
||||
WithRequiredLocation("eu01").
|
||||
WithRequiredObjectId("1").
|
||||
WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject).
|
||||
WithRequiredOperation("stackit.demo-service.v1.operation").
|
||||
WithRequiredApiRequest(pkgAuditCommon.ApiRequest{
|
||||
Body: nil,
|
||||
Header: internalAuditApi.TestHeadersSa,
|
||||
Host: "localhost",
|
||||
Method: "POST",
|
||||
Scheme: "https",
|
||||
Proto: "HTTP/1.1",
|
||||
URL: pkgAuditCommon.RequestUrl{
|
||||
Path: "/",
|
||||
RawQuery: nil,
|
||||
},
|
||||
}).
|
||||
WithRequiredRequestClientIp("127.0.0.1").
|
||||
WithRequiredServiceName("demo-service").
|
||||
WithRequiredWorkerId("worker-id")
|
||||
|
||||
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, logEntry)
|
||||
|
||||
assert.Equal(t, "projects/1/logs/admin-activity", logEntry.LogName)
|
||||
assert.Nil(t, logEntry.Labels)
|
||||
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_DEFAULT, logEntry.Severity)
|
||||
assert.NotNil(t, logEntry.Timestamp)
|
||||
assert.Nil(t, logEntry.CorrelationId)
|
||||
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
|
||||
|
||||
assert.NotNil(t, logEntry.ProtoPayload)
|
||||
|
||||
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "10f38b01_534b_47bb_a03a_e294ca2be4de", authenticationInfo.PrincipalId)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
assert.Equal(t, "projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/10f38b01-534b-47bb-a03a-e294ca2be4de", *authenticationInfo.ServiceAccountName)
|
||||
|
||||
assert.Nil(t, logEntry.ProtoPayload.AuthorizationInfo)
|
||||
assert.Nil(t, logEntry.ProtoPayload.Metadata)
|
||||
assert.Equal(t, "stackit.demo-service.v1.operation", logEntry.ProtoPayload.OperationName)
|
||||
assert.Nil(t, logEntry.ProtoPayload.Request)
|
||||
|
||||
requestMetadata := logEntry.ProtoPayload.RequestMetadata
|
||||
assert.NotNil(t, requestMetadata)
|
||||
assert.Equal(t, "127.0.0.1", requestMetadata.CallerIp)
|
||||
assert.Equal(t, "custom", requestMetadata.CallerSuppliedUserAgent)
|
||||
|
||||
requestAttributes := requestMetadata.RequestAttributes
|
||||
assert.NotNil(t, requestAttributes)
|
||||
assert.Equal(t, "/", requestAttributes.Path)
|
||||
assert.NotNil(t, requestAttributes.Time)
|
||||
assert.Equal(t, "localhost", requestAttributes.Host)
|
||||
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_POST, requestAttributes.Method)
|
||||
assert.Nil(t, requestAttributes.Id)
|
||||
assert.Equal(t, "https", requestAttributes.Scheme)
|
||||
assert.Equal(t, map[string]string{"user-agent": "custom"}, requestAttributes.Headers)
|
||||
assert.Nil(t, requestAttributes.Query)
|
||||
assert.Equal(t, "HTTP/1.1", requestAttributes.Protocol)
|
||||
|
||||
requestAttributesAuth := requestAttributes.Auth
|
||||
assert.NotNil(t, requestAttributesAuth)
|
||||
assert.Equal(t, "10f38b01_534b_47bb_a03a_e294ca2be4de/stackit%2Fserviceaccount", requestAttributesAuth.Principal)
|
||||
assert.Equal(t, []string{"stackit", "api"}, requestAttributesAuth.Audiences)
|
||||
assert.NotNil(t, requestAttributesAuth.Claims)
|
||||
|
||||
assert.Equal(t, "projects/1", logEntry.ProtoPayload.ResourceName)
|
||||
assert.Nil(t, logEntry.ProtoPayload.Response)
|
||||
|
||||
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
|
||||
assert.NotNil(t, responseMetadata)
|
||||
assert.Nil(t, responseMetadata.ErrorDetails)
|
||||
assert.Nil(t, responseMetadata.ErrorMessage)
|
||||
assert.Equal(t, wrapperspb.Int32(200), responseMetadata.StatusCode)
|
||||
|
||||
responseAttributes := responseMetadata.ResponseAttributes
|
||||
assert.NotNil(t, responseAttributes)
|
||||
assert.Nil(t, responseAttributes.Headers)
|
||||
assert.Nil(t, responseAttributes.NumResponseItems)
|
||||
assert.Nil(t, responseAttributes.Size)
|
||||
assert.NotNil(t, responseAttributes.Time)
|
||||
|
||||
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
|
||||
|
||||
validator, err := protovalidate.New()
|
||||
assert.NoError(t, err)
|
||||
err = validator.Validate(logEntry)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("system event", func(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 +429,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) {
|
|||
|
||||
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail)
|
||||
assert.Nil(t, authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "none", authenticationInfo.PrincipalId)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
|
|
@ -399,16 +497,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 +517,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 +549,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 +578,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").
|
||||
|
|
@ -505,13 +603,141 @@ func Test_AuditLogEntryBuilder(t *testing.T) {
|
|||
assert.EqualError(t, err, "json: cannot unmarshal string into Go value of type map[string]interface {}\ninvalid response")
|
||||
assert.Nil(t, logEntry)
|
||||
})
|
||||
|
||||
t.Run("get api request", func(t *testing.T) {
|
||||
requestBody := map[string]interface{}{"key": "response"}
|
||||
requestBodyBytes, err := ResponseBodyToBytes(requestBody)
|
||||
assert.NoError(t, err)
|
||||
builder := NewAuditLogEntryBuilder().
|
||||
WithRequiredApiRequest(pkgAuditCommon.ApiRequest{
|
||||
Body: requestBodyBytes,
|
||||
Header: internalAuditApi.TestHeaders,
|
||||
Host: "localhost",
|
||||
Method: "POST",
|
||||
Scheme: "https",
|
||||
Proto: "HTTP/1.1",
|
||||
URL: pkgAuditCommon.RequestUrl{
|
||||
Path: "/",
|
||||
RawQuery: nil,
|
||||
},
|
||||
}).
|
||||
WithRequiredLocation("eu01").
|
||||
WithRequiredObjectId("1").
|
||||
WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject).
|
||||
WithRequiredOperation("stackit.demo-service.v1.operation").
|
||||
WithRequiredRequestClientIp("127.0.0.1").
|
||||
WithRequiredServiceName("demo-service").
|
||||
WithRequiredWorkerId("worker-id")
|
||||
|
||||
// get the request before building the auditlog entry
|
||||
apiRequest := builder.GetApiRequest()
|
||||
assert.NotNil(t, apiRequest)
|
||||
assert.Equal(t, requestBodyBytes, apiRequest.Body)
|
||||
|
||||
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, logEntry)
|
||||
|
||||
// get the request after building the auditlog entry
|
||||
apiRequest = builder.GetApiRequest()
|
||||
assert.NotNil(t, apiRequest)
|
||||
assert.Equal(t, requestBodyBytes, apiRequest.Body)
|
||||
})
|
||||
|
||||
t.Run("get invalid api request", func(t *testing.T) {
|
||||
requestBodyBytes := []byte("invalid")
|
||||
builder := NewAuditLogEntryBuilder().
|
||||
WithRequiredApiRequest(pkgAuditCommon.ApiRequest{
|
||||
Body: requestBodyBytes,
|
||||
Header: internalAuditApi.TestHeaders,
|
||||
Host: "localhost",
|
||||
Method: "POST",
|
||||
Scheme: "https",
|
||||
Proto: "HTTP/1.1",
|
||||
URL: pkgAuditCommon.RequestUrl{
|
||||
Path: "/",
|
||||
RawQuery: nil,
|
||||
},
|
||||
}).
|
||||
WithRequiredLocation("eu01").
|
||||
WithRequiredObjectId("1").
|
||||
WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject).
|
||||
WithRequiredOperation("stackit.demo-service.v1.operation").
|
||||
WithRequiredRequestClientIp("127.0.0.1").
|
||||
WithRequiredServiceName("demo-service").
|
||||
WithRequiredWorkerId("worker-id")
|
||||
|
||||
// get the request before building the auditlog entry
|
||||
apiRequest := builder.GetApiRequest()
|
||||
assert.NotNil(t, apiRequest)
|
||||
assert.Equal(t, requestBodyBytes, apiRequest.Body)
|
||||
|
||||
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
|
||||
|
||||
assert.EqualError(t, err, "invalid character 'i' looking for beginning of value\ninvalid request body")
|
||||
assert.Nil(t, logEntry)
|
||||
|
||||
// get the request after building the auditlog entry
|
||||
apiRequest = builder.GetApiRequest()
|
||||
assert.NotNil(t, apiRequest)
|
||||
assert.Equal(t, requestBodyBytes, apiRequest.Body)
|
||||
})
|
||||
|
||||
t.Run("modify request body", func(t *testing.T) {
|
||||
requestBodyBytes := []byte("{\"key\":\"value\"}")
|
||||
builder := NewAuditLogEntryBuilder().
|
||||
WithRequiredApiRequest(pkgAuditCommon.ApiRequest{
|
||||
Body: requestBodyBytes,
|
||||
Header: internalAuditApi.TestHeaders,
|
||||
Host: "localhost",
|
||||
Method: "POST",
|
||||
Scheme: "https",
|
||||
Proto: "HTTP/1.1",
|
||||
URL: pkgAuditCommon.RequestUrl{
|
||||
Path: "/",
|
||||
RawQuery: nil,
|
||||
},
|
||||
}).
|
||||
WithRequiredLocation("eu01").
|
||||
WithRequiredObjectId("1").
|
||||
WithRequiredObjectType(pkgAuditCommon.ObjectTypeProject).
|
||||
WithRequiredOperation("stackit.demo-service.v1.operation").
|
||||
WithRequiredRequestClientIp("127.0.0.1").
|
||||
WithRequiredServiceName("demo-service").
|
||||
WithRequiredWorkerId("worker-id")
|
||||
|
||||
// get the request before building the auditlog entry
|
||||
apiRequest := builder.GetApiRequest()
|
||||
assert.NotNil(t, apiRequest)
|
||||
assert.Equal(t, requestBodyBytes, apiRequest.Body)
|
||||
|
||||
// update the request body
|
||||
updatedBodyBytes := []byte("{\"key\":\"updated\"}")
|
||||
apiRequest.Body = updatedBodyBytes
|
||||
|
||||
// build the audit log entry
|
||||
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, logEntry)
|
||||
|
||||
// check the request body from the serialized event
|
||||
requestBodyJson, err := logEntry.ProtoPayload.Request.MarshalJSON()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, updatedBodyBytes, requestBodyJson)
|
||||
|
||||
// check the request after building the auditlog entry
|
||||
apiRequest = builder.GetApiRequest()
|
||||
assert.NotNil(t, apiRequest)
|
||||
assert.Equal(t, updatedBodyBytes, apiRequest.Body)
|
||||
})
|
||||
}
|
||||
|
||||
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,44 +750,44 @@ 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)
|
||||
assert.Equal(t, "validation error:\n - log_name: value does not match regex pattern `^[a-z-]+/[a-z0-9-]+/logs/(?:admin-activity|system-event|policy-denied|data-access)$` [string.pattern]\n - proto_payload.operation_name: value is required [required]\n - proto_payload.resource_name: value does not match regex pattern `^[a-z]+/[a-z0-9-]+(?:/[a-z0-9-]+/[a-z0-9-_]+)*$` [string.pattern]\n - proto_payload.request_metadata.caller_supplied_user_agent: value is required [required]\n - proto_payload.request_metadata.request_attributes.method: value is required [required]\n - proto_payload.request_metadata.request_attributes.headers: value is required [required]\n - proto_payload.request_metadata.request_attributes.path: value is required [required]\n - proto_payload.request_metadata.request_attributes.host: value is required [required]\n - proto_payload.request_metadata.request_attributes.scheme: value is required [required]\n - proto_payload.request_metadata.request_attributes.protocol: value is required [required]", err.Error())
|
||||
assert.Equal(t, "validation errors:\n - log_name: value does not match regex pattern `^[a-z-]+/[a-z0-9-]+/logs/(?:admin-activity|system-event|policy-denied|data-access)$`\n - proto_payload.operation_name: value is required\n - proto_payload.resource_name: value does not match regex pattern `^[a-z]+/[a-z0-9-]+(?:/[a-z0-9-]+/[a-z0-9-_]+)*$`\n - proto_payload.request_metadata.caller_supplied_user_agent: value is required\n - proto_payload.request_metadata.request_attributes.method: value is required\n - proto_payload.request_metadata.request_attributes.headers: value is required\n - proto_payload.request_metadata.request_attributes.path: value is required\n - proto_payload.request_metadata.request_attributes.host: value is required\n - proto_payload.request_metadata.request_attributes.scheme: value is required\n - proto_payload.request_metadata.request_attributes.protocol: value is required", err.Error())
|
||||
assert.Nil(t, cloudEvent)
|
||||
assert.Nil(t, routingIdentifier)
|
||||
})
|
||||
|
||||
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)
|
||||
|
|
@ -604,7 +830,7 @@ func Test_AuditEventBuilder(t *testing.T) {
|
|||
|
||||
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
|
|
@ -663,7 +889,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 +903,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 +921,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 +934,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)
|
||||
|
|
@ -751,7 +977,7 @@ func Test_AuditEventBuilder(t *testing.T) {
|
|||
|
||||
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
|
|
@ -817,13 +1043,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 +1057,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 +1075,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 +1095,7 @@ func Test_AuditEventBuilder(t *testing.T) {
|
|||
|
||||
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail)
|
||||
assert.Nil(t, authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "none", authenticationInfo.PrincipalId)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
|
|
@ -929,7 +1155,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 +1166,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 +1184,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 +1204,7 @@ func Test_AuditEventBuilder(t *testing.T) {
|
|||
|
||||
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||
assert.NotNil(t, authenticationInfo)
|
||||
assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail)
|
||||
assert.Nil(t, authenticationInfo.PrincipalEmail)
|
||||
assert.Equal(t, "none", authenticationInfo.PrincipalId)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||
|
|
@ -1038,7 +1264,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 +1276,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 +1294,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 +1307,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 +1337,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 +1347,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 +1359,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 +1368,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,16 +1,23 @@
|
|||
package api
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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 (
|
||||
|
|
@ -47,13 +54,7 @@ func ObjectTypeFromPluralString(value string) ObjectType {
|
|||
|
||||
func (t ObjectType) IsSupportedType() error {
|
||||
switch t {
|
||||
case ObjectTypeOrganization:
|
||||
fallthrough
|
||||
case ObjectTypeFolder:
|
||||
fallthrough
|
||||
case ObjectTypeProject:
|
||||
fallthrough
|
||||
case ObjectTypeSystem:
|
||||
case ObjectTypeOrganization, ObjectTypeFolder, ObjectTypeProject, ObjectTypeSystem:
|
||||
return nil
|
||||
default:
|
||||
return ErrUnknownObjectType
|
||||
|
|
@ -148,7 +149,7 @@ type AuditApi interface {
|
|||
// ProtobufValidator is an abstraction for validators.
|
||||
// Concrete implementations are e.g. protovalidate.Validator
|
||||
type ProtobufValidator interface {
|
||||
Validate(msg proto.Message) error
|
||||
Validate(msg proto.Message, options ...protovalidate.ValidationOption) error
|
||||
}
|
||||
|
||||
// CloudEvent is a representation of a cloudevents.io object.
|
||||
|
|
@ -223,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
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ func (g *DefaultSequenceNumberGenerator) Next() uint64 {
|
|||
var next uint64
|
||||
if len(g.backlog) == 0 {
|
||||
next = g.sequenceNumber
|
||||
g.sequenceNumber += 1
|
||||
g.sequenceNumber++
|
||||
} else {
|
||||
next = g.backlog[0]
|
||||
g.backlog = g.backlog[1:]
|
||||
|
|
@ -53,7 +53,7 @@ func (g *DefaultSequenceNumberGenerator) Revert(value uint64) {
|
|||
g.sequenceNumberLock.Lock()
|
||||
defer g.sequenceNumberLock.Unlock()
|
||||
if value == g.sequenceNumber-1 {
|
||||
g.sequenceNumber -= 1
|
||||
g.sequenceNumber--
|
||||
} else if !slices.Contains(g.backlog, value) {
|
||||
g.backlog = append(g.backlog, value)
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_DefaultSequenceNumberGenerator(t *testing.T) {
|
||||
|
|
@ -16,11 +16,12 @@ type Logger interface {
|
|||
|
||||
func wrapErr(err []error) error {
|
||||
var e error
|
||||
if len(err) == 0 {
|
||||
switch {
|
||||
case len(err) == 0:
|
||||
e = nil
|
||||
} else if len(err) == 1 {
|
||||
case len(err) == 1:
|
||||
e = err[0]
|
||||
} else {
|
||||
default:
|
||||
e = errors.Join(err...)
|
||||
}
|
||||
return e
|
||||
148
pkg/messaging/api/amqp.go
Normal file
148
pkg/messaging/api/amqp.go
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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"
|
||||
)
|
||||
|
||||
// AmqpApi implements Api.
|
||||
type AmqpApi struct {
|
||||
connection *internalMessaging.AmqpConnection
|
||||
connectionPool internalMessaging.ConnectionPool
|
||||
connectionPoolHandle *internalMessaging.ConnectionPoolHandle
|
||||
senderCache map[string]*internalMessaging.AmqpSenderSession
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
var _ Api = &AmqpApi{}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: connectionPool,
|
||||
connectionPoolHandle: connectionPool.NewHandle(),
|
||||
senderCache: make(map[string]*internalMessaging.AmqpSenderSession),
|
||||
}
|
||||
|
||||
var messagingApi Api = amqpApi
|
||||
return messagingApi, nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: connectionPool,
|
||||
connectionPoolHandle: connectionPool.NewHandle(),
|
||||
senderCache: make(map[string]*internalMessaging.AmqpSenderSession),
|
||||
}
|
||||
|
||||
var messagingApi Api = amqpApi
|
||||
return messagingApi, nil
|
||||
}
|
||||
|
||||
// Send implements Api.Send.
|
||||
// If errors occur the connection to the messaging system will be closed and re-established.
|
||||
func (a *AmqpApi) Send(_ context.Context, topic string, data []byte, contentType string, applicationProperties map[string]any) error {
|
||||
|
||||
// create or get sender from cache
|
||||
var sender = a.senderFromCache(topic)
|
||||
if sender == nil {
|
||||
if err := a.newSender(topic); err != nil {
|
||||
return err
|
||||
}
|
||||
sender = a.senderFromCache(topic)
|
||||
}
|
||||
|
||||
// first attempt to send
|
||||
var sendErr error
|
||||
wrappedData := [][]byte{data}
|
||||
if err := sender.Send(topic, wrappedData, contentType, applicationProperties); err != nil {
|
||||
sendErr = fmt.Errorf("send: %w", err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// renew sender
|
||||
if err := a.newSender(topic); err != nil {
|
||||
return errors.Join(sendErr, err)
|
||||
}
|
||||
sender = a.senderFromCache(topic)
|
||||
|
||||
// retry send
|
||||
if err := sender.Send(topic, wrappedData, contentType, applicationProperties); err != nil {
|
||||
return errors.Join(sendErr, fmt.Errorf("retry send: %w", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AmqpApi) senderFromCache(topic string) *internalMessaging.AmqpSenderSession {
|
||||
a.lock.RLock()
|
||||
defer a.lock.RUnlock()
|
||||
return a.senderCache[topic]
|
||||
}
|
||||
|
||||
func (a *AmqpApi) newSender(topic string) error {
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
connectionIsClosed := a.connection == nil || a.connection.IsClosed()
|
||||
if connectionIsClosed {
|
||||
connection, err := a.connectionPool.GetConnection(a.connectionPoolHandle)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get connection: %w", err)
|
||||
}
|
||||
a.connection = connection
|
||||
}
|
||||
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
sender, err := a.connection.NewSender(ctx, topic)
|
||||
cancelFn()
|
||||
if err != nil {
|
||||
return fmt.Errorf("new sender: %w", err)
|
||||
}
|
||||
|
||||
a.senderCache[topic] = sender
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements Api.Close
|
||||
func (a *AmqpApi) Close(_ context.Context) error {
|
||||
pkgLog.AuditLogger.Info("close audit amqp connection pool")
|
||||
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
// cached senders
|
||||
var closeErrors []error
|
||||
for _, session := range a.senderCache {
|
||||
if err := session.Close(); err != nil {
|
||||
closeErrors = append(closeErrors, fmt.Errorf("close session: %w", err))
|
||||
}
|
||||
}
|
||||
clear(a.senderCache)
|
||||
|
||||
// pool
|
||||
if err := a.connectionPool.Close(); err != nil {
|
||||
closeErrors = append(closeErrors, fmt.Errorf("close pool: %w", err))
|
||||
}
|
||||
|
||||
if len(closeErrors) > 0 {
|
||||
return fmt.Errorf("close: %w", errors.Join(closeErrors...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
463
pkg/messaging/api/amqp_test.go
Normal file
463
pkg/messaging/api/amqp_test.go
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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
|
||||
}
|
||||
|
||||
func (m *connectionPoolMock) Close() error {
|
||||
return m.Called().Error(0)
|
||||
}
|
||||
|
||||
func (m *connectionPoolMock) NewHandle() *internalMessaging.ConnectionPoolHandle {
|
||||
return m.Called().Get(0).(*internalMessaging.ConnectionPoolHandle)
|
||||
}
|
||||
|
||||
func (m *connectionPoolMock) GetConnection(handle *internalMessaging.ConnectionPoolHandle) (*internalMessaging.AmqpConnection, error) {
|
||||
return m.Called(handle).Get(0).(*internalMessaging.AmqpConnection), m.Called(handle).Error(1)
|
||||
}
|
||||
|
||||
var _ internalMessaging.ConnectionPool = (*connectionPoolMock)(nil)
|
||||
|
||||
func Test_NewAmqpMessagingApi(t *testing.T) {
|
||||
_, err := NewAmqpApi(
|
||||
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\"")
|
||||
}
|
||||
|
||||
func Test_AmqpMessagingApi_Send(t *testing.T) {
|
||||
// Specify test timeout
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 120*time.Second)
|
||||
defer cancelFn()
|
||||
|
||||
// Start solace docker container
|
||||
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(pkgMessagingCommon.AmqpConnectionPoolConfig{
|
||||
Parameters: pkgMessagingCommon.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString},
|
||||
PoolSize: 1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = api.Send(ctx, "topic-name", []byte{}, "application/json", make(map[string]any))
|
||||
assert.EqualError(t, err, "send: topic \"topic-name\" name lacks mandatory prefix \"topic://\"\nretry send: topic \"topic-name\" name lacks mandatory prefix \"topic://\"")
|
||||
})
|
||||
|
||||
t.Run("send successfully", func(t *testing.T) {
|
||||
defer solaceContainer.StopOnError()
|
||||
|
||||
// Initialize the solace queue
|
||||
topicSubscriptionTopicPattern := "auditlog/>"
|
||||
queueName := "send-successfully"
|
||||
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
|
||||
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
|
||||
topicName := fmt.Sprintf("topic://auditlog/%s", "amqp-send-successfully")
|
||||
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
|
||||
|
||||
api, err := NewDefaultAmqpApi(pkgMessagingCommon.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString})
|
||||
assert.NoError(t, err)
|
||||
|
||||
data := []byte("data")
|
||||
applicationProperties := make(map[string]interface{})
|
||||
applicationProperties["key"] = "value"
|
||||
|
||||
err = api.Send(ctx, topicName, data, "application/json", applicationProperties)
|
||||
assert.NoError(t, err)
|
||||
|
||||
message, err := solaceContainer.NextMessage(ctx, fmt.Sprintf("queue://%s", queueName), true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "data", string(message.Data[0]))
|
||||
assert.Equal(t, topicName, *message.Properties.To)
|
||||
assert.Equal(t, "application/json", *message.Properties.ContentType)
|
||||
assert.Equal(t, applicationProperties, message.ApplicationProperties)
|
||||
|
||||
err = api.Close(ctx)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpMessagingApi_Send_Special_Cases(t *testing.T) {
|
||||
|
||||
channelReceiver := func(channel chan struct{}) <-chan struct{} {
|
||||
return channel
|
||||
}
|
||||
|
||||
newActiveConnection := func() *internalMessaging.AmqpConnection {
|
||||
channel := make(chan struct{})
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Done", mock.Anything).Return(channelReceiver(channel))
|
||||
return &internalMessaging.AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
newClosedConnection := func() *internalMessaging.AmqpConnection {
|
||||
channel := make(chan struct{})
|
||||
close(channel)
|
||||
|
||||
conn := &amqpConnMock{}
|
||||
conn.On("Done", mock.Anything).Return(channelReceiver(channel))
|
||||
return &internalMessaging.AmqpConnection{
|
||||
ConnectionName: "test",
|
||||
Lock: sync.RWMutex{},
|
||||
Conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("connection nil sender nil", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Send", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
session := &amqpSessionMock{}
|
||||
session.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(sender, nil)
|
||||
|
||||
connection := newActiveConnection()
|
||||
conn := connection.Conn.(*amqpConnMock)
|
||||
conn.On("NewSession", mock.Anything, mock.Anything).Return(session, nil)
|
||||
|
||||
pool := &connectionPoolMock{}
|
||||
pool.On("GetConnection", mock.Anything).Return(connection, nil)
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: pool,
|
||||
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))
|
||||
assert.NoError(t, err)
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Send", 1)
|
||||
session.AssertNumberOfCalls(t, "NewSender", 1)
|
||||
pool.AssertNumberOfCalls(t, "GetConnection", 2)
|
||||
})
|
||||
|
||||
t.Run("connection closed sender nil", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Send", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
session := &amqpSessionMock{}
|
||||
session.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(sender, nil)
|
||||
|
||||
connection := newActiveConnection()
|
||||
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)
|
||||
amqpApi := &AmqpApi{
|
||||
connection: closedConnection,
|
||||
connectionPool: pool,
|
||||
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))
|
||||
assert.NoError(t, err)
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Send", 1)
|
||||
session.AssertNumberOfCalls(t, "NewSender", 1)
|
||||
pool.AssertNumberOfCalls(t, "GetConnection", 2)
|
||||
closedConnMock.AssertNumberOfCalls(t, "Done", 1)
|
||||
})
|
||||
|
||||
t.Run("connection nil get connection fail", func(t *testing.T) {
|
||||
var connection *internalMessaging.AmqpConnection = nil
|
||||
|
||||
pool := &connectionPoolMock{}
|
||||
pool.On("GetConnection", mock.Anything).Return(connection, errors.New("connection error"))
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: pool,
|
||||
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))
|
||||
assert.EqualError(t, err, "get connection: connection error")
|
||||
|
||||
pool.AssertNumberOfCalls(t, "GetConnection", 2)
|
||||
})
|
||||
|
||||
t.Run("connection active sender nil", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Send", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
session := &amqpSessionMock{}
|
||||
session.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(sender, nil)
|
||||
|
||||
connection := newActiveConnection()
|
||||
conn := connection.Conn.(*amqpConnMock)
|
||||
conn.On("NewSession", mock.Anything, mock.Anything).Return(session, nil)
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connection: connection,
|
||||
senderCache: make(map[string]*internalMessaging.AmqpSenderSession),
|
||||
}
|
||||
|
||||
err := amqpApi.Send(context.Background(), "topic://some-topic", []byte("data"), "application/json", make(map[string]any))
|
||||
assert.NoError(t, err)
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Send", 1)
|
||||
session.AssertNumberOfCalls(t, "NewSender", 1)
|
||||
})
|
||||
|
||||
t.Run("connection active new sender fail", func(t *testing.T) {
|
||||
var sender *amqpSenderMock = nil
|
||||
|
||||
session := &amqpSessionMock{}
|
||||
session.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(sender, errors.New("new sender error"))
|
||||
session.On("Close", mock.Anything).Return(nil)
|
||||
|
||||
connection := newActiveConnection()
|
||||
conn := connection.Conn.(*amqpConnMock)
|
||||
conn.On("NewSession", mock.Anything, mock.Anything).Return(session, nil)
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connection: connection,
|
||||
senderCache: make(map[string]*internalMessaging.AmqpSenderSession),
|
||||
}
|
||||
|
||||
err := amqpApi.Send(context.Background(), "topic://some-topic", []byte("data"), "application/json", make(map[string]any))
|
||||
assert.EqualError(t, err, "new sender: new internal sender: new sender error")
|
||||
|
||||
session.AssertNumberOfCalls(t, "NewSender", 1)
|
||||
session.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
|
||||
t.Run("connection active sender set", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Send", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
topic := "topic://some-topic"
|
||||
amqpApi := &AmqpApi{
|
||||
connection: newActiveConnection(),
|
||||
senderCache: map[string]*internalMessaging.AmqpSenderSession{topic: {Sender: sender}},
|
||||
}
|
||||
|
||||
err := amqpApi.Send(context.Background(), topic, []byte("data"), "application/json", make(map[string]any))
|
||||
assert.NoError(t, err)
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Send", 1)
|
||||
})
|
||||
|
||||
t.Run("send fail", func(t *testing.T) {
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Send", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("send error"))
|
||||
|
||||
session := &amqpSessionMock{}
|
||||
session.On("NewSender", mock.Anything, mock.Anything, mock.Anything).Return(sender, nil)
|
||||
|
||||
topic := "topic://some-topic"
|
||||
connection := newActiveConnection()
|
||||
connection.Conn.(*amqpConnMock).On("NewSession", mock.Anything, mock.Anything, mock.Anything).Return(session, nil)
|
||||
amqpApi := &AmqpApi{
|
||||
connection: connection,
|
||||
senderCache: map[string]*internalMessaging.AmqpSenderSession{topic: {Sender: sender}},
|
||||
}
|
||||
|
||||
err := amqpApi.Send(context.Background(), topic, []byte("data"), "application/json", make(map[string]any))
|
||||
assert.EqualError(t, err, "send: send error\nretry send: send error")
|
||||
|
||||
sender.AssertNumberOfCalls(t, "Send", 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AmqpMessagingApi_Close(t *testing.T) {
|
||||
|
||||
t.Run("close without cached senders", func(t *testing.T) {
|
||||
pool := &connectionPoolMock{}
|
||||
pool.On("Close").Return(nil)
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: pool,
|
||||
connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0},
|
||||
senderCache: make(map[string]*internalMessaging.AmqpSenderSession),
|
||||
}
|
||||
|
||||
err := amqpApi.Close(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
||||
pool.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
|
||||
t.Run("close fail without cached senders", func(t *testing.T) {
|
||||
pool := &connectionPoolMock{}
|
||||
pool.On("Close").Return(errors.New("close error"))
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: pool,
|
||||
connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0},
|
||||
senderCache: make(map[string]*internalMessaging.AmqpSenderSession),
|
||||
}
|
||||
|
||||
err := amqpApi.Close(context.Background())
|
||||
assert.EqualError(t, err, "close: close pool: close error")
|
||||
|
||||
pool.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
|
||||
t.Run("close with cached senders", func(t *testing.T) {
|
||||
pool := &connectionPoolMock{}
|
||||
pool.On("Close").Return(nil)
|
||||
|
||||
session := &amqpSessionMock{}
|
||||
session.On("Close", mock.Anything).Return(nil)
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Close", mock.Anything).Return(nil)
|
||||
senderSession := &internalMessaging.AmqpSenderSession{
|
||||
Session: session,
|
||||
Sender: sender,
|
||||
}
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: pool,
|
||||
connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0},
|
||||
senderCache: map[string]*internalMessaging.AmqpSenderSession{"key": senderSession},
|
||||
}
|
||||
|
||||
err := amqpApi.Close(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(amqpApi.senderCache))
|
||||
|
||||
pool.AssertNumberOfCalls(t, "Close", 1)
|
||||
session.AssertNumberOfCalls(t, "Close", 1)
|
||||
sender.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
|
||||
t.Run("close fail with cached senders", func(t *testing.T) {
|
||||
pool := &connectionPoolMock{}
|
||||
pool.On("Close").Return(nil)
|
||||
|
||||
session := &amqpSessionMock{}
|
||||
session.On("Close", mock.Anything).Return(nil)
|
||||
sender := &amqpSenderMock{}
|
||||
sender.On("Close", mock.Anything).Return(errors.New("close sender error"))
|
||||
senderSession := &internalMessaging.AmqpSenderSession{
|
||||
Session: session,
|
||||
Sender: sender,
|
||||
}
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: pool,
|
||||
connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0},
|
||||
senderCache: map[string]*internalMessaging.AmqpSenderSession{"key": senderSession},
|
||||
}
|
||||
|
||||
err := amqpApi.Close(context.Background())
|
||||
assert.EqualError(t, err, "close: close session: close sender error")
|
||||
assert.Equal(t, 0, len(amqpApi.senderCache))
|
||||
|
||||
pool.AssertNumberOfCalls(t, "Close", 1)
|
||||
session.AssertNumberOfCalls(t, "Close", 1)
|
||||
sender.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
|
||||
t.Run("close fail", func(t *testing.T) {
|
||||
pool := &connectionPoolMock{}
|
||||
pool.On("Close").Return(errors.New("close pool error"))
|
||||
|
||||
session := &amqpSessionMock{}
|
||||
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 := &internalMessaging.AmqpSenderSession{
|
||||
Session: session,
|
||||
Sender: sender,
|
||||
}
|
||||
|
||||
amqpApi := &AmqpApi{
|
||||
connectionPool: pool,
|
||||
connectionPoolHandle: &internalMessaging.ConnectionPoolHandle{ConnectionOffset: 0},
|
||||
senderCache: map[string]*internalMessaging.AmqpSenderSession{"key": senderSession},
|
||||
}
|
||||
|
||||
err := amqpApi.Close(context.Background())
|
||||
assert.EqualError(t, err, "close: close session: close sender error\nclose session error\nclose pool: close pool error")
|
||||
assert.Equal(t, 0, len(amqpApi.senderCache))
|
||||
|
||||
pool.AssertNumberOfCalls(t, "Close", 1)
|
||||
session.AssertNumberOfCalls(t, "Close", 1)
|
||||
sender.AssertNumberOfCalls(t, "Close", 1)
|
||||
})
|
||||
}
|
||||
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,8 +1,4 @@
|
|||
package messaging
|
||||
|
||||
const AmqpTopicPrefix = "topic://"
|
||||
const connectionTimeoutSeconds = 10
|
||||
const disconnectionTimeoutSeconds = 10
|
||||
package common
|
||||
|
||||
type AmqpConnectionConfig struct {
|
||||
BrokerUrl string `json:"brokerUrl"`
|
||||
|
|
@ -1,25 +1,28 @@
|
|||
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"
|
||||
"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 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")
|
||||
|
||||
|
|
@ -132,12 +135,13 @@ func NewSolaceContainer(ctx context.Context) (*SolaceContainer, error) {
|
|||
|
||||
// Start docker container
|
||||
request := testcontainers.ContainerRequest{
|
||||
Image: "solace/solace-pubsub-standard:10.8",
|
||||
Image: dockerImage,
|
||||
ExposedPorts: []string{"5672/tcp", "8080/tcp"},
|
||||
SkipReaper: true,
|
||||
AutoRemove: true,
|
||||
ShmSize: 1024 * 1024 * 1024, // 1 GB,
|
||||
Env: env,
|
||||
HostConfigModifier: func(config *docker.HostConfig) {
|
||||
config.AutoRemove = true
|
||||
config.ShmSize = 1024 * 1024 * 1024 // 1 GB,
|
||||
},
|
||||
Env: env,
|
||||
WaitingFor: wait.ForLog("Running pre-startup checks:").
|
||||
WithStartupTimeout(90 * time.Second),
|
||||
}
|
||||
|
|
@ -167,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())
|
||||
|
|
@ -295,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
|
||||
}
|
||||
|
|
@ -131,7 +131,8 @@ message AuditLog {
|
|||
// Required: true
|
||||
string service_name = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// The name of the service method or operation.
|
||||
|
|
@ -232,17 +233,18 @@ message AuthenticationInfo {
|
|||
// Required: true
|
||||
string principal_id = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// The email address of the authenticated user.
|
||||
// Service accounts have email addresses that can be used.
|
||||
//
|
||||
// Required: true
|
||||
string principal_email = 2 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.max_len = 255
|
||||
// Required: false
|
||||
optional string principal_email = 2 [
|
||||
(buf.validate.field).string.min_len = 5,
|
||||
(buf.validate.field).string.max_len = 255,
|
||||
(buf.validate.field).string.email = true
|
||||
];
|
||||
|
||||
// The name of the service account used to create or exchange
|
||||
|
|
@ -325,7 +327,7 @@ message AttributeContext {
|
|||
// Required: true
|
||||
string principal = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.pattern = "^[a-zA-Z0-9-%.]+/[a-zA-Z0-9-%.]+$"
|
||||
(buf.validate.field).string.pattern = "^[a-zA-Z0-9-%._]+/[a-zA-Z0-9-%.]+$"
|
||||
];
|
||||
|
||||
// The intended audience(s) for this authentication information. Reflects
|
||||
|
|
@ -414,7 +416,8 @@ message AttributeContext {
|
|||
string path = 4 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.max_len = 255
|
||||
(buf.validate.field).string.max_len = 255,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// The HTTP request `Host` header value.
|
||||
|
|
@ -422,7 +425,8 @@ message AttributeContext {
|
|||
// Required: true
|
||||
string host = 5 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// The URL scheme, such as `http`, `https` or `gRPC`.
|
||||
|
|
@ -430,7 +434,8 @@ message AttributeContext {
|
|||
// Required: true
|
||||
string scheme = 6 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// The HTTP URL query in the format of "name1=value1&name2=value2", as it
|
||||
|
|
@ -457,7 +462,8 @@ message AttributeContext {
|
|||
// Required: true
|
||||
string protocol = 9 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// The request authentication.
|
||||
|
|
@ -521,7 +527,8 @@ message RequestMetadata {
|
|||
string caller_supplied_user_agent = 2 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.max_len = 255
|
||||
(buf.validate.field).string.max_len = 255,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// This field contains request attributes like request url, time, etc.
|
||||
|
|
@ -577,7 +584,8 @@ message ServiceAccountDelegationInfo {
|
|||
// Required: true
|
||||
string principal_id = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// The email address of the authenticated user.
|
||||
|
|
@ -587,7 +595,8 @@ message ServiceAccountDelegationInfo {
|
|||
string principal_email = 2 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.min_len = 1,
|
||||
(buf.validate.field).string.max_len = 255
|
||||
(buf.validate.field).string.max_len = 255,
|
||||
(buf.validate.field).string.pattern = ".*\\S.*"
|
||||
];
|
||||
|
||||
// Metadata about the service that uses the service account.
|
||||
|
|
|
|||
13
sonar-project.properties
Normal file
13
sonar-project.properties
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
sonar.projectKey=xx-sit-odj-sec-ident:audit-go
|
||||
sonar.host.url=https://sonarqube.schwarz
|
||||
sonar.projectName=audit-go
|
||||
sonar.sources=.
|
||||
sonar.exclusions=**/*_test.go,**/vendor/**,**/mocks/**,**/*.yml,**/gen/**, **/test/solace.go
|
||||
sonar.tests=.
|
||||
sonar.test.inclusions=**/*_test.go
|
||||
sonar.test.exclusions=**/vendor/**,**/mocks/**
|
||||
sonar.issuesReport.html.enable=true
|
||||
sonar.log.level=INFO
|
||||
sonar.go.coverage.reportPaths=out/cover.out
|
||||
sonar.go.tests.reportPaths=out/report.json
|
||||
sonar.go.golangci-lint.reportPaths=out/lint.xml
|
||||
Loading…
Reference in a new issue