Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5100d53c76 | ||
|
|
01f30cfd2b | ||
|
|
9d1b981644 | ||
|
|
3347d598a4 | ||
|
|
6bdf6e1cd3 | ||
|
|
f5e9eb52a7 | ||
|
|
85d29c1196 | ||
|
|
37d9282f7e | ||
|
|
5c2477eded | ||
|
|
bfd5fd4c16 | ||
|
|
dfc666ebb0 | ||
|
|
75f87fd618 | ||
|
|
752f80ea96 | ||
|
|
08269c034b | ||
|
|
89fa8928dc | ||
|
|
6fccd2010c | ||
|
|
4296334275 | ||
|
|
daa836e97d | ||
|
|
f9a89b64be | ||
|
|
775d25cb59 | ||
|
|
ef992cb9db | ||
|
|
c3b8a3f1ce |
No files matched your search
+15
-19
@@ -10,19 +10,18 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: 'stable'
|
go-version: 'stable'
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: go test -race -coverprofile=coverage.txt ./...
|
run: go test -race -coverprofile=coverage.txt ./...
|
||||||
|
|
||||||
- name: Check coverage
|
- name: Check coverage
|
||||||
uses: vladopajic/go-test-coverage@v2
|
id: coverage
|
||||||
with:
|
run: |
|
||||||
config: ./.testcoverage.yml
|
go install github.com/vladopajic/go-test-coverage/v2@latest
|
||||||
|
go-test-coverage --config ./.testcoverage.yml --github-action-output
|
||||||
# Download baseline coverage from main branch (for PRs)
|
|
||||||
- name: Download baseline coverage
|
- name: Download baseline coverage
|
||||||
if: gitea.event_name == 'pull_request'
|
if: gitea.event_name == 'pull_request'
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
@@ -30,14 +29,12 @@ jobs:
|
|||||||
name: coverage-baseline
|
name: coverage-baseline
|
||||||
path: ./baseline
|
path: ./baseline
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
# Compare coverage against baseline (for PRs)
|
|
||||||
- name: Compare coverage
|
- name: Compare coverage
|
||||||
if: gitea.event_name == 'pull_request'
|
if: gitea.event_name == 'pull_request'
|
||||||
run: |
|
run: |
|
||||||
CURRENT=$(go tool cover -func=coverage.txt | grep "^total:" | awk '{print $NF}' | tr -d '%')
|
CURRENT="${{ steps.coverage.outputs.total-coverage }}"
|
||||||
if [ -f ./baseline/coverage.txt ]; then
|
if [ -f ./baseline/coverage.txt ]; then
|
||||||
BASE=$(go tool cover -func=./baseline/coverage.txt | grep "^total:" | awk '{print $NF}' | tr -d '%')
|
BASE=$(cat ./baseline/coverage.txt)
|
||||||
echo "Base coverage: ${BASE}%"
|
echo "Base coverage: ${BASE}%"
|
||||||
echo "Current coverage: ${CURRENT}%"
|
echo "Current coverage: ${CURRENT}%"
|
||||||
if [ "$(echo "$CURRENT < $BASE" | bc -l)" -eq 1 ]; then
|
if [ "$(echo "$CURRENT < $BASE" | bc -l)" -eq 1 ]; then
|
||||||
@@ -49,8 +46,9 @@ jobs:
|
|||||||
echo "No baseline coverage found, skipping comparison"
|
echo "No baseline coverage found, skipping comparison"
|
||||||
echo "Current coverage: ${CURRENT}%"
|
echo "Current coverage: ${CURRENT}%"
|
||||||
fi
|
fi
|
||||||
|
- name: Save coverage baseline
|
||||||
# Upload coverage as baseline (only on main)
|
if: gitea.ref == 'refs/heads/main'
|
||||||
|
run: echo "${{ steps.coverage.outputs.total-coverage }}" > coverage.txt
|
||||||
- name: Upload coverage baseline
|
- name: Upload coverage baseline
|
||||||
if: gitea.ref == 'refs/heads/main'
|
if: gitea.ref == 'refs/heads/main'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
@@ -58,25 +56,23 @@ jobs:
|
|||||||
name: coverage-baseline
|
name: coverage-baseline
|
||||||
path: coverage.txt
|
path: coverage.txt
|
||||||
retention-days: 90
|
retention-days: 90
|
||||||
|
|
||||||
# Post coverage to PR comment
|
|
||||||
- name: Post coverage comment
|
- name: Post coverage comment
|
||||||
if: gitea.event_name == 'pull_request'
|
if: gitea.event_name == 'pull_request'
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
GITEA_URL: ${{ gitea.server_url }}
|
GITEA_URL: ${{ gitea.server_url }}
|
||||||
run: |
|
run: |
|
||||||
COVERAGE=$(go tool cover -func=coverage.txt | grep "^total:" | awk '{print $NF}')
|
COVERAGE="${{ steps.coverage.outputs.total-coverage }}"
|
||||||
curl -X POST "${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/issues/${{ gitea.event.pull_request.number }}/comments" \
|
curl -X POST "${GITEA_URL}/api/v1/repos/${{ gitea.repository }}/issues/${{ gitea.event.pull_request.number }}/comments" \
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"body\": \"## Coverage Report\n\nTotal coverage: **${COVERAGE}**\"}"
|
-d "{\"body\": \"## Coverage Report\n\nTotal coverage: **${COVERAGE}%**\"}"
|
||||||
|
|
||||||
vulnerabilities:
|
vulnerabilities:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: 'stable'
|
go-version: 'stable'
|
||||||
- name: Check vulnerabilities
|
- name: Check vulnerabilities
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SKIP: no-commit-to-branch
|
SKIP: no-commit-to-branch
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: stable
|
go-version: stable
|
||||||
- uses: actions/setup-python@v6
|
- uses: actions/setup-python@v7
|
||||||
with:
|
with:
|
||||||
python-version: '3.14'
|
python-version: '3.14'
|
||||||
- name: Install goimports
|
- name: Install goimports
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ repos:
|
|||||||
- --allow-multiple-documents
|
- --allow-multiple-documents
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
||||||
rev: v9.24.0
|
rev: v9.26.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: commitlint
|
- id: commitlint
|
||||||
stages: [ commit-msg ]
|
stages: [ commit-msg ]
|
||||||
@@ -30,10 +30,10 @@ repos:
|
|||||||
- id: go-test
|
- id: go-test
|
||||||
- id: gofumpt
|
- id: gofumpt
|
||||||
- repo: https://github.com/golangci/golangci-lint
|
- repo: https://github.com/golangci/golangci-lint
|
||||||
rev: v2.11.3
|
rev: v2.13.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: golangci-lint-full
|
- id: golangci-lint-full
|
||||||
- repo: https://github.com/gitleaks/gitleaks
|
- repo: https://github.com/gitleaks/gitleaks
|
||||||
rev: v8.30.0
|
rev: v8.30.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: gitleaks
|
- id: gitleaks
|
||||||
@@ -2,6 +2,46 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [0.6.1] - 2026-09-16
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- Keep existing privileges when User.Added is processed late (#331)
|
||||||
|
|
||||||
|
## [0.6.0] - 2026-09-14
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- [**breaking**] Consume privilege events with go-messaging-amqp (#327)
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.13.2 (#325)
|
||||||
|
- Bump the minor version for breaking changes before 1.0.0 (#328)
|
||||||
|
|
||||||
|
## [0.5.1] - 2026-08-29
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- *(ci)* Use go-test-coverage binary directly to fix Gitea Actions (#303)
|
||||||
|
- *(deps)* Update module github.com/stretchr/testify to v1.12.0 (#319)
|
||||||
|
- *(deps)* Update module github.com/stretchr/testify to v1.12.1 (#321)
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- *(deps)* Update pre-commit hook gitleaks/gitleaks to v8.30.1 (#296)
|
||||||
|
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.4 (#298)
|
||||||
|
- *(deps)* Update dependency go to v1.26.2 (#300)
|
||||||
|
- *(deps)* Update pre-commit hook alessandrojcm/commitlint-pre-commit-hook to v9.25.0 (#304)
|
||||||
|
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.12.0 (#306)
|
||||||
|
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.12.1 (#308)
|
||||||
|
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.12.2 (#309)
|
||||||
|
- *(deps)* Update actions/checkout action to v7 (#311)
|
||||||
|
- *(deps)* Update pre-commit hook alessandrojcm/commitlint-pre-commit-hook to v9.26.0 (#313)
|
||||||
|
- *(deps)* Update actions/setup-go action to v7 (#315)
|
||||||
|
- *(deps)* Update actions/setup-python action to v7 (#317)
|
||||||
|
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.13.1 (#322)
|
||||||
|
|
||||||
## [0.5.0] - 2026-03-12
|
## [0.5.0] - 2026-03-12
|
||||||
|
|
||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|||||||
@@ -43,4 +43,4 @@ The `CompanyPrivileges` struct contains permission flags:
|
|||||||
|
|
||||||
### Event Handling
|
### Event Handling
|
||||||
|
|
||||||
Implements `goamqp` message handlers to receive privilege update events from the authz-service, keeping the local privilege cache up-to-date.
|
Registers per-replica (transient) go-messaging-amqp consumers for privilege update events from the authz-service (`Setup()`), keeping the local privilege cache up-to-date. Each routing key gets its own queue, so events for one company arrive in any order — `Process` must never discard state it didn't itself record. `User.Added` therefore creates the company entry only when it is missing: a `Privilege.Added` processed ahead of it would otherwise be overwritten, and nothing re-reads the privilege until the next `Fetch()`. The converse doesn't hold yet — `setPrivileges` still creates an entry for a company it never saw a `User.Added` for, so a `Privilege.*` delivered after `User.Removed` resurrects an all-false membership. Don't combine `Setup()` with go-messaging-amqp's `WithReconnect`: a reconnect declares new per-replica queues, so revocations published during the outage are lost unless `Fetch()` runs again. Services exit on connection loss (`CloseListener`) and re-fetch on start.
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -8,7 +9,8 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/sparetimecoders/goamqp"
|
goamqp "codeberg.org/messaging/go-messaging-amqp"
|
||||||
|
spec "codeberg.org/messaging/messaging"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompanyPrivileges contains the privileges for a combination of email address and company id
|
// CompanyPrivileges contains the privileges for a combination of email address and company id
|
||||||
@@ -95,42 +97,59 @@ func (h *PrivilegeHandler) Fetch() error {
|
|||||||
|
|
||||||
func (h *PrivilegeHandler) Setup() []goamqp.Setup {
|
func (h *PrivilegeHandler) Setup() []goamqp.Setup {
|
||||||
return []goamqp.Setup{
|
return []goamqp.Setup{
|
||||||
goamqp.TransientEventStreamConsumer("User.Added", h.Process, UserAdded{}),
|
goamqp.TransientEventStreamConsumer("User.Added", process[UserAdded](h)),
|
||||||
goamqp.TransientEventStreamConsumer("User.Removed", h.Process, UserRemoved{}),
|
goamqp.TransientEventStreamConsumer("User.Removed", process[UserRemoved](h)),
|
||||||
goamqp.TransientEventStreamConsumer("Privilege.Added", h.Process, PrivilegeAdded{}),
|
goamqp.TransientEventStreamConsumer("Privilege.Added", process[PrivilegeAdded](h)),
|
||||||
goamqp.TransientEventStreamConsumer("Privilege.Removed", h.Process, PrivilegeRemoved{}),
|
goamqp.TransientEventStreamConsumer("Privilege.Removed", process[PrivilegeRemoved](h)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// privilegeEvent is the set of events Process handles.
|
||||||
|
type privilegeEvent interface {
|
||||||
|
UserAdded | UserRemoved | PrivilegeAdded | PrivilegeRemoved
|
||||||
|
}
|
||||||
|
|
||||||
|
// process adapts Process to a typed go-messaging-amqp handler.
|
||||||
|
func process[T privilegeEvent](h *PrivilegeHandler) spec.EventHandler[T] {
|
||||||
|
return func(_ context.Context, event spec.ConsumableEvent[T]) error {
|
||||||
|
return h.Process(&event.Payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process privilege-related events and update the internal state
|
// Process privilege-related events and update the internal state
|
||||||
func (h *PrivilegeHandler) Process(msg interface{}, _ goamqp.Headers) (interface{}, error) {
|
func (h *PrivilegeHandler) Process(msg any) error {
|
||||||
h.Lock()
|
h.Lock()
|
||||||
defer h.Unlock()
|
defer h.Unlock()
|
||||||
|
|
||||||
switch ev := msg.(type) {
|
switch ev := msg.(type) {
|
||||||
case *UserAdded:
|
case *UserAdded:
|
||||||
if priv, exists := h.privileges[ev.Email]; exists {
|
// Keep the privileges already recorded for the company. Each routing key has
|
||||||
priv[ev.CompanyID] = &CompanyPrivileges{}
|
// its own transient queue, so a Privilege.Added published after this event can
|
||||||
} else {
|
// be processed before it; overwriting the entry here would drop that privilege
|
||||||
h.privileges[ev.Email] = map[string]*CompanyPrivileges{
|
// until the next Fetch, which only runs at start.
|
||||||
ev.CompanyID: {},
|
priv, exists := h.privileges[ev.Email]
|
||||||
}
|
if !exists {
|
||||||
|
priv = map[string]*CompanyPrivileges{}
|
||||||
|
h.privileges[ev.Email] = priv
|
||||||
}
|
}
|
||||||
return nil, nil
|
if _, exists := priv[ev.CompanyID]; !exists {
|
||||||
|
priv[ev.CompanyID] = &CompanyPrivileges{}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
case *UserRemoved:
|
case *UserRemoved:
|
||||||
if priv, exists := h.privileges[ev.Email]; exists {
|
if priv, exists := h.privileges[ev.Email]; exists {
|
||||||
delete(priv, ev.CompanyID)
|
delete(priv, ev.CompanyID)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil
|
||||||
case *PrivilegeAdded:
|
case *PrivilegeAdded:
|
||||||
h.setPrivileges(ev.Email, ev.CompanyID, ev.Privilege, true)
|
h.setPrivileges(ev.Email, ev.CompanyID, ev.Privilege, true)
|
||||||
return nil, nil
|
return nil
|
||||||
case *PrivilegeRemoved:
|
case *PrivilegeRemoved:
|
||||||
h.setPrivileges(ev.Email, ev.CompanyID, ev.Privilege, false)
|
h.setPrivileges(ev.Email, ev.CompanyID, ev.Privilege, false)
|
||||||
return nil, nil
|
return nil
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Got unexpected message type (%s): '%+v'\n", reflect.TypeOf(msg).String(), msg)
|
fmt.Printf("Got unexpected message type (%s): '%+v'\n", reflect.TypeOf(msg).String(), msg)
|
||||||
return nil, fmt.Errorf("unexpected event type: '%s'", reflect.TypeOf(msg))
|
return fmt.Errorf("unexpected event type: '%s'", reflect.TypeOf(msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+129
-47
@@ -1,6 +1,7 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@@ -8,28 +9,28 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sparetimecoders/goamqp"
|
goamqp "codeberg.org/messaging/go-messaging-amqp"
|
||||||
|
spec "codeberg.org/messaging/messaging"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPrivilegeHandler_Process_InvalidType(t *testing.T) {
|
func TestPrivilegeHandler_Process_InvalidType(t *testing.T) {
|
||||||
handler := New(WithBaseURL("base"))
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
result, err := handler.Process("abc", goamqp.Headers{})
|
err := handler.Process("abc")
|
||||||
|
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.EqualError(t, err, "unexpected event type: 'string'")
|
assert.EqualError(t, err, "unexpected event type: 'string'")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrivilegeHandler_Process_PrivilegeRemoved(t *testing.T) {
|
func TestPrivilegeHandler_Process_PrivilegeRemoved(t *testing.T) {
|
||||||
handler := New(WithBaseURL("base"))
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
result, err := handler.Process(&PrivilegeAdded{
|
err := handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeAdmin,
|
Privilege: PrivilegeAdmin,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
||||||
@@ -38,12 +39,11 @@ func TestPrivilegeHandler_Process_PrivilegeRemoved(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, []string{"abc-123"}, companies)
|
assert.Equal(t, []string{"abc-123"}, companies)
|
||||||
|
|
||||||
result, err = handler.Process(&PrivilegeRemoved{
|
err = handler.Process(&PrivilegeRemoved{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeAdmin,
|
Privilege: PrivilegeAdmin,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
||||||
@@ -56,18 +56,16 @@ func TestPrivilegeHandler_Process_PrivilegeRemoved(t *testing.T) {
|
|||||||
func TestPrivilegeHandler_Process_UserAdded_And_UserRemoved(t *testing.T) {
|
func TestPrivilegeHandler_Process_UserAdded_And_UserRemoved(t *testing.T) {
|
||||||
handler := New(WithBaseURL("base"))
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
result, err := handler.Process(&UserAdded{
|
err := handler.Process(&UserAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
result, err = handler.Process(&UserAdded{
|
err = handler.Process(&UserAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-456",
|
CompanyID: "abc-456",
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
||||||
@@ -76,18 +74,16 @@ func TestPrivilegeHandler_Process_UserAdded_And_UserRemoved(t *testing.T) {
|
|||||||
sort.Strings(companies)
|
sort.Strings(companies)
|
||||||
assert.Equal(t, []string{"abc-123", "abc-456"}, companies)
|
assert.Equal(t, []string{"abc-123", "abc-456"}, companies)
|
||||||
|
|
||||||
result, err = handler.Process(&UserRemoved{
|
err = handler.Process(&UserRemoved{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
result, err = handler.Process(&UserRemoved{
|
err = handler.Process(&UserRemoved{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-456",
|
CompanyID: "abc-456",
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
||||||
@@ -96,6 +92,50 @@ func TestPrivilegeHandler_Process_UserAdded_And_UserRemoved(t *testing.T) {
|
|||||||
assert.Empty(t, companies)
|
assert.Empty(t, companies)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrivilegeHandler_Process_UserAdded_Keeps_Existing_Privileges(t *testing.T) {
|
||||||
|
// Each routing key has its own transient queue, so Privilege.Added can be
|
||||||
|
// processed before the User.Added published before it. The privilege must
|
||||||
|
// survive either order: nothing re-reads it until the next Fetch at start.
|
||||||
|
userAdded := &UserAdded{Email: "jim@example.org", CompanyID: "abc-123"}
|
||||||
|
privilegeAdded := &PrivilegeAdded{Email: "jim@example.org", CompanyID: "abc-123", Privilege: PrivilegeAdmin}
|
||||||
|
for name, order := range map[string][]any{
|
||||||
|
"user added first": {userAdded, privilegeAdded},
|
||||||
|
"privilege added first": {privilegeAdded, userAdded},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
handler := New(WithBaseURL("base"))
|
||||||
|
for _, event := range order {
|
||||||
|
require.NoError(t, handler.Process(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
||||||
|
return privileges.Admin
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrivilegeHandler_Process_UserAdded_After_UserRemoved_Starts_Empty(t *testing.T) {
|
||||||
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
|
assert.NoError(t, handler.Process(&UserAdded{Email: "jim@example.org", CompanyID: "abc-123"}))
|
||||||
|
assert.NoError(t, handler.Process(&PrivilegeAdded{
|
||||||
|
Email: "jim@example.org",
|
||||||
|
CompanyID: "abc-123",
|
||||||
|
Privilege: PrivilegeAdmin,
|
||||||
|
}))
|
||||||
|
assert.NoError(t, handler.Process(&UserRemoved{Email: "jim@example.org", CompanyID: "abc-123"}))
|
||||||
|
assert.NoError(t, handler.Process(&UserAdded{Email: "jim@example.org", CompanyID: "abc-123"}))
|
||||||
|
|
||||||
|
// Membership is back, without the privileges the removal took away.
|
||||||
|
assert.Equal(t, []string{"abc-123"}, handler.CompaniesByUser("jim@example.org", func(CompanyPrivileges) bool {
|
||||||
|
return true
|
||||||
|
}))
|
||||||
|
assert.False(t, handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
||||||
|
return privileges.Admin
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrivilegeHandler_GetCompanies_Email_Not_Found(t *testing.T) {
|
func TestPrivilegeHandler_GetCompanies_Email_Not_Found(t *testing.T) {
|
||||||
handler := New(WithBaseURL("base"))
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
@@ -109,11 +149,10 @@ func TestPrivilegeHandler_GetCompanies_Email_Not_Found(t *testing.T) {
|
|||||||
func TestPrivilegeHandler_GetCompanies_No_Companies_Found(t *testing.T) {
|
func TestPrivilegeHandler_GetCompanies_No_Companies_Found(t *testing.T) {
|
||||||
handler := New(WithBaseURL("base"))
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
result, err := handler.Process(&UserAdded{
|
err := handler.Process(&UserAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
||||||
@@ -128,11 +167,10 @@ func TestPrivilegeHandler_GetCompanies_No_Companies_Found(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, []string{"abc-123"}, companies)
|
assert.Equal(t, []string{"abc-123"}, companies)
|
||||||
|
|
||||||
result, err = handler.Process(&UserRemoved{
|
err = handler.Process(&UserRemoved{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
||||||
@@ -144,12 +182,11 @@ func TestPrivilegeHandler_GetCompanies_No_Companies_Found(t *testing.T) {
|
|||||||
func TestPrivilegeHandler_GetCompanies_Company_With_Company_Access_Found(t *testing.T) {
|
func TestPrivilegeHandler_GetCompanies_Company_With_Company_Access_Found(t *testing.T) {
|
||||||
handler := New(WithBaseURL("base"))
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
result, err := handler.Process(&PrivilegeAdded{
|
err := handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeCompany,
|
Privilege: PrivilegeCompany,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
||||||
@@ -162,12 +199,11 @@ func TestPrivilegeHandler_GetCompanies_Company_With_Company_Access_Found(t *test
|
|||||||
func TestPrivilegeHandler_GetCompanies_Company_With_Admin_Access_Found(t *testing.T) {
|
func TestPrivilegeHandler_GetCompanies_Company_With_Admin_Access_Found(t *testing.T) {
|
||||||
handler := New(WithBaseURL("base"))
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
result, err := handler.Process(&PrivilegeAdded{
|
err := handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeConsumer,
|
Privilege: PrivilegeConsumer,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
assert.Nil(t, result)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
|
||||||
@@ -190,11 +226,11 @@ func TestPrivilegeHandler_IsAllowed_Return_False_If_No_Privileges(t *testing.T)
|
|||||||
func TestPrivilegeHandler_IsAllowed_Return_True_If_Privilege_Exists(t *testing.T) {
|
func TestPrivilegeHandler_IsAllowed_Return_True_If_Privilege_Exists(t *testing.T) {
|
||||||
handler := New(WithBaseURL("base"))
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
_, _ = handler.Process(&PrivilegeAdded{
|
_ = handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeTime,
|
Privilege: PrivilegeTime,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
|
|
||||||
result := handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
result := handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
||||||
return privileges.Time
|
return privileges.Time
|
||||||
@@ -202,11 +238,11 @@ func TestPrivilegeHandler_IsAllowed_Return_True_If_Privilege_Exists(t *testing.T
|
|||||||
|
|
||||||
assert.True(t, result)
|
assert.True(t, result)
|
||||||
|
|
||||||
_, _ = handler.Process(&PrivilegeAdded{
|
_ = handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeInvoicing,
|
Privilege: PrivilegeInvoicing,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
|
|
||||||
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
||||||
return privileges.Invoicing
|
return privileges.Invoicing
|
||||||
@@ -214,11 +250,11 @@ func TestPrivilegeHandler_IsAllowed_Return_True_If_Privilege_Exists(t *testing.T
|
|||||||
|
|
||||||
assert.True(t, result)
|
assert.True(t, result)
|
||||||
|
|
||||||
_, _ = handler.Process(&PrivilegeAdded{
|
_ = handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeAccounting,
|
Privilege: PrivilegeAccounting,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
|
|
||||||
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
||||||
return privileges.Accounting
|
return privileges.Accounting
|
||||||
@@ -226,11 +262,11 @@ func TestPrivilegeHandler_IsAllowed_Return_True_If_Privilege_Exists(t *testing.T
|
|||||||
|
|
||||||
assert.True(t, result)
|
assert.True(t, result)
|
||||||
|
|
||||||
_, _ = handler.Process(&PrivilegeAdded{
|
_ = handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeSupplier,
|
Privilege: PrivilegeSupplier,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
|
|
||||||
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
||||||
return privileges.Supplier
|
return privileges.Supplier
|
||||||
@@ -238,11 +274,11 @@ func TestPrivilegeHandler_IsAllowed_Return_True_If_Privilege_Exists(t *testing.T
|
|||||||
|
|
||||||
assert.True(t, result)
|
assert.True(t, result)
|
||||||
|
|
||||||
_, _ = handler.Process(&PrivilegeAdded{
|
_ = handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: "abc-123",
|
CompanyID: "abc-123",
|
||||||
Privilege: PrivilegeSalary,
|
Privilege: PrivilegeSalary,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
|
|
||||||
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
|
||||||
return privileges.Salary
|
return privileges.Salary
|
||||||
@@ -522,11 +558,11 @@ func TestPrivilegeHandler_Concurrent_Process_And_Read(t *testing.T) {
|
|||||||
companyID := fmt.Sprintf("company-%d", i%10)
|
companyID := fmt.Sprintf("company-%d", i%10)
|
||||||
go func(id string) {
|
go func(id string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
_, _ = handler.Process(&PrivilegeAdded{
|
_ = handler.Process(&PrivilegeAdded{
|
||||||
Email: "jim@example.org",
|
Email: "jim@example.org",
|
||||||
CompanyID: id,
|
CompanyID: id,
|
||||||
Privilege: PrivilegeAdmin,
|
Privilege: PrivilegeAdmin,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
}(companyID)
|
}(companyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,11 +634,11 @@ func TestPrivilegeHandler_Concurrent_Multiple_Operations(t *testing.T) {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(idx int) {
|
go func(idx int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
_, _ = handler.Process(&PrivilegeAdded{
|
_ = handler.Process(&PrivilegeAdded{
|
||||||
Email: "jane@example.org",
|
Email: "jane@example.org",
|
||||||
CompanyID: fmt.Sprintf("company-%d", idx%5),
|
CompanyID: fmt.Sprintf("company-%d", idx%5),
|
||||||
Privilege: PrivilegeCompany,
|
Privilege: PrivilegeCompany,
|
||||||
}, goamqp.Headers{})
|
})
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,3 +684,49 @@ func TestPrivilegeHandler_Concurrent_Multiple_Operations(t *testing.T) {
|
|||||||
expectedJane := []string{"company-0", "company-1", "company-2", "company-3", "company-4"}
|
expectedJane := []string{"company-0", "company-1", "company-2", "company-3", "company-4"}
|
||||||
assert.Equal(t, expectedJane, janeCompanies)
|
assert.Equal(t, expectedJane, janeCompanies)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrivilegeHandler_Setup(t *testing.T) {
|
||||||
|
handler := New(WithBaseURL("base"))
|
||||||
|
|
||||||
|
topology, err := goamqp.CollectTopology("some-service", handler.Setup()...)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
wiring := map[string]string{}
|
||||||
|
for _, e := range topology.Endpoints {
|
||||||
|
assert.Equal(t, spec.DirectionConsume, e.Direction)
|
||||||
|
assert.True(t, e.Ephemeral, "%s must be a per-replica consumer", e.RoutingKey)
|
||||||
|
wiring[e.RoutingKey] = e.MessageType
|
||||||
|
}
|
||||||
|
// A key wired to the wrong type could turn a revocation into a grant.
|
||||||
|
assert.Equal(t, map[string]string{
|
||||||
|
"User.Added": "client.UserAdded",
|
||||||
|
"User.Removed": "client.UserRemoved",
|
||||||
|
"Privilege.Added": "client.PrivilegeAdded",
|
||||||
|
"Privilege.Removed": "client.PrivilegeRemoved",
|
||||||
|
}, wiring)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrivilegeHandler_process(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
handler := New(WithBaseURL("base"))
|
||||||
|
admin := func(p CompanyPrivileges) bool { return p.Admin }
|
||||||
|
|
||||||
|
assert.NoError(t, process[UserAdded](handler)(ctx, spec.ConsumableEvent[UserAdded]{
|
||||||
|
Payload: UserAdded{Email: "jim@example.org", CompanyID: "abc-123"},
|
||||||
|
}))
|
||||||
|
assert.False(t, handler.IsAllowed("jim@example.org", "abc-123", admin))
|
||||||
|
|
||||||
|
assert.NoError(t, process[PrivilegeAdded](handler)(ctx, spec.ConsumableEvent[PrivilegeAdded]{
|
||||||
|
Payload: PrivilegeAdded{Email: "jim@example.org", CompanyID: "abc-123", Privilege: PrivilegeAdmin},
|
||||||
|
}))
|
||||||
|
assert.True(t, handler.IsAllowed("jim@example.org", "abc-123", admin))
|
||||||
|
|
||||||
|
assert.NoError(t, process[PrivilegeRemoved](handler)(ctx, spec.ConsumableEvent[PrivilegeRemoved]{
|
||||||
|
Payload: PrivilegeRemoved{Email: "jim@example.org", CompanyID: "abc-123", Privilege: PrivilegeAdmin},
|
||||||
|
}))
|
||||||
|
assert.False(t, handler.IsAllowed("jim@example.org", "abc-123", admin))
|
||||||
|
|
||||||
|
assert.NoError(t, process[UserRemoved](handler)(ctx, spec.ConsumableEvent[UserRemoved]{
|
||||||
|
Payload: UserRemoved{Email: "jim@example.org", CompanyID: "abc-123"},
|
||||||
|
}))
|
||||||
|
assert.Empty(t, handler.CompaniesByUser("jim@example.org", func(CompanyPrivileges) bool { return true }))
|
||||||
|
}
|
||||||
@@ -78,3 +78,7 @@ filter_commits = false
|
|||||||
topo_order = false
|
topo_order = false
|
||||||
# sort the commits inside sections by oldest/newest order
|
# sort the commits inside sections by oldest/newest order
|
||||||
sort_commits = "oldest"
|
sort_commits = "oldest"
|
||||||
|
|
||||||
|
[bump]
|
||||||
|
# Before 1.0.0, a breaking change bumps the minor version instead of the major.
|
||||||
|
breaking_always_bump_major = false
|
||||||
@@ -1,19 +1,31 @@
|
|||||||
module gitea.unbound.se/shiny/authz_client
|
module gitea.unbound.se/shiny/authz_client
|
||||||
|
|
||||||
go 1.22.12
|
go 1.26.2
|
||||||
|
|
||||||
toolchain go1.26.1
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/sparetimecoders/goamqp v0.3.3
|
codeberg.org/messaging/go-messaging-amqp v0.0.5
|
||||||
github.com/stretchr/testify v1.11.1
|
codeberg.org/messaging/messaging v0.0.5
|
||||||
|
github.com/stretchr/testify v1.12.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
github.com/prometheus/common v0.66.1 // indirect
|
||||||
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
|
github.com/rabbitmq/amqp091-go v1.15.0 // indirect
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||||
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||||
|
golang.org/x/sys v0.45.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.8 // indirect
|
||||||
)
|
)
|
||||||
@@ -1,20 +1,72 @@
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
codeberg.org/messaging/go-messaging-amqp v0.0.5 h1:zzcJ+FVWgBPKyTkq9RXF11yvnhs8IN5BALBC+mps1zA=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
codeberg.org/messaging/go-messaging-amqp v0.0.5/go.mod h1:6bSCIkKH0V/oI7vaIq8ttINKYwov9c8ckZrwjfvp7kc=
|
||||||
|
codeberg.org/messaging/messaging v0.0.5 h1:/ueH90F4RNPUeeJIwdG9oVhDW7+XjCzwOYq8xc0kfQk=
|
||||||
|
codeberg.org/messaging/messaging v0.0.5/go.mod h1:xyWLUcfaVzcN6GWk9uaQsxPVUC2Vm2S89mYZGdiWTWg=
|
||||||
|
codeberg.org/messaging/messaging/tck v0.0.3 h1:a4Nr7ytFqEJloTOyVeAtMNgO9Fig1ZUirjW4FVlK0lc=
|
||||||
|
codeberg.org/messaging/messaging/tck v0.0.3/go.mod h1:RiOsKXGAhNQK2STBVYGjhN0CFtmcrsEne1qUe15n8Q4=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
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/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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/sparetimecoders/goamqp v0.3.3 h1:z/nfTPmrjeU/rIVuNOgsVLCimp3WFoNFvS3ZzXRJ6HE=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/sparetimecoders/goamqp v0.3.3/go.mod h1:W9NRCpWLE+Vruv2dcRSbszNil2O826d2Nv6kAkETW5o=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/nats-io/nats.go v1.52.0 h1:n3avV4VBsCgsdwh71TppsTwtv+QdPs7ntSKM8qJLGsc=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/nats-io/nats.go v1.52.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno=
|
||||||
|
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
|
||||||
|
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
|
||||||
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
|
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||||
|
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||||
|
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||||
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
|
github.com/rabbitmq/amqp091-go v1.15.0 h1:LEQL4/yp48/Wigt6A6XOu18RQRo8ZHtB5I/KZJn+gkw=
|
||||||
|
github.com/rabbitmq/amqp091-go v1.15.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||||
|
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/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||||
|
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||||
|
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/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||||
|
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||||
|
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||||
|
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||||
|
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||||
|
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||||
|
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||||
|
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||||
|
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||||
|
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||||
|
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
Reference in new issue
Block a user