more otel
This commit is contained in:
1 parent
9620e3bfa6
commit
21f04d696b
88 files changed
+23419
-192
No files matched your search
@@ -0,0 +1,165 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestAudienceUnmarshal(t *testing.T) {
|
||||
var a1 Audience
|
||||
if err := json.Unmarshal([]byte(`"single-aud"`), &a1); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !a1.Contains("single-aud") || a1.Contains("other") {
|
||||
t.Fatalf("unexpected audience content: %v", a1)
|
||||
}
|
||||
|
||||
var a2 Audience
|
||||
if err := json.Unmarshal([]byte(`["aud1", "aud2"]`), &a2); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !a2.Contains("aud1") || !a2.Contains("aud2") || a2.Contains("aud3") {
|
||||
t.Fatalf("unexpected audience content: %v", a2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyToken(t *testing.T) {
|
||||
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate RSA key: %v", err)
|
||||
}
|
||||
|
||||
auth := &OidcAuth{
|
||||
issuer: "https://test.issuer",
|
||||
client: "test-client",
|
||||
keys: map[string]*rsa.PublicKey{
|
||||
"key-1": &privKey.PublicKey,
|
||||
},
|
||||
fetched: time.Now(),
|
||||
}
|
||||
|
||||
// Helper to mint test token
|
||||
mintToken := func(kid string, claims IDClaims) string {
|
||||
headerJSON, _ := json.Marshal(map[string]string{
|
||||
"kid": kid,
|
||||
"alg": "RS256",
|
||||
})
|
||||
claimsJSON, _ := json.Marshal(claims)
|
||||
|
||||
headerB64 := base64.RawURLEncoding.EncodeToString(headerJSON)
|
||||
payloadB64 := base64.RawURLEncoding.EncodeToString(claimsJSON)
|
||||
|
||||
sum := sha256.Sum256([]byte(headerB64 + "." + payloadB64))
|
||||
sig, _ := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA256, sum[:])
|
||||
sigB64 := base64.RawURLEncoding.EncodeToString(sig)
|
||||
|
||||
return headerB64 + "." + payloadB64 + "." + sigB64
|
||||
}
|
||||
|
||||
// Valid token
|
||||
validToken := mintToken("key-1", IDClaims{
|
||||
Iss: "https://test.issuer",
|
||||
Sub: "user-123",
|
||||
Aud: Audience{"test-client"},
|
||||
Exp: time.Now().Add(time.Hour).Unix(),
|
||||
})
|
||||
|
||||
claims, err := auth.VerifyToken(context.Background(), validToken)
|
||||
if err != nil {
|
||||
t.Fatalf("expected valid token to pass, got: %v", err)
|
||||
}
|
||||
if claims.Sub != "user-123" {
|
||||
t.Fatalf("expected sub user-123, got %s", claims.Sub)
|
||||
}
|
||||
|
||||
// Expired token
|
||||
expiredToken := mintToken("key-1", IDClaims{
|
||||
Iss: "https://test.issuer",
|
||||
Sub: "user-123",
|
||||
Aud: Audience{"test-client"},
|
||||
Exp: time.Now().Add(-time.Hour).Unix(),
|
||||
})
|
||||
if _, err := auth.VerifyToken(context.Background(), expiredToken); err == nil {
|
||||
t.Fatalf("expected expired token to fail")
|
||||
}
|
||||
|
||||
// Wrong audience
|
||||
wrongAudToken := mintToken("key-1", IDClaims{
|
||||
Iss: "https://test.issuer",
|
||||
Sub: "user-123",
|
||||
Aud: Audience{"other-client"},
|
||||
Exp: time.Now().Add(time.Hour).Unix(),
|
||||
})
|
||||
if _, err := auth.VerifyToken(context.Background(), wrongAudToken); err == nil {
|
||||
t.Fatalf("expected wrong audience to fail")
|
||||
}
|
||||
|
||||
// Wrong issuer
|
||||
wrongIssToken := mintToken("key-1", IDClaims{
|
||||
Iss: "https://wrong.issuer",
|
||||
Sub: "user-123",
|
||||
Aud: Audience{"test-client"},
|
||||
Exp: time.Now().Add(time.Hour).Unix(),
|
||||
})
|
||||
if _, err := auth.VerifyToken(context.Background(), wrongIssToken); err == nil {
|
||||
t.Fatalf("expected wrong issuer to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireAuthMiddleware(t *testing.T) {
|
||||
auth := &OidcAuth{
|
||||
issuer: "https://test.issuer",
|
||||
client: "test-client",
|
||||
ingestKey: "secret-key",
|
||||
agentToken: "agent-secret",
|
||||
}
|
||||
|
||||
handler := auth.RequireAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
}))
|
||||
|
||||
// 1. Unauthenticated request -> 401
|
||||
req := httptest.NewRequest("GET", "/api/history", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("expected 401 for unauthenticated request, got %d", rec.Code)
|
||||
}
|
||||
|
||||
// 2. Ingest key in header -> 200
|
||||
reqKey := httptest.NewRequest("GET", "/api/history", nil)
|
||||
reqKey.Header.Set("X-Otel-Ingest-Key", "secret-key")
|
||||
recKey := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recKey, reqKey)
|
||||
if recKey.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200 with valid ingest key, got %d", recKey.Code)
|
||||
}
|
||||
|
||||
// 3. Dedicated service token in a header -> 200
|
||||
reqAgent := httptest.NewRequest("GET", "/api/history", nil)
|
||||
reqAgent.Header.Set("X-Otel-Agent-Token", "agent-secret")
|
||||
recAgent := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recAgent, reqAgent)
|
||||
if recAgent.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200 with valid agent token, got %d", recAgent.Code)
|
||||
}
|
||||
|
||||
// 4. X-Auth-User from upstream ingress -> 200
|
||||
reqAuthUser := httptest.NewRequest("GET", "/api/history", nil)
|
||||
reqAuthUser.Header.Set("X-Auth-User", "mats@tornberg.me")
|
||||
recAuthUser := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recAuthUser, reqAuthUser)
|
||||
if recAuthUser.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200 with X-Auth-User, got %d", recAuthUser.Code)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user