From b0aabba6a444c6f6c1cad095e1da37520e56f13d Mon Sep 17 00:00:00 2001 From: joernchen Date: Sat, 14 Dec 2024 19:04:06 +0100 Subject: [PATCH] Address review feedback --- exporter/http.go | 4 +- exporter/http_test.go | 104 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/exporter/http.go b/exporter/http.go index fd7afa9c..1bfe5934 100644 --- a/exporter/http.go +++ b/exporter/http.go @@ -14,9 +14,9 @@ import ( ) func (e *Exporter) ServeHTTP(w http.ResponseWriter, r *http.Request) { - err := e.verifyBasicAuth(r.BasicAuth()) - if err != nil { + if err := e.verifyBasicAuth(r.BasicAuth()); err != nil { w.Header().Set("WWW-Authenticate", `Basic realm="redis-exporter, charset=UTF-8"`) + http.Error(w, err.Error(), http.StatusUnauthorized) return } diff --git a/exporter/http_test.go b/exporter/http_test.go index 3b067435..70c709bd 100644 --- a/exporter/http_test.go +++ b/exporter/http_test.go @@ -522,6 +522,110 @@ func TestVerifyBasicAuth(t *testing.T) { } } +func TestBasicAuth(t *testing.T) { + if os.Getenv("TEST_REDIS_URI") == "" { + t.Skipf("TEST_REDIS_URI not set - skipping") + } + + tests := []struct { + name string + username string + password string + configUsername string + configPassword string + wantStatusCode int + }{ + { + name: "No auth configured - no credentials provided", + username: "", + password: "", + configUsername: "", + configPassword: "", + wantStatusCode: http.StatusOK, + }, + { + name: "Auth configured - correct credentials", + username: "testuser", + password: "testpass", + configUsername: "testuser", + configPassword: "testpass", + wantStatusCode: http.StatusOK, + }, + { + name: "Auth configured - wrong username", + username: "wronguser", + password: "testpass", + configUsername: "testuser", + configPassword: "testpass", + wantStatusCode: http.StatusUnauthorized, + }, + { + name: "Auth configured - wrong password", + username: "testuser", + password: "wrongpass", + configUsername: "testuser", + configPassword: "testpass", + wantStatusCode: http.StatusUnauthorized, + }, + { + name: "Auth configured - no credentials provided", + username: "", + password: "", + configUsername: "testuser", + configPassword: "testpass", + wantStatusCode: http.StatusUnauthorized, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e, _ := NewRedisExporter(os.Getenv("TEST_REDIS_URI"), Options{ + Namespace: "test", + Registry: prometheus.NewRegistry(), + BasicAuthUsername: tt.configUsername, + BasicAuthPassword: tt.configPassword, + }) + ts := httptest.NewServer(e) + defer ts.Close() + + client := &http.Client{} + req, err := http.NewRequest("GET", ts.URL+"/metrics", nil) + if err != nil { + t.Fatalf("Failed to create request: %v", err) + } + + if tt.username != "" || tt.password != "" { + req.SetBasicAuth(tt.username, tt.password) + } + + resp, err := client.Do(req) + if err != nil { + t.Fatalf("Failed to send request: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != tt.wantStatusCode { + t.Errorf("Expected status code %d, got %d", tt.wantStatusCode, resp.StatusCode) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("Failed to read response body: %v", err) + } + + if tt.wantStatusCode == http.StatusOK { + if !strings.Contains(string(body), "test_up") { + t.Errorf("Expected body to contain 'test_up', got: %s", string(body)) + } + } else { + if !strings.Contains(resp.Header.Get("WWW-Authenticate"), "Basic realm=\"redis-exporter") { + t.Errorf("Expected WWW-Authenticate header, got: %s", resp.Header.Get("WWW-Authenticate")) + } + } + }) + } +} + func downloadURL(t *testing.T, u string) string { _, res := downloadURLWithStatusCode(t, u) return res