You've already forked coredns-powerdns-api-wrapper
first commit
This commit is contained in:
174
auth_test.go
Normal file
174
auth_test.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// Copyright (C) 2026 Bryan Joshua Pedini
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// newTestRouter builds a WebServer with a minimal Config and wires the real
|
||||
// Routes()/middleware stack, without any DB (nil Store/DB) — for the pure
|
||||
// handler tests that never reach the Store (auth, unknown server_id, etc).
|
||||
func newTestRouter(cfg *Config) (*WebServer, *mux.Router) {
|
||||
ws := &WebServer{
|
||||
Config: cfg,
|
||||
Logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
||||
}
|
||||
r := mux.NewRouter()
|
||||
r.Use(ws.loggingMiddleware)
|
||||
ws.Routes(r)
|
||||
return ws, r
|
||||
}
|
||||
|
||||
func testConfig() *Config {
|
||||
return &Config{
|
||||
PDNSAPIKey: "test-secret-key",
|
||||
ListenAddr: ":3000",
|
||||
TablePrefix: "test_pdnsapi_",
|
||||
PDNSServerID: "localhost",
|
||||
DefaultTTL: 120,
|
||||
LogLevel: "info",
|
||||
PUID: 1000,
|
||||
PGID: 1000,
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthMiddleware(t *testing.T) {
|
||||
cfg := testConfig()
|
||||
_, r := newTestRouter(cfg)
|
||||
srv := httptest.NewServer(r)
|
||||
defer srv.Close()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
headerKey string
|
||||
sendHeader bool
|
||||
wantStatus int
|
||||
wantBody bool
|
||||
}{
|
||||
{"missing key", "", false, http.StatusUnauthorized, true},
|
||||
{"wrong key", "wrong-key", true, http.StatusUnauthorized, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", srv.URL+"/api/v1/servers/localhost/zones", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tt.sendHeader {
|
||||
req.Header.Set("X-API-Key", tt.headerKey)
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != tt.wantStatus {
|
||||
t.Fatalf("status = %d, want %d", resp.StatusCode, tt.wantStatus)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantBody := `{"error":"Unauthorized"}` + "\n"
|
||||
if string(body) != wantBody {
|
||||
t.Fatalf("body = %q, want %q", string(body), wantBody)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthMiddleware_CorrectKeyPassesThrough(t *testing.T) {
|
||||
// Correct key should reach the handler (which will fail past auth since
|
||||
// there is no Store — but it must NOT be a 401). Unknown server_id also
|
||||
// checked before Store is touched, so use that to prove auth passed.
|
||||
cfg := testConfig()
|
||||
_, r := newTestRouter(cfg)
|
||||
srv := httptest.NewServer(r)
|
||||
defer srv.Close()
|
||||
|
||||
req, err := http.NewRequest("GET", srv.URL+"/api/v1/servers/not-localhost/zones", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("X-API-Key", cfg.PDNSAPIKey)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusUnauthorized {
|
||||
t.Fatalf("correct key was rejected with 401")
|
||||
}
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Fatalf("status = %d, want %d (unknown server_id, proves auth passed)", resp.StatusCode, http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnknownServerID(t *testing.T) {
|
||||
cfg := testConfig()
|
||||
_, r := newTestRouter(cfg)
|
||||
srv := httptest.NewServer(r)
|
||||
defer srv.Close()
|
||||
|
||||
paths := []struct {
|
||||
method string
|
||||
path string
|
||||
}{
|
||||
{"GET", "/api/v1/servers/bogus/zones"},
|
||||
{"GET", "/api/v1/servers/bogus/zones/example.com."},
|
||||
{"PATCH", "/api/v1/servers/bogus/zones/example.com."},
|
||||
{"PUT", "/api/v1/servers/bogus/zones/example.com./notify"},
|
||||
}
|
||||
|
||||
for _, p := range paths {
|
||||
t.Run(p.method+" "+p.path, func(t *testing.T) {
|
||||
req, err := http.NewRequest(p.method, srv.URL+p.path, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("X-API-Key", cfg.PDNSAPIKey)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Fatalf("status = %d, want 404", resp.StatusCode)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
want := `{"error":"Not Found"}` + "\n"
|
||||
if string(body) != want {
|
||||
t.Fatalf("body = %q, want %q", string(body), want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteJSONError(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
writeJSONError(w, http.StatusTeapot, "custom message")
|
||||
|
||||
if w.Code != http.StatusTeapot {
|
||||
t.Fatalf("status = %d, want %d", w.Code, http.StatusTeapot)
|
||||
}
|
||||
if ct := w.Header().Get("Content-Type"); ct != "application/json" {
|
||||
t.Fatalf("Content-Type = %q, want application/json", ct)
|
||||
}
|
||||
want := `{"error":"custom message"}` + "\n"
|
||||
if w.Body.String() != want {
|
||||
t.Fatalf("body = %q, want %q", w.Body.String(), want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user