github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/auth/cookieauth/cookieauth.go (about) 1 // Copyright 2019 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 // cookieauth uses a “Netscape cookie file” to implement the GOAUTH protocol 6 // described in https://golang.org/issue/26232. 7 // It expects the location of the file as the first command-line argument. 8 // 9 // Example GOAUTH usage: 10 // 11 // export GOAUTH="cookieauth $(git config --get http.cookieFile)" 12 // 13 // See http://www.cookiecentral.com/faq/#3.5 for a description of the Netscape 14 // cookie file format. 15 package main 16 17 import ( 18 "bufio" 19 "fmt" 20 "io" 21 "log" 22 "net/http" 23 "net/http/cookiejar" 24 "net/url" 25 "os" 26 "strconv" 27 "strings" 28 "time" 29 "unicode" 30 ) 31 32 func main() { 33 if len(os.Args) < 2 { 34 fmt.Fprintf(os.Stderr, "usage: %s COOKIEFILE [URL]\n", os.Args[0]) 35 os.Exit(2) 36 } 37 38 log.SetPrefix("cookieauth: ") 39 40 f, err := os.Open(os.Args[1]) 41 if err != nil { 42 log.Fatalf("failed to read cookie file: %v\n", os.Args[1]) 43 } 44 defer f.Close() 45 46 var ( 47 targetURL *url.URL 48 targetURLs = map[string]*url.URL{} 49 ) 50 if len(os.Args) == 3 { 51 targetURL, err = url.ParseRequestURI(os.Args[2]) 52 if err != nil { 53 log.Fatalf("invalid request URI (%v): %q\n", err, os.Args[2]) 54 } 55 targetURLs[targetURL.String()] = targetURL 56 } else if len(os.Args) > 3 { 57 // Extra arguments were passed: maybe the protocol was expanded? 58 // We don't know how to interpret the request, so ignore it. 59 return 60 } 61 62 entries, err := parseCookieFile(f.Name(), f) 63 if err != nil { 64 log.Fatalf("error reading cookie file: %v\n", f.Name()) 65 } 66 67 jar, err := cookiejar.New(nil) 68 if err != nil { 69 log.Fatalf("failed to initialize cookie jar: %v\n", err) 70 } 71 72 for _, e := range entries { 73 u := &url.URL{ 74 Scheme: "https", 75 Host: e.Host, 76 Path: e.Cookie.Path, 77 } 78 79 if targetURL == nil { 80 targetURLs[u.String()] = u 81 } 82 83 jar.SetCookies(u, []*http.Cookie{&e.Cookie}) 84 } 85 86 for _, u := range targetURLs { 87 req := &http.Request{URL: u, Header: make(http.Header)} 88 for _, c := range jar.Cookies(req.URL) { 89 req.AddCookie(c) 90 } 91 fmt.Printf("%s\n\n", u) 92 req.Header.Write(os.Stdout) 93 fmt.Println() 94 } 95 } 96 97 type Entry struct { 98 Host string 99 Cookie http.Cookie 100 } 101 102 // parseCookieFile parses a Netscape cookie file as described in 103 // http://www.cookiecentral.com/faq/#3.5. 104 func parseCookieFile(name string, r io.Reader) ([]*Entry, error) { 105 var entries []*Entry 106 s := bufio.NewScanner(r) 107 line := 0 108 for s.Scan() { 109 line++ 110 text := strings.TrimSpace(s.Text()) 111 if len(text) < 2 || (text[0] == '#' && unicode.IsSpace(rune(text[1]))) { 112 continue 113 } 114 115 e, err := parseCookieLine(text) 116 if err != nil { 117 log.Printf("%s:%d: %v\n", name, line, err) 118 continue 119 } 120 entries = append(entries, e) 121 } 122 return entries, s.Err() 123 } 124 125 func parseCookieLine(line string) (*Entry, error) { 126 f := strings.Fields(line) 127 if len(f) < 7 { 128 return nil, fmt.Errorf("found %d columns; want 7", len(f)) 129 } 130 131 e := new(Entry) 132 c := &e.Cookie 133 134 if domain := f[0]; strings.HasPrefix(domain, "#HttpOnly_") { 135 c.HttpOnly = true 136 e.Host = strings.TrimPrefix(domain[10:], ".") 137 } else { 138 e.Host = strings.TrimPrefix(domain, ".") 139 } 140 141 isDomain, err := strconv.ParseBool(f[1]) 142 if err != nil { 143 return nil, fmt.Errorf("non-boolean domain flag: %v", err) 144 } 145 if isDomain { 146 c.Domain = e.Host 147 } 148 149 c.Path = f[2] 150 151 c.Secure, err = strconv.ParseBool(f[3]) 152 if err != nil { 153 return nil, fmt.Errorf("non-boolean secure flag: %v", err) 154 } 155 156 expiration, err := strconv.ParseInt(f[4], 10, 64) 157 if err != nil { 158 return nil, fmt.Errorf("malformed expiration: %v", err) 159 } 160 c.Expires = time.Unix(expiration, 0) 161 162 c.Name = f[5] 163 c.Value = f[6] 164 165 return e, nil 166 }