ref: fb754a4a3745ee5492c8e8bf870b01e8d6c11291
pkg/go-git-http/auth/auth.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
package auth import ( "net/http" "regexp" "strings" ) type AuthInfo struct { // Usernane or email Username string // Plaintext password or token Password string // repo component of URL // Usually: "username/repo_name" // But could also be: "some_repo.git" Repo string // Are we pushing or fetching ? Push bool Fetch bool } var ( repoNameRegex = regexp.MustCompile("^/?(.*?)/(HEAD|git-upload-pack|git-receive-pack|info/refs|objects/.*)$") ) func Authenticator(authf func(AuthInfo) (bool, error)) func(http.Handler) http.Handler { return func(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { auth, err := parseAuthHeader(req.Header.Get("Authorization")) if err != nil { w.Header().Set("WWW-Authenticate", `Basic realm="git server"`) http.Error(w, err.Error(), 401) return } // Build up info from request headers and URL info := AuthInfo{ Username: auth.Name, Password: auth.Pass, Repo: repoName(req.URL.Path), Push: isPush(req), Fetch: isFetch(req), } // Call authentication function authenticated, err := authf(info) if err != nil { code := 500 msg := err.Error() if se, ok := err.(StatusError); ok { code = se.StatusCode() } http.Error(w, msg, code) return } // Deny access to repo if !authenticated { http.Error(w, "Forbidden", 403) return } // Access granted handler.ServeHTTP(w, req) }) } } func isFetch(req *http.Request) bool { return isService("upload-pack", req) } func isPush(req *http.Request) bool { return isService("receive-pack", req) } func isService(service string, req *http.Request) bool { return getServiceType(req) == service || strings.HasSuffix(req.URL.Path, service) } func repoName(urlPath string) string { matches := repoNameRegex.FindStringSubmatch(urlPath) if matches == nil { return "" } return matches[1] } func getServiceType(r *http.Request) string { service_type := r.FormValue("service") if s := strings.HasPrefix(service_type, "git-"); !s { return "" } return strings.Replace(service_type, "git-", "", 1) } // StatusCode is an interface allowing authenticators // to pass down error's with an http error code type StatusError interface { StatusCode() int } |