vcs-test/vcweb: add a handler that requires HTTPS Basic Auth
This endpoint will be used for integration testing golang/go#26232.
Updates golang/go#26232
Change-Id: I70cab336b885b82abfe8b77839bc9163600f15b0
Reviewed-on: https://go-review.googlesource.com/c/build/+/170581
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
diff --git a/vcs-test/vcweb/auth.go b/vcs-test/vcweb/auth.go
new file mode 100644
index 0000000..d6ba58d
--- /dev/null
+++ b/vcs-test/vcweb/auth.go
@@ -0,0 +1,117 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ pathpkg "path"
+ "strings"
+)
+
+type accessToken struct {
+ Username, Password string
+ StatusCode int // defaults to 401.
+ Message string
+}
+
+// newAuthHandler serves authenticated data from within dir.
+//
+// For each request, the handler looks for a file in a parent directory (still
+// within dir) named ".access" and parses it as a JSON-serialized accessToken.
+// If the credentials from the request match the accessToken, the file is served
+// normally; otherwise, it is rejected with the StatusCode and Message provided
+// by the token.
+func newAuthHandler(dir http.FileSystem) http.Handler {
+ return &authHandler{dir}
+}
+
+type authHandler struct {
+ dir http.FileSystem
+}
+
+func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if !strings.HasPrefix(r.URL.Path, "/auth/") {
+ http.Error(w, "path does not start with /auth/", http.StatusInternalServerError)
+ return
+ }
+
+ path := strings.TrimPrefix(r.URL.Path, "/auth/")
+ if path == "" {
+ http.NotFound(w, r)
+ return
+ }
+ if strings.HasPrefix(pathpkg.Base(path), ".") {
+ http.Error(w, "filename contains leading dot", http.StatusBadRequest)
+ return
+ }
+
+ f, err := h.dir.Open(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ http.NotFound(w, r)
+ } else {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+ return
+ }
+
+ accessDir := path
+ if fi, err := f.Stat(); err == nil && !fi.IsDir() {
+ accessDir = pathpkg.Dir(path)
+ }
+ f.Close()
+
+ var accessFile http.File
+ for {
+ var err error
+ accessFile, err = h.dir.Open(pathpkg.Join(accessDir, ".access"))
+ if err == nil {
+ break
+ }
+
+ if !os.IsNotExist(err) {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if accessDir == "." {
+ http.Error(w, "failed to locate access file", http.StatusInternalServerError)
+ return
+ }
+ accessDir = pathpkg.Dir(accessDir)
+ }
+
+ data, err := ioutil.ReadAll(accessFile)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ var token accessToken
+ if err := json.Unmarshal(data, &token); err != nil {
+ log.Print(err)
+ http.Error(w, "malformed access file", http.StatusInternalServerError)
+ return
+ }
+ if username, password, ok := r.BasicAuth(); !ok || username != token.Username || password != token.Password {
+ code := token.StatusCode
+ if code == 0 {
+ code = http.StatusUnauthorized
+ }
+ if code == http.StatusUnauthorized {
+ w.Header().Add("WWW-Authenticate", fmt.Sprintf("basic realm=%s", accessDir))
+ }
+ http.Error(w, token.Message, code)
+ return
+ }
+
+ http.FileServer(h.dir).ServeHTTP(w, r)
+}
diff --git a/vcs-test/vcweb/insecure.go b/vcs-test/vcweb/insecure.go
index 9c64097..eb74348 100644
--- a/vcs-test/vcweb/insecure.go
+++ b/vcs-test/vcweb/insecure.go
@@ -18,6 +18,7 @@
func insecureRedirectDispatch(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(r.URL.Path, "/insecure/") {
http.Error(w, "path does not start with /insecure/", http.StatusInternalServerError)
+ return
}
url := *r.URL
diff --git a/vcs-test/vcweb/main.go b/vcs-test/vcweb/main.go
index ad15a48..4ceafed 100644
--- a/vcs-test/vcweb/main.go
+++ b/vcs-test/vcweb/main.go
@@ -68,6 +68,7 @@
http.Handle("/fossil/", fossilHandler())
http.Handle("/bzr/", bzrHandler())
http.Handle("/insecure/", insecureRedirectHandler())
+ http.Handle("/auth/", newAuthHandler(http.Dir(filepath.Join(*dir, "auth"))))
handler := logger(http.HandlerFunc(loadAndHandle))