...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package main
16
17 import (
18 "bytes"
19 "fmt"
20 "log"
21 "net/http"
22 "net/url"
23 "os"
24 "os/exec"
25 "path/filepath"
26 "strings"
27 )
28
29 func main() {
30 if len(os.Args) < 2 || !filepath.IsAbs(os.Args[1]) {
31 fmt.Fprintf(os.Stderr, "usage: %s WORKDIR [URL]", os.Args[0])
32 os.Exit(2)
33 }
34
35 log.SetPrefix("gitauth: ")
36
37 if len(os.Args) != 3 {
38
39
40
41 return
42 }
43
44 u, err := url.ParseRequestURI(os.Args[2])
45 if err != nil {
46 log.Fatalf("invalid request URI (%v): %q\n", err, os.Args[1])
47 }
48
49 var (
50 prefix *url.URL
51 lastHeader http.Header
52 lastStatus = http.StatusUnauthorized
53 )
54 for lastStatus == http.StatusUnauthorized {
55 cmd := exec.Command("git", "credential", "fill")
56
57
58
59
60
61 cmd.Dir = os.Args[1]
62
63 cmd.Stdin = strings.NewReader(fmt.Sprintf("url=%s\n", u))
64 cmd.Stderr = os.Stderr
65 out, err := cmd.Output()
66 if err != nil {
67 log.Fatalf("'git credential fill' failed: %v\n", err)
68 }
69
70 prefix = new(url.URL)
71 var username, password string
72 lines := strings.Split(string(out), "\n")
73 for _, line := range lines {
74 frags := strings.SplitN(line, "=", 2)
75 if len(frags) != 2 {
76 continue
77 }
78 switch strings.TrimSpace(frags[0]) {
79 case "protocol":
80 prefix.Scheme = frags[1]
81 case "host":
82 prefix.Host = frags[1]
83 case "path":
84 prefix.Path = frags[1]
85 case "username":
86 username = frags[1]
87 case "password":
88 password = frags[1]
89 case "url":
90
91
92
93 u, err := url.ParseRequestURI(frags[1])
94 if err == nil {
95 prefix = u
96 } else {
97 log.Printf("malformed URL from 'git credential fill' (%v): %q\n", err, frags[1])
98
99 }
100 }
101 }
102
103
104 if !strings.HasPrefix(u.String(), prefix.String()) {
105 log.Fatalf("requested a credential for %q, but 'git credential fill' provided one for %q\n", u, prefix)
106 }
107
108
109
110
111 req, err := http.NewRequest("HEAD", u.String(), nil)
112 if err != nil {
113 log.Fatalf("internal error constructing HTTP HEAD request: %v\n", err)
114 }
115 req.SetBasicAuth(username, password)
116 lastHeader = req.Header
117 resp, err := http.DefaultClient.Do(req)
118 if err != nil {
119 log.Printf("HTTPS HEAD request failed to connect: %v\n", err)
120
121
122 break
123 }
124 lastStatus = resp.StatusCode
125
126 if resp.StatusCode != http.StatusOK {
127 log.Printf("%s: %v %s\n", u, resp.StatusCode, http.StatusText(resp.StatusCode))
128 }
129
130 if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusUnauthorized {
131
132
133
134 action := "approve"
135 if resp.StatusCode != http.StatusOK {
136 action = "reject"
137 }
138 cmd = exec.Command("git", "credential", action)
139 cmd.Stderr = os.Stderr
140 cmd.Stdout = os.Stderr
141 cmd.Stdin = bytes.NewReader(out)
142 _ = cmd.Run()
143 }
144 }
145
146
147 fmt.Printf("%s\n\n", prefix)
148 lastHeader.Write(os.Stdout)
149 fmt.Println()
150 }
151
View as plain text