mirror of
https://github.com/hanzo-fi/gateway.git
synced 2026-10-30 20:34:40 +00:00
134 lines
3.9 KiB
Go
134 lines
3.9 KiB
Go
//go:build it
|
|
|
|
package e2e_test
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/testing/deferred"
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/testing/platform/natstesting"
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/audit"
|
|
"github.com/nats-io/nats.go"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("Audit", func() {
|
|
var topicName = "audit-e2e"
|
|
|
|
Context("basic audit flow", Ordered, func() {
|
|
var (
|
|
backend *backendRecorder
|
|
gatewayURL string
|
|
messages chan *nats.Msg
|
|
)
|
|
|
|
BeforeAll(func(specContext SpecContext) {
|
|
natsURL := deferred.Map(natsServer, (*natstesting.NatsServer).ClientURL)
|
|
url, err := natsURL.Wait(specContext)
|
|
Expect(err).To(BeNil())
|
|
|
|
backend = startBackend()
|
|
gatewayURL = startGateway(url, backend.Server.Listener.Addr().String(), topicName)
|
|
messages = subscribeToAuditTopic(url, topicName)
|
|
})
|
|
|
|
When("a request is proxied through the gateway", func() {
|
|
var resp *http.Response
|
|
|
|
BeforeEach(func() {
|
|
req, err := http.NewRequest("POST", gatewayURL+"/api/test",
|
|
strings.NewReader(`{"hello":"world"}`))
|
|
Expect(err).To(BeNil())
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err = http.DefaultClient.Do(req)
|
|
Expect(err).To(BeNil())
|
|
defer func() { _ = resp.Body.Close() }()
|
|
})
|
|
|
|
It("should return a successful response", func() {
|
|
Expect(resp.StatusCode).To(Equal(http.StatusOK))
|
|
})
|
|
|
|
It("should publish an audit event to NATS", func() {
|
|
Eventually(messages).Should(Receive(Satisfy(func(msg *nats.Msg) bool {
|
|
payload := parseAuditPayload(msg)
|
|
return payload.HTTP.Request.Method == "POST" &&
|
|
payload.HTTP.Request.Path == "/api/test" &&
|
|
payload.HTTP.Response.StatusCode == http.StatusOK &&
|
|
payload.Actor.OrganizationID == "test-org" &&
|
|
payload.Actor.StackID == "test-stack"
|
|
})))
|
|
})
|
|
|
|
It("should set the X-Formance-Audit header on the proxied request", func() {
|
|
Expect(backend.LastRequest()).NotTo(BeNil())
|
|
Expect(backend.LastRequest().Header.Get(audit.HandledHeader)).To(Equal("true"))
|
|
})
|
|
|
|
It("should strip the Authorization header from the audit payload", func() {
|
|
req, err := http.NewRequest("GET", gatewayURL+"/api/auth-check", nil)
|
|
Expect(err).To(BeNil())
|
|
req.Header.Set("Authorization", "Bearer some-secret-token")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
Expect(err).To(BeNil())
|
|
_ = resp.Body.Close()
|
|
|
|
Eventually(messages).Should(Receive(Satisfy(func(msg *nats.Msg) bool {
|
|
payload := parseAuditPayload(msg)
|
|
return payload.HTTP.Request.Path == "/api/auth-check" &&
|
|
payload.HTTP.Request.Header.Get("Authorization") == ""
|
|
})))
|
|
})
|
|
})
|
|
|
|
When("a request already has the audit header", func() {
|
|
It("should skip audit and not publish an event", func() {
|
|
// Drain any pending messages
|
|
for {
|
|
select {
|
|
case <-messages:
|
|
default:
|
|
goto drained
|
|
}
|
|
}
|
|
drained:
|
|
|
|
req, err := http.NewRequest("GET", gatewayURL+"/api/skip-me", nil)
|
|
Expect(err).To(BeNil())
|
|
req.Header.Set(audit.HandledHeader, "true")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
Expect(err).To(BeNil())
|
|
_ = resp.Body.Close()
|
|
|
|
Expect(resp.StatusCode).To(Equal(http.StatusOK))
|
|
|
|
Consistently(messages, 500*time.Millisecond).ShouldNot(Receive())
|
|
})
|
|
})
|
|
|
|
When("the audit header is not in the captured payload", func() {
|
|
It("should not leak the audit header into the audit event", func() {
|
|
req, err := http.NewRequest("GET", gatewayURL+"/api/no-leak", nil)
|
|
Expect(err).To(BeNil())
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
Expect(err).To(BeNil())
|
|
_ = resp.Body.Close()
|
|
|
|
Eventually(messages).Should(Receive(Satisfy(func(msg *nats.Msg) bool {
|
|
payload := parseAuditPayload(msg)
|
|
return payload.HTTP.Request.Path == "/api/no-leak" &&
|
|
payload.HTTP.Request.Header.Get(audit.HandledHeader) == ""
|
|
})))
|
|
})
|
|
})
|
|
})
|
|
})
|