Commit 92d36b74 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Rename stuff, comments

parent d35a23da
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
"strings" "strings"
) )
type gitHandler struct { type gitService struct {
method string method string
regexp *regexp.Regexp regexp *regexp.Regexp
handle_func func(string, string, string, http.ResponseWriter, *http.Request) handle_func func(string, string, string, http.ResponseWriter, *http.Request)
...@@ -25,14 +25,14 @@ var http_client = &http.Client{} ...@@ -25,14 +25,14 @@ var http_client = &http.Client{}
var path_traversal = regexp.MustCompile(`/../`) var path_traversal = regexp.MustCompile(`/../`)
// Command-line options // Command-line options
var repo_root string var repo_root = "."
var listen_addr = flag.String("listen_addr", "localhost:8181", "Listen address for HTTP server") var listen_addr = flag.String("listen_addr", "localhost:8181", "Listen address for HTTP server")
var auth_backend = flag.String("auth_backend", "http://localhost:8080", "Authentication/authorization backend") var auth_backend = flag.String("auth_backend", "http://localhost:8080", "Authentication/authorization backend")
var git_handlers = [...]gitHandler{ var git_services = [...]gitService{
gitHandler{"GET", regexp.MustCompile(`\A(/..*)/info/refs\z`), handle_get_info_refs, ""}, gitService{"GET", regexp.MustCompile(`\A(/..*)/info/refs\z`), handle_get_info_refs, ""},
gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-upload-pack\z`), handle_post_rpc, "git-upload-pack"}, gitService{"POST", regexp.MustCompile(`\A(/..*)/git-upload-pack\z`), handle_post_rpc, "git-upload-pack"},
gitHandler{"POST", regexp.MustCompile(`\A(/..*)/git-receive-pack\z`), handle_post_rpc, "git-receive-pack"}, gitService{"POST", regexp.MustCompile(`\A(/..*)/git-receive-pack\z`), handle_post_rpc, "git-receive-pack"},
} }
func main() { func main() {
...@@ -50,7 +50,7 @@ func git_handler(w http.ResponseWriter, r *http.Request) { ...@@ -50,7 +50,7 @@ func git_handler(w http.ResponseWriter, r *http.Request) {
log.Print(r.Method, " ", r.URL) log.Print(r.Method, " ", r.URL)
for _, g := range git_handlers { for _, g := range git_services {
path_match := g.regexp.FindStringSubmatch(r.URL.Path) path_match := g.regexp.FindStringSubmatch(r.URL.Path)
if r.Method == g.method && path_match != nil { if r.Method == g.method && path_match != nil {
found_path := path_match[1] found_path := path_match[1]
...@@ -64,9 +64,11 @@ func git_handler(w http.ResponseWriter, r *http.Request) { ...@@ -64,9 +64,11 @@ func git_handler(w http.ResponseWriter, r *http.Request) {
return return
} }
if auth_response.StatusCode != 200 { if auth_response.StatusCode != 200 {
// The Git request is not allowed by the backend. Maybe the client // The Git request is not allowed by the backend. Maybe the
// needs to send HTTP Basic credentials. // client needs to send HTTP Basic credentials. Forward the
// Forward the response from the auth backend to our client // response from the auth backend to our client. This includes
// the 'WWW-Authentication' header that acts as a hint that
// Basic auth credentials are needed.
for k, v := range auth_response.Header { for k, v := range auth_response.Header {
w.Header()[k] = v w.Header()[k] = v
} }
...@@ -75,6 +77,8 @@ func git_handler(w http.ResponseWriter, r *http.Request) { ...@@ -75,6 +77,8 @@ func git_handler(w http.ResponseWriter, r *http.Request) {
return return
} }
// The auth backend told us who the user is according to them // The auth backend told us who the user is according to them
// (GL_ID). We must extract this information from the auth response
// body.
if _, err := fmt.Fscan(auth_response.Body, &user); err != nil { if _, err := fmt.Fscan(auth_response.Body, &user); err != nil {
fail_500(w, err) fail_500(w, err)
return return
...@@ -84,6 +88,7 @@ func git_handler(w http.ResponseWriter, r *http.Request) { ...@@ -84,6 +88,7 @@ func git_handler(w http.ResponseWriter, r *http.Request) {
} }
} }
http.Error(w, "Not found", 404) http.Error(w, "Not found", 404)
return
} }
func valid_path(p string) bool { func valid_path(p string) bool {
...@@ -91,6 +96,8 @@ func valid_path(p string) bool { ...@@ -91,6 +96,8 @@ func valid_path(p string) bool {
log.Printf("path traversal detected in %s", p) log.Printf("path traversal detected in %s", p)
return false return false
} }
// If /path/to/foo.git/objects exist then let's assume it is a valid Git
// repository.
if _, err := os.Stat(path.Join(repo_root, p, "objects")); err != nil { if _, err := os.Stat(path.Join(repo_root, p, "objects")); err != nil {
log.Print(err) log.Print(err)
return false return false
...@@ -104,21 +111,19 @@ func do_auth_request(r *http.Request) (result *http.Response, err error) { ...@@ -104,21 +111,19 @@ func do_auth_request(r *http.Request) (result *http.Response, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Forward all headers from our client to the auth backend. This includes
// HTTP Basic authentication credentials (the 'Authorization' header).
for k, v := range r.Header { for k, v := range r.Header {
auth_req.Header[k] = v auth_req.Header[k] = v
} }
result, err = http_client.Do(auth_req) return http_client.Do(auth_req)
if err != nil {
return nil, err
}
return result, nil
} }
func handle_get_info_refs(user string, _ string, path string, w http.ResponseWriter, r *http.Request) { func handle_get_info_refs(user string, _ string, path string, w http.ResponseWriter, r *http.Request) {
rpc := r.URL.Query().Get("service") rpc := r.URL.Query().Get("service")
switch rpc { switch rpc {
case "git-upload-pack", "git-receive-pack": case "git-upload-pack", "git-receive-pack":
cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", "--advertise-refs", path) cmd := exec.Command("git", sub_command(rpc), "--stateless-rpc", "--advertise-refs", path)
set_cmd_env(cmd, user) set_cmd_env(cmd, user)
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
...@@ -131,7 +136,7 @@ func handle_get_info_refs(user string, _ string, path string, w http.ResponseWri ...@@ -131,7 +136,7 @@ func handle_get_info_refs(user string, _ string, path string, w http.ResponseWri
return return
} }
w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc)) w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-advertisement", rpc))
no_cache(w) header_no_cache(w)
w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just panic w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just panic
if err := pkt_line(w, fmt.Sprintf("# service=%s\n", rpc)); err != nil { if err := pkt_line(w, fmt.Sprintf("# service=%s\n", rpc)); err != nil {
panic(err) panic(err)
...@@ -140,10 +145,10 @@ func handle_get_info_refs(user string, _ string, path string, w http.ResponseWri ...@@ -140,10 +145,10 @@ func handle_get_info_refs(user string, _ string, path string, w http.ResponseWri
panic(err) panic(err)
} }
if _, err := io.Copy(w, stdout); err != nil { if _, err := io.Copy(w, stdout); err != nil {
panic(err) // No point in sending 500 to the client panic(err)
} }
if err := cmd.Wait(); err != nil { if err := cmd.Wait(); err != nil {
panic(err) // No point in sending 500 to the client panic(err)
} }
case "": case "":
// The 'dumb' Git HTTP protocol is not supported // The 'dumb' Git HTTP protocol is not supported
...@@ -151,6 +156,10 @@ func handle_get_info_refs(user string, _ string, path string, w http.ResponseWri ...@@ -151,6 +156,10 @@ func handle_get_info_refs(user string, _ string, path string, w http.ResponseWri
} }
} }
func sub_command(rpc string) string {
return strings.TrimPrefix(rpc, "git-")
}
func set_cmd_env(cmd *exec.Cmd, user string) { func set_cmd_env(cmd *exec.Cmd, user string) {
cmd.Env = []string{fmt.Sprintf("PATH=%s", os.Getenv("PATH")), fmt.Sprintf("GL_ID=%s", user)} cmd.Env = []string{fmt.Sprintf("PATH=%s", os.Getenv("PATH")), fmt.Sprintf("GL_ID=%s", user)}
} }
...@@ -168,7 +177,8 @@ func handle_post_rpc(user string, rpc string, path string, w http.ResponseWriter ...@@ -168,7 +177,8 @@ func handle_post_rpc(user string, rpc string, path string, w http.ResponseWriter
} else { } else {
body = r.Body body = r.Body
} }
cmd := exec.Command("git", strings.TrimPrefix(rpc, "git-"), "--stateless-rpc", path)
cmd := exec.Command("git", sub_command(rpc), "--stateless-rpc", path)
set_cmd_env(cmd, user) set_cmd_env(cmd, user)
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
...@@ -193,13 +203,13 @@ func handle_post_rpc(user string, rpc string, path string, w http.ResponseWriter ...@@ -193,13 +203,13 @@ func handle_post_rpc(user string, rpc string, path string, w http.ResponseWriter
stdin.Close() stdin.Close()
w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-result", rpc)) w.Header().Add("Content-Type", fmt.Sprintf("application/x-%s-result", rpc))
no_cache(w) header_no_cache(w)
w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just panic w.WriteHeader(200) // Don't bother with HTTP 500 from this point on, just panic
if _, err := io.Copy(w, stdout); err != nil { if _, err := io.Copy(w, stdout); err != nil {
panic(err) // No point in sending 500 to the client panic(err)
} }
if err := cmd.Wait(); err != nil { if err := cmd.Wait(); err != nil {
panic(err) // No point in sending 500 to the client panic(err)
} }
} }
...@@ -218,6 +228,6 @@ func fail_500(w http.ResponseWriter, err error) { ...@@ -218,6 +228,6 @@ func fail_500(w http.ResponseWriter, err error) {
log.Print(err) log.Print(err)
} }
func no_cache(w http.ResponseWriter) { func header_no_cache(w http.ResponseWriter) {
w.Header().Add("Cache-Control", "no-cache") w.Header().Add("Cache-Control", "no-cache")
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment