network/pkg/httputil/auth_test.go
2026-01-20 10:03:55 +02:00

335 lines
6.5 KiB
Go

package httputil
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestExtractBearerToken(t *testing.T) {
tests := []struct {
name string
header string
want string
}{
{
name: "valid bearer token",
header: "Bearer abc123",
want: "abc123",
},
{
name: "case insensitive",
header: "bearer xyz789",
want: "xyz789",
},
{
name: "with extra spaces",
header: "Bearer token-with-spaces ",
want: "token-with-spaces",
},
{
name: "no bearer scheme",
header: "Basic abc123",
want: "",
},
{
name: "empty header",
header: "",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
if tt.header != "" {
req.Header.Set("Authorization", tt.header)
}
if got := ExtractBearerToken(req); got != tt.want {
t.Errorf("ExtractBearerToken() = %v, want %v", got, tt.want)
}
})
}
}
func TestExtractAPIKey(t *testing.T) {
tests := []struct {
name string
header string
xapi string
query string
want string
}{
{
name: "X-API-Key header (priority)",
xapi: "key-from-header",
want: "key-from-header",
},
{
name: "ApiKey scheme",
header: "ApiKey my-api-key",
want: "my-api-key",
},
{
name: "Bearer with non-JWT token",
header: "Bearer simple-token",
want: "simple-token",
},
{
name: "Bearer with JWT (should skip)",
header: "Bearer eyJ.abc.xyz",
want: "",
},
{
name: "query parameter api_key",
query: "?api_key=query-key",
want: "query-key",
},
{
name: "query parameter token",
query: "?token=token-key",
want: "token-key",
},
{
name: "X-API-Key takes priority over Authorization",
xapi: "xapi-key",
header: "Bearer bearer-key",
want: "xapi-key",
},
{
name: "no auth",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
url := "/"
if tt.query != "" {
url += tt.query
}
req := httptest.NewRequest(http.MethodGet, url, nil)
if tt.header != "" {
req.Header.Set("Authorization", tt.header)
}
if tt.xapi != "" {
req.Header.Set("X-API-Key", tt.xapi)
}
if got := ExtractAPIKey(req); got != tt.want {
t.Errorf("ExtractAPIKey() = %v, want %v", got, tt.want)
}
})
}
}
func TestIsJWT(t *testing.T) {
tests := []struct {
name string
token string
want bool
}{
{
name: "valid JWT structure",
token: "header.payload.signature",
want: true,
},
{
name: "real JWT",
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
want: true,
},
{
name: "not a JWT - no dots",
token: "simple-token",
want: false,
},
{
name: "not a JWT - one dot",
token: "part1.part2",
want: false,
},
{
name: "not a JWT - three dots",
token: "a.b.c.d",
want: false,
},
{
name: "empty string",
token: "",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsJWT(tt.token); got != tt.want {
t.Errorf("IsJWT(%q) = %v, want %v", tt.token, got, tt.want)
}
})
}
}
func TestExtractNamespaceHeader(t *testing.T) {
tests := []struct {
name string
header string
want string
}{
{
name: "valid namespace",
header: "my-namespace",
want: "my-namespace",
},
{
name: "with whitespace",
header: " my-namespace ",
want: "my-namespace",
},
{
name: "empty header",
header: "",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
if tt.header != "" {
req.Header.Set("X-Namespace", tt.header)
}
if got := ExtractNamespaceHeader(req); got != tt.want {
t.Errorf("ExtractNamespaceHeader() = %v, want %v", got, tt.want)
}
})
}
}
func TestExtractWalletHeader(t *testing.T) {
tests := []struct {
name string
header string
want string
}{
{
name: "valid wallet",
header: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbC",
want: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbC",
},
{
name: "with whitespace",
header: " 0x742d35Cc ",
want: "0x742d35Cc",
},
{
name: "empty header",
header: "",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
if tt.header != "" {
req.Header.Set("X-Wallet", tt.header)
}
if got := ExtractWalletHeader(req); got != tt.want {
t.Errorf("ExtractWalletHeader() = %v, want %v", got, tt.want)
}
})
}
}
func TestHasAuthHeader(t *testing.T) {
tests := []struct {
name string
header string
want bool
}{
{
name: "has auth header",
header: "Bearer token",
want: true,
},
{
name: "no auth header",
header: "",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
if tt.header != "" {
req.Header.Set("Authorization", tt.header)
}
if got := HasAuthHeader(req); got != tt.want {
t.Errorf("HasAuthHeader() = %v, want %v", got, tt.want)
}
})
}
}
func TestExtractBasicAuth(t *testing.T) {
tests := []struct {
name string
header string
wantUsername string
wantPassword string
wantOK bool
}{
{
name: "valid basic auth",
header: "Basic " + basicAuth("user", "pass"),
wantUsername: "user",
wantPassword: "pass",
wantOK: true,
},
{
name: "no auth header",
header: "",
wantOK: false,
},
{
name: "bearer token",
header: "Bearer token",
wantOK: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
if tt.header != "" {
req.Header.Set("Authorization", tt.header)
}
username, password, ok := ExtractBasicAuth(req)
if ok != tt.wantOK {
t.Errorf("ExtractBasicAuth() ok = %v, want %v", ok, tt.wantOK)
}
if ok {
if username != tt.wantUsername {
t.Errorf("ExtractBasicAuth() username = %v, want %v", username, tt.wantUsername)
}
if password != tt.wantPassword {
t.Errorf("ExtractBasicAuth() password = %v, want %v", password, tt.wantPassword)
}
}
})
}
}
// Helper function to create basic auth header
func basicAuth(username, password string) string {
return EncodeBase64([]byte(username + ":" + password))
}