github.com/bir3/gocompiler@v0.9.2202/src/cmd/gocmd/internal/vcweb/auth.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package vcweb 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "io" 11 "log" 12 "net/http" 13 "os" 14 "path" 15 "strings" 16 ) 17 18 // authHandler serves requests only if the Basic Auth data sent with the request 19 // matches the contents of a ".access" file in the requested directory. 20 // 21 // For each request, the handler looks for a file named ".access" and parses it 22 // as a JSON-serialized accessToken. If the credentials from the request match 23 // the accessToken, the file is served normally; otherwise, it is rejected with 24 // the StatusCode and Message provided by the token. 25 type authHandler struct{} 26 27 type accessToken struct { 28 Username, Password string 29 StatusCode int // defaults to 401. 30 Message string 31 } 32 33 func (h *authHandler) Available() bool { return true } 34 35 func (h *authHandler) Handler(dir string, env []string, logger *log.Logger) (http.Handler, error) { 36 fs := http.Dir(dir) 37 38 handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 39 urlPath := req.URL.Path 40 if urlPath != "" && strings.HasPrefix(path.Base(urlPath), ".") { 41 http.Error(w, "filename contains leading dot", http.StatusBadRequest) 42 return 43 } 44 45 f, err := fs.Open(urlPath) 46 if err != nil { 47 if os.IsNotExist(err) { 48 http.NotFound(w, req) 49 } else { 50 http.Error(w, err.Error(), http.StatusInternalServerError) 51 } 52 return 53 } 54 55 accessDir := urlPath 56 if fi, err := f.Stat(); err == nil && !fi.IsDir() { 57 accessDir = path.Dir(urlPath) 58 } 59 f.Close() 60 61 var accessFile http.File 62 for { 63 var err error 64 accessFile, err = fs.Open(path.Join(accessDir, ".access")) 65 if err == nil { 66 break 67 } 68 69 if !os.IsNotExist(err) { 70 http.Error(w, err.Error(), http.StatusInternalServerError) 71 return 72 } 73 if accessDir == "." { 74 http.Error(w, "failed to locate access file", http.StatusInternalServerError) 75 return 76 } 77 accessDir = path.Dir(accessDir) 78 } 79 80 data, err := io.ReadAll(accessFile) 81 if err != nil { 82 http.Error(w, err.Error(), http.StatusInternalServerError) 83 return 84 } 85 86 var token accessToken 87 if err := json.Unmarshal(data, &token); err != nil { 88 logger.Print(err) 89 http.Error(w, "malformed access file", http.StatusInternalServerError) 90 return 91 } 92 if username, password, ok := req.BasicAuth(); !ok || username != token.Username || password != token.Password { 93 code := token.StatusCode 94 if code == 0 { 95 code = http.StatusUnauthorized 96 } 97 if code == http.StatusUnauthorized { 98 w.Header().Add("WWW-Authenticate", fmt.Sprintf("basic realm=%s", accessDir)) 99 } 100 http.Error(w, token.Message, code) 101 return 102 } 103 104 http.FileServer(fs).ServeHTTP(w, req) 105 }) 106 107 return handler, nil 108 }