Files
commerce/pkg/auth/middleware_test.go

174 lines
4.5 KiB
Go

// Copyright (c) 2014-present Hanzo AI, Inc.
// Licensed under MIT OR Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.
package auth
import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/zap-proto/zip"
)
func TestRequireIdentity(t *testing.T) {
cases := []struct {
name string
require bool
headers map[string]string
wantCode int
wantOrg string
wantUser string
wantEmail string
wantNext bool
}{
{
name: "headers present, require true",
require: true,
headers: map[string]string{
HeaderOrgID: "hanzo",
HeaderUserID: "user-123",
HeaderUserEmail: "[email protected]",
},
wantCode: http.StatusOK,
wantOrg: "hanzo",
wantUser: "user-123",
wantEmail: "[email protected]",
wantNext: true,
},
{
name: "missing headers, require true → 401",
require: true,
headers: nil,
wantCode: http.StatusUnauthorized,
wantNext: false,
},
{
name: "missing headers, require false → next with empty ctx",
require: false,
headers: nil,
wantCode: http.StatusOK,
wantNext: true,
},
{
name: "partial headers (only org), require true → allowed",
require: true,
headers: map[string]string{HeaderOrgID: "lux"},
wantCode: http.StatusOK,
wantOrg: "lux",
wantNext: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var (
called bool
gotOrg string
gotUser string
gotEmail string
)
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
called = true
gotOrg = OrgID(r.Context())
gotUser = UserID(r.Context())
gotEmail = UserEmail(r.Context())
w.WriteHeader(http.StatusOK)
})
handler := RequireIdentity(tc.require)(next)
req := httptest.NewRequest(http.MethodGet, "/v1/commerce/health", nil)
for k, v := range tc.headers {
req.Header.Set(k, v)
}
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != tc.wantCode {
t.Fatalf("status: want %d got %d (body=%q)", tc.wantCode, rec.Code, rec.Body.String())
}
if called != tc.wantNext {
t.Fatalf("next called: want %v got %v", tc.wantNext, called)
}
if gotOrg != tc.wantOrg {
t.Fatalf("org: want %q got %q", tc.wantOrg, gotOrg)
}
if gotUser != tc.wantUser {
t.Fatalf("user: want %q got %q", tc.wantUser, gotUser)
}
if gotEmail != tc.wantEmail {
t.Fatalf("email: want %q got %q", tc.wantEmail, gotEmail)
}
})
}
}
func TestIdentity(t *testing.T) {
app := zip.New(zip.Config{DisableStartupMessage: true})
app.Use(Identity(true))
app.Raw(http.MethodGet, "/x", func(c *zip.Ctx) error {
return c.String(http.StatusOK, OrgID(c.Context())+":"+UserID(c.Context()))
})
t.Run("401 without identity", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/x", nil)
resp, err := app.Test(req)
if err != nil {
t.Fatalf("test request: %v", err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Fatalf("want 401 got %d", resp.StatusCode)
}
})
t.Run("200 with identity", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/x", nil)
req.Header.Set(HeaderOrgID, "hanzo")
req.Header.Set(HeaderUserID, "u1")
resp, err := app.Test(req)
if err != nil {
t.Fatalf("test request: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("want 200 got %d", resp.StatusCode)
}
body, _ := io.ReadAll(resp.Body)
if string(body) != "hanzo:u1" {
t.Fatalf("body: want hanzo:u1 got %q", string(body))
}
})
}
// An X-Org-Id header names an org; it authenticates nobody. Only IAMTokenRequired,
// which requires the validated X-User-Id, marks a request iam_authenticated.
func TestIdentityOrgHeaderIsNotAuthentication(t *testing.T) {
app := zip.New(zip.Config{DisableStartupMessage: true})
app.Use(Identity(false))
app.Raw(http.MethodGet, "/x", func(c *zip.Ctx) error {
if b, _ := c.Locals("iam_authenticated").(bool); b {
return c.String(http.StatusOK, "authenticated")
}
return c.String(http.StatusOK, "anonymous")
})
req := httptest.NewRequest(http.MethodGet, "/x", nil)
req.Header.Set(HeaderOrgID, "victim")
resp, err := app.Test(req)
if err != nil {
t.Fatalf("test request: %v", err)
}
body, _ := io.ReadAll(resp.Body)
if string(body) != "anonymous" {
t.Fatalf("X-Org-Id alone: got %q, want anonymous", body)
}
}
func TestAccessorsEmptyContext(t *testing.T) {
ctx := context.Background()
if OrgID(ctx) != "" || UserID(ctx) != "" || UserEmail(ctx) != "" {
t.Fatalf("expected all-empty accessors on bare context")
}
}