From ef7fd18e57d115351cff654d7bbe02965ee7dc0a Mon Sep 17 00:00:00 2001
From: Will McCutchen <will@mccutch.org>
Date: Sat, 15 Oct 2016 16:50:36 -0700
Subject: [PATCH] Add /hidden-basic-auth/:user/:pass handler

---
 httpbin/handlers.go      | 26 ++++++++++++++++
 httpbin/handlers_test.go | 64 ++++++++++++++++++++++++++++++++++++++++
 httpbin/httpbin.go       |  2 ++
 3 files changed, 92 insertions(+)

diff --git a/httpbin/handlers.go b/httpbin/handlers.go
index 13754e6..d9e48d7 100644
--- a/httpbin/handlers.go
+++ b/httpbin/handlers.go
@@ -310,3 +310,29 @@ func (h *HTTPBin) BasicAuth(w http.ResponseWriter, r *http.Request) {
 	})
 	writeJSON(w, body, status)
 }
+
+// HiddenBasicAuth requires HTTP Basic authentication but returns a status of
+// 404 if the request is unauthorized
+func (h *HTTPBin) HiddenBasicAuth(w http.ResponseWriter, r *http.Request) {
+	parts := strings.Split(r.URL.Path, "/")
+	if len(parts) != 4 {
+		http.Error(w, "Not Found", http.StatusNotFound)
+		return
+	}
+	expectedUser := parts[2]
+	expectedPass := parts[3]
+
+	givenUser, givenPass, _ := r.BasicAuth()
+
+	authorized := givenUser == expectedUser && givenPass == expectedPass
+	if !authorized {
+		http.Error(w, "Not Found", http.StatusNotFound)
+		return
+	}
+
+	body, _ := json.Marshal(&authResponse{
+		Authorized: authorized,
+		User:       givenUser,
+	})
+	writeJSON(w, body, http.StatusOK)
+}
diff --git a/httpbin/handlers_test.go b/httpbin/handlers_test.go
index 6698bcb..721a417 100644
--- a/httpbin/handlers_test.go
+++ b/httpbin/handlers_test.go
@@ -992,3 +992,67 @@ func TestBasicAuth(t *testing.T) {
 		})
 	}
 }
+
+func TestHiddenBasicAuth(t *testing.T) {
+	t.Run("ok", func(t *testing.T) {
+		r, _ := http.NewRequest("GET", "/hidden-basic-auth/user/pass", nil)
+		r.SetBasicAuth("user", "pass")
+		w := httptest.NewRecorder()
+		handler.ServeHTTP(w, r)
+
+		assertStatusCode(t, w, http.StatusOK)
+		assertContentType(t, w, jsonContentType)
+
+		resp := &authResponse{}
+		json.Unmarshal(w.Body.Bytes(), resp)
+
+		expectedResp := &authResponse{
+			Authorized: true,
+			User:       "user",
+		}
+		if !reflect.DeepEqual(resp, expectedResp) {
+			t.Fatalf("expected response %#v, got %#v", expectedResp, resp)
+		}
+	})
+
+	t.Run("error/no auth", func(t *testing.T) {
+		r, _ := http.NewRequest("GET", "/hidden-basic-auth/user/pass", nil)
+		w := httptest.NewRecorder()
+		handler.ServeHTTP(w, r)
+
+		assertStatusCode(t, w, http.StatusNotFound)
+		if w.Header().Get("WWW-Authenticate") != "" {
+			t.Fatal("did not expect WWW-Authenticate header")
+		}
+	})
+
+	t.Run("error/bad auth", func(t *testing.T) {
+		r, _ := http.NewRequest("GET", "/hidden-basic-auth/user/pass", nil)
+		r.SetBasicAuth("bad", "auth")
+		w := httptest.NewRecorder()
+		handler.ServeHTTP(w, r)
+
+		assertStatusCode(t, w, http.StatusNotFound)
+		if w.Header().Get("WWW-Authenticate") != "" {
+			t.Fatal("did not expect WWW-Authenticate header")
+		}
+	})
+
+	var errorTests = []struct {
+		url    string
+		status int
+	}{
+		{"/hidden-basic-auth", http.StatusNotFound},
+		{"/hidden-basic-auth/user", http.StatusNotFound},
+		{"/hidden-basic-auth/user/pass/extra", http.StatusNotFound},
+	}
+	for _, test := range errorTests {
+		t.Run("error"+test.url, func(t *testing.T) {
+			r, _ := http.NewRequest("GET", test.url, nil)
+			r.SetBasicAuth("foo", "bar")
+			w := httptest.NewRecorder()
+			handler.ServeHTTP(w, r)
+			assertStatusCode(t, w, test.status)
+		})
+	}
+}
diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go
index fca0002..dd00375 100644
--- a/httpbin/httpbin.go
+++ b/httpbin/httpbin.go
@@ -85,12 +85,14 @@ func (h *HTTPBin) Handler() http.Handler {
 	mux.HandleFunc("/cookies/delete", h.DeleteCookies)
 
 	mux.HandleFunc("/basic-auth/", h.BasicAuth)
+	mux.HandleFunc("/hidden-basic-auth/", h.HiddenBasicAuth)
 
 	// Make sure our ServeMux doesn't "helpfully" redirect these invalid
 	// endpoints by adding a trailing slash. See the ServeMux docs for more
 	// info: https://golang.org/pkg/net/http/#ServeMux
 	mux.HandleFunc("/absolute-redirect", http.NotFound)
 	mux.HandleFunc("/basic-auth", http.NotFound)
+	mux.HandleFunc("/hidden-basic-auth", http.NotFound)
 	mux.HandleFunc("/redirect", http.NotFound)
 	mux.HandleFunc("/relative-redirect", http.NotFound)
 	mux.HandleFunc("/status", http.NotFound)
-- 
GitLab