faas/gateway/handlers/external_auth_test.go
Alex Ellis e3c976a428 Fix error handling for ExternalAuth
This corrects an issue where the error body was being hidden
for the external auth handler. It also adds the ca-certs into
the runtime Docker image for when the gateway is calling an
external plugin exposed over HTTPS.

Tested with OAuth2 plugin.

Signed-off-by: Alex Ellis <alexellis2@gmail.com>
2019-06-14 19:17:25 +01:00

240 lines
6.7 KiB
Go

package handlers
import (
"bytes"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func Test_External_Auth_Wrapper_FailsInvalidAuth(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusForbidden)
}))
defer s.Close()
next := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
passBody := false
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
rr := httptest.NewRecorder()
handler(rr, req)
if rr.Code == http.StatusOK {
t.Errorf("Status incorrect, did not want: %d, but got %d", http.StatusOK, rr.Code)
}
}
func Test_External_Auth_Wrapper_FailsInvalidAuth_WritesBody(t *testing.T) {
wantBody := []byte(`invalid credentials`)
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusForbidden)
w.Write(wantBody)
}))
defer s.Close()
next := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
passBody := false
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
rr := httptest.NewRecorder()
handler(rr, req)
if rr.Code == http.StatusOK {
t.Errorf("Status incorrect, did not want: %d, but got %d", http.StatusOK, rr.Code)
}
if bytes.Compare(rr.Body.Bytes(), wantBody) != 0 {
t.Errorf("Body incorrect, want: %s, but got %s", []byte(wantBody), rr.Body)
}
}
func Test_External_Auth_Wrapper_PassesValidAuth(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer s.Close()
next := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
passBody := false
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
rr := httptest.NewRecorder()
handler(rr, req)
want := http.StatusNotImplemented
if rr.Code != want {
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
}
}
func Test_External_Auth_Wrapper_WithoutRequiredHeaderFailsAuth(t *testing.T) {
wantToken := "secret-key"
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Token") == wantToken {
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusUnauthorized)
}))
defer s.Close()
next := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
passBody := false
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
// use an invalid token
req.Header.Set("X-Token", "invalid-key")
rr := httptest.NewRecorder()
handler(rr, req)
want := http.StatusUnauthorized
if rr.Code != want {
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
}
}
func Test_External_Auth_Wrapper_WithoutRequiredHeaderFailsAuth_ProxiesServerHeaders(t *testing.T) {
wantToken := "secret-key"
wantRealm := `Basic realm="Restricted"`
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Token") == wantToken {
w.WriteHeader(http.StatusOK)
return
}
w.Header().Set("Www-Authenticate", wantRealm)
w.WriteHeader(http.StatusUnauthorized)
}))
defer s.Close()
next := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
passBody := false
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
// use an invalid token
req.Header.Set("X-Token", "invalid-key")
rr := httptest.NewRecorder()
handler(rr, req)
want := http.StatusUnauthorized
if rr.Code != want {
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
}
got := rr.Header().Get("Www-Authenticate")
if got != wantRealm {
t.Errorf("Www-Authenticate header, want: %s, but got %s, %q", wantRealm, got, rr.Header())
}
}
func Test_External_Auth_Wrapper_WithRequiredHeaderPassesValidAuth(t *testing.T) {
wantToken := "secret-key"
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Token") == wantToken {
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusUnauthorized)
}))
defer s.Close()
next := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
passBody := false
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
req.Header.Set("X-Token", wantToken)
rr := httptest.NewRecorder()
handler(rr, req)
want := http.StatusNotImplemented
if rr.Code != want {
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
}
}
func Test_External_Auth_Wrapper_TimeoutGivesInternalServerError(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(50 * time.Millisecond)
w.WriteHeader(http.StatusOK)
}))
defer s.Close()
next := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}
passBody := false
handler := MakeExternalAuthHandler(next, time.Millisecond*10, s.URL, passBody)
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
rr := httptest.NewRecorder()
handler(rr, req)
want := http.StatusInternalServerError
if rr.Code != want {
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
}
wantSubstring := "context deadline exceeded\n"
if !strings.HasSuffix(string(rr.Body.Bytes()), wantSubstring) {
t.Errorf("Body incorrect, want to have suffix: %q, but got %q", []byte(wantSubstring), rr.Body)
}
}
// // Test_External_Auth_Wrapper_PassesValidAuthButOnly200IsValid this test exists
// // to document the TODO action to consider all "2xx" statuses as valid.
// func Test_External_Auth_Wrapper_PassesValidAuthButOnly200IsValid(t *testing.T) {
// s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// w.WriteHeader(http.StatusAccepted)
// }))
// defer s.Close()
// next := func(w http.ResponseWriter, r *http.Request) {
// w.WriteHeader(http.StatusNotImplemented)
// }
// passBody := false
// handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
// req := httptest.NewRequest(http.MethodGet, s.URL, nil)
// rr := httptest.NewRecorder()
// handler(rr, req)
// want := http.StatusUnauthorized
// if rr.Code != want {
// t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
// }
// }