From 8422b0e22ca6efe039007c4578edbb1f2e2644f7 Mon Sep 17 00:00:00 2001 From: rubyist Date: Tue, 6 Oct 2015 22:13:31 -0400 Subject: [PATCH 1/7] Basic netrc support for LFS API requests --- Nut.toml | 1 + lfs/credentials.go | 35 +- .../github.com/bgentry/go-netrc/.hgignore | 3 + .../_nuts/github.com/bgentry/go-netrc/LICENSE | 20 + .../github.com/bgentry/go-netrc/README.md | 9 + .../netrc/examples/bad_default_order.netrc | 13 + .../go-netrc/netrc/examples/good.netrc | 22 + .../bgentry/go-netrc/netrc/netrc.go | 510 ++++++++++++++++ .../bgentry/go-netrc/netrc/netrc_test.go | 559 ++++++++++++++++++ 9 files changed, 1170 insertions(+), 2 deletions(-) create mode 100644 vendor/_nuts/github.com/bgentry/go-netrc/.hgignore create mode 100644 vendor/_nuts/github.com/bgentry/go-netrc/LICENSE create mode 100644 vendor/_nuts/github.com/bgentry/go-netrc/README.md create mode 100644 vendor/_nuts/github.com/bgentry/go-netrc/netrc/examples/bad_default_order.netrc create mode 100644 vendor/_nuts/github.com/bgentry/go-netrc/netrc/examples/good.netrc create mode 100644 vendor/_nuts/github.com/bgentry/go-netrc/netrc/netrc.go create mode 100644 vendor/_nuts/github.com/bgentry/go-netrc/netrc/netrc_test.go diff --git a/Nut.toml b/Nut.toml index 8a011a96..09e22925 100644 --- a/Nut.toml +++ b/Nut.toml @@ -20,3 +20,4 @@ authors = [ "github.com/spf13/pflag" = "580b9be06c33d8ba9dcc8757ea56b7642472c2f5" "github.com/technoweenie/assert" = "b25ea301d127043ffacf3b2545726e79b6632139" "github.com/technoweenie/go-contentaddressable" = "38171def3cd15e3b76eb156219b3d48704643899" +"github.com/bgentry/go-netrc/netrc" = "9fd32a8b3d3d3f9d43c341bfe098430e07609480" diff --git a/lfs/credentials.go b/lfs/credentials.go index e919f76f..f339cb9e 100644 --- a/lfs/credentials.go +++ b/lfs/credentials.go @@ -3,10 +3,15 @@ package lfs import ( "bytes" "fmt" + "net" "net/http" "net/url" + "os" "os/exec" + "path/filepath" "strings" + + "github.com/github/git-lfs/vendor/_nuts/github.com/bgentry/go-netrc/netrc" ) // getCreds gets the credentials for the given request's URL, and sets its @@ -23,9 +28,10 @@ func getCreds(req *http.Request) (Creds, error) { // getCredsForAPI gets the credentials for LFS API requests and sets the given // request's Authorization header with them using Basic Authentication. // 1. Check the LFS URL for authentication. Ex: http://user:pass@example.com -// 2. Check the Git remote URL for authentication IF it's the same scheme and +// 2. Check netrc for authentication. +// 3. Check the Git remote URL for authentication IF it's the same scheme and // host of the LFS URL. -// 3. Ask 'git credential' to fill in the password from one of the above URLs. +// 4. Ask 'git credential' to fill in the password from one of the above URLs. // // This prefers the Git remote URL for checking credentials so that users only // have to enter their passwords once for Git and Git LFS. It uses the same @@ -44,6 +50,10 @@ func getCredsForAPI(req *http.Request) (Creds, error) { return nil, nil } + if setCredURLFromNetrc(req) { + return nil, nil + } + return fillCredentials(req, credsUrl) } @@ -87,6 +97,27 @@ func getCredURLForAPI(req *http.Request) (*url.URL, error) { return credsUrl, nil } +func setCredURLFromNetrc(req *http.Request) bool { + home := os.Getenv("HOME") + if len(home) == 0 { + return false + } + + nrc := filepath.Join(home, ".netrc") + host, _, err := net.SplitHostPort(req.URL.Host) + if err != nil { + return false + } + + machine, err := netrc.FindMachine(nrc, host) + if err != nil || machine == nil { + return false + } + + setRequestAuth(req, machine.Login, machine.Password) + return true +} + func skipCredsCheck(req *http.Request) bool { if len(req.Header.Get("Authorization")) > 0 { return true diff --git a/vendor/_nuts/github.com/bgentry/go-netrc/.hgignore b/vendor/_nuts/github.com/bgentry/go-netrc/.hgignore new file mode 100644 index 00000000..0871e011 --- /dev/null +++ b/vendor/_nuts/github.com/bgentry/go-netrc/.hgignore @@ -0,0 +1,3 @@ +syntax: glob +*.8 +*.a diff --git a/vendor/_nuts/github.com/bgentry/go-netrc/LICENSE b/vendor/_nuts/github.com/bgentry/go-netrc/LICENSE new file mode 100644 index 00000000..aade9a58 --- /dev/null +++ b/vendor/_nuts/github.com/bgentry/go-netrc/LICENSE @@ -0,0 +1,20 @@ +Original version Copyright © 2010 Fazlul Shahriar . Newer +portions Copyright © 2014 Blake Gentry . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/_nuts/github.com/bgentry/go-netrc/README.md b/vendor/_nuts/github.com/bgentry/go-netrc/README.md new file mode 100644 index 00000000..6759f7ad --- /dev/null +++ b/vendor/_nuts/github.com/bgentry/go-netrc/README.md @@ -0,0 +1,9 @@ +# go-netrc + +A Golang package for reading and writing netrc files. This package can parse netrc +files, make changes to them, and then serialize them back to netrc format, while +preserving any whitespace that was present in the source file. + +[![GoDoc](https://godoc.org/github.com/bgentry/go-netrc?status.png)][godoc] + +[godoc]: https://godoc.org/github.com/bgentry/go-netrc "go-netrc on Godoc.org" diff --git a/vendor/_nuts/github.com/bgentry/go-netrc/netrc/examples/bad_default_order.netrc b/vendor/_nuts/github.com/bgentry/go-netrc/netrc/examples/bad_default_order.netrc new file mode 100644 index 00000000..6aeec07a --- /dev/null +++ b/vendor/_nuts/github.com/bgentry/go-netrc/netrc/examples/bad_default_order.netrc @@ -0,0 +1,13 @@ +# I am a comment +machine mail.google.com + login joe@gmail.com + account gmail + password somethingSecret +# I am another comment + +default + login anonymous + password joe@example.com + +machine ray login demo password mypassword + diff --git a/vendor/_nuts/github.com/bgentry/go-netrc/netrc/examples/good.netrc b/vendor/_nuts/github.com/bgentry/go-netrc/netrc/examples/good.netrc new file mode 100644 index 00000000..41a8e5ba --- /dev/null +++ b/vendor/_nuts/github.com/bgentry/go-netrc/netrc/examples/good.netrc @@ -0,0 +1,22 @@ +# I am a comment +machine mail.google.com + login joe@gmail.com + account justagmail #end of line comment with trailing space + password somethingSecret + # I am another comment + +macdef allput +put src/* + +macdef allput2 + put src/* +put src2/* + +machine ray login demo password mypassword + +machine weirdlogin login uname password pass#pass + +default + login anonymous + password joe@example.com + diff --git a/vendor/_nuts/github.com/bgentry/go-netrc/netrc/netrc.go b/vendor/_nuts/github.com/bgentry/go-netrc/netrc/netrc.go new file mode 100644 index 00000000..ea49987c --- /dev/null +++ b/vendor/_nuts/github.com/bgentry/go-netrc/netrc/netrc.go @@ -0,0 +1,510 @@ +package netrc + +import ( + "bufio" + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +type tkType int + +const ( + tkMachine tkType = iota + tkDefault + tkLogin + tkPassword + tkAccount + tkMacdef + tkComment + tkWhitespace +) + +var keywords = map[string]tkType{ + "machine": tkMachine, + "default": tkDefault, + "login": tkLogin, + "password": tkPassword, + "account": tkAccount, + "macdef": tkMacdef, + "#": tkComment, +} + +type Netrc struct { + tokens []*token + machines []*Machine + macros Macros + updateLock sync.Mutex +} + +// FindMachine returns the Machine in n named by name. If a machine named by +// name exists, it is returned. If no Machine with name name is found and there +// is a ``default'' machine, the ``default'' machine is returned. Otherwise, nil +// is returned. +func (n *Netrc) FindMachine(name string) (m *Machine) { + // TODO(bgentry): not safe for concurrency + var def *Machine + for _, m = range n.machines { + if m.Name == name { + return m + } + if m.IsDefault() { + def = m + } + } + if def == nil { + return nil + } + return def +} + +// MarshalText implements the encoding.TextMarshaler interface to encode a +// Netrc into text format. +func (n *Netrc) MarshalText() (text []byte, err error) { + // TODO(bgentry): not safe for concurrency + for i := range n.tokens { + switch n.tokens[i].kind { + case tkComment, tkDefault, tkWhitespace: // always append these types + text = append(text, n.tokens[i].rawkind...) + default: + if n.tokens[i].value != "" { // skip empty-value tokens + text = append(text, n.tokens[i].rawkind...) + } + } + if n.tokens[i].kind == tkMacdef { + text = append(text, ' ') + text = append(text, n.tokens[i].macroName...) + } + text = append(text, n.tokens[i].rawvalue...) + } + return +} + +func (n *Netrc) NewMachine(name, login, password, account string) *Machine { + n.updateLock.Lock() + defer n.updateLock.Unlock() + + prefix := "\n" + if len(n.tokens) == 0 { + prefix = "" + } + m := &Machine{ + Name: name, + Login: login, + Password: password, + Account: account, + + nametoken: &token{ + kind: tkMachine, + rawkind: []byte(prefix + "machine"), + value: name, + rawvalue: []byte(" " + name), + }, + logintoken: &token{ + kind: tkLogin, + rawkind: []byte("\n\tlogin"), + value: login, + rawvalue: []byte(" " + login), + }, + passtoken: &token{ + kind: tkPassword, + rawkind: []byte("\n\tpassword"), + value: password, + rawvalue: []byte(" " + password), + }, + accounttoken: &token{ + kind: tkAccount, + rawkind: []byte("\n\taccount"), + value: account, + rawvalue: []byte(" " + account), + }, + } + n.insertMachineTokensBeforeDefault(m) + for i := range n.machines { + if n.machines[i].IsDefault() { + n.machines = append(append(n.machines[:i], m), n.machines[i:]...) + return m + } + } + n.machines = append(n.machines, m) + return m +} + +func (n *Netrc) insertMachineTokensBeforeDefault(m *Machine) { + newtokens := []*token{m.nametoken} + if m.logintoken.value != "" { + newtokens = append(newtokens, m.logintoken) + } + if m.passtoken.value != "" { + newtokens = append(newtokens, m.passtoken) + } + if m.accounttoken.value != "" { + newtokens = append(newtokens, m.accounttoken) + } + for i := range n.tokens { + if n.tokens[i].kind == tkDefault { + // found the default, now insert tokens before it + n.tokens = append(n.tokens[:i], append(newtokens, n.tokens[i:]...)...) + return + } + } + // didn't find a default, just add the newtokens to the end + n.tokens = append(n.tokens, newtokens...) + return +} + +func (n *Netrc) RemoveMachine(name string) { + n.updateLock.Lock() + defer n.updateLock.Unlock() + + for i := range n.machines { + if n.machines[i] != nil && n.machines[i].Name == name { + m := n.machines[i] + for _, t := range []*token{ + m.nametoken, m.logintoken, m.passtoken, m.accounttoken, + } { + n.removeToken(t) + } + n.machines = append(n.machines[:i], n.machines[i+1:]...) + return + } + } +} + +func (n *Netrc) removeToken(t *token) { + if t != nil { + for i := range n.tokens { + if n.tokens[i] == t { + n.tokens = append(n.tokens[:i], n.tokens[i+1:]...) + return + } + } + } +} + +// Machine contains information about a remote machine. +type Machine struct { + Name string + Login string + Password string + Account string + + nametoken *token + logintoken *token + passtoken *token + accounttoken *token +} + +// IsDefault returns true if the machine is a "default" token, denoted by an +// empty name. +func (m *Machine) IsDefault() bool { + return m.Name == "" +} + +// UpdatePassword sets the password for the Machine m. +func (m *Machine) UpdatePassword(newpass string) { + m.Password = newpass + updateTokenValue(m.passtoken, newpass) +} + +// UpdateLogin sets the login for the Machine m. +func (m *Machine) UpdateLogin(newlogin string) { + m.Login = newlogin + updateTokenValue(m.logintoken, newlogin) +} + +// UpdateAccount sets the login for the Machine m. +func (m *Machine) UpdateAccount(newaccount string) { + m.Account = newaccount + updateTokenValue(m.accounttoken, newaccount) +} + +func updateTokenValue(t *token, value string) { + oldvalue := t.value + t.value = value + newraw := make([]byte, len(t.rawvalue)) + copy(newraw, t.rawvalue) + t.rawvalue = append( + bytes.TrimSuffix(newraw, []byte(oldvalue)), + []byte(value)..., + ) +} + +// Macros contains all the macro definitions in a netrc file. +type Macros map[string]string + +type token struct { + kind tkType + macroName string + value string + rawkind []byte + rawvalue []byte +} + +// Error represents a netrc file parse error. +type Error struct { + LineNum int // Line number + Msg string // Error message +} + +// Error returns a string representation of error e. +func (e *Error) Error() string { + return fmt.Sprintf("line %d: %s", e.LineNum, e.Msg) +} + +func (e *Error) BadDefaultOrder() bool { + return e.Msg == errBadDefaultOrder +} + +const errBadDefaultOrder = "default token must appear after all machine tokens" + +// scanLinesKeepPrefix is a split function for a Scanner that returns each line +// of text. The returned token may include newlines if they are before the +// first non-space character. The returned line may be empty. The end-of-line +// marker is one optional carriage return followed by one mandatory newline. In +// regular expression notation, it is `\r?\n`. The last non-empty line of +// input will be returned even if it has no newline. +func scanLinesKeepPrefix(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + // Skip leading spaces. + start := 0 + for width := 0; start < len(data); start += width { + var r rune + r, width = utf8.DecodeRune(data[start:]) + if !unicode.IsSpace(r) { + break + } + } + if i := bytes.IndexByte(data[start:], '\n'); i >= 0 { + // We have a full newline-terminated line. + return start + i, data[0 : start+i], nil + } + // If we're at EOF, we have a final, non-terminated line. Return it. + if atEOF { + return len(data), data, nil + } + // Request more data. + return 0, nil, nil +} + +// scanWordsKeepPrefix is a split function for a Scanner that returns each +// space-separated word of text, with prefixing spaces included. It will never +// return an empty string. The definition of space is set by unicode.IsSpace. +// +// Adapted from bufio.ScanWords(). +func scanTokensKeepPrefix(data []byte, atEOF bool) (advance int, token []byte, err error) { + // Skip leading spaces. + start := 0 + for width := 0; start < len(data); start += width { + var r rune + r, width = utf8.DecodeRune(data[start:]) + if !unicode.IsSpace(r) { + break + } + } + if atEOF && len(data) == 0 || start == len(data) { + return len(data), data, nil + } + if len(data) > start && data[start] == '#' { + return scanLinesKeepPrefix(data, atEOF) + } + // Scan until space, marking end of word. + for width, i := 0, start; i < len(data); i += width { + var r rune + r, width = utf8.DecodeRune(data[i:]) + if unicode.IsSpace(r) { + return i, data[:i], nil + } + } + // If we're at EOF, we have a final, non-empty, non-terminated word. Return it. + if atEOF && len(data) > start { + return len(data), data, nil + } + // Request more data. + return 0, nil, nil +} + +func newToken(rawb []byte) (*token, error) { + _, tkind, err := bufio.ScanWords(rawb, true) + if err != nil { + return nil, err + } + var ok bool + t := token{rawkind: rawb} + t.kind, ok = keywords[string(tkind)] + if !ok { + trimmed := strings.TrimSpace(string(tkind)) + if trimmed == "" { + t.kind = tkWhitespace // whitespace-only, should happen only at EOF + return &t, nil + } + if strings.HasPrefix(trimmed, "#") { + t.kind = tkComment // this is a comment + return &t, nil + } + return &t, fmt.Errorf("keyword expected; got " + string(tkind)) + } + return &t, nil +} + +func scanValue(scanner *bufio.Scanner, pos int) ([]byte, string, int, error) { + if scanner.Scan() { + raw := scanner.Bytes() + pos += bytes.Count(raw, []byte{'\n'}) + return raw, strings.TrimSpace(string(raw)), pos, nil + } + if err := scanner.Err(); err != nil { + return nil, "", pos, &Error{pos, err.Error()} + } + return nil, "", pos, nil +} + +func parse(r io.Reader, pos int) (*Netrc, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + nrc := Netrc{machines: make([]*Machine, 0, 20), macros: make(Macros, 10)} + + defaultSeen := false + var currentMacro *token + var m *Machine + var t *token + scanner := bufio.NewScanner(bytes.NewReader(b)) + scanner.Split(scanTokensKeepPrefix) + + for scanner.Scan() { + rawb := scanner.Bytes() + if len(rawb) == 0 { + break + } + pos += bytes.Count(rawb, []byte{'\n'}) + t, err = newToken(rawb) + if err != nil { + if currentMacro == nil { + return nil, &Error{pos, err.Error()} + } + currentMacro.rawvalue = append(currentMacro.rawvalue, rawb...) + continue + } + + if currentMacro != nil && bytes.Contains(rawb, []byte{'\n', '\n'}) { + // if macro rawvalue + rawb would contain \n\n, then macro def is over + currentMacro.value = strings.TrimLeft(string(currentMacro.rawvalue), "\r\n") + nrc.macros[currentMacro.macroName] = currentMacro.value + currentMacro = nil + } + + switch t.kind { + case tkMacdef: + if _, t.macroName, pos, err = scanValue(scanner, pos); err != nil { + return nil, &Error{pos, err.Error()} + } + currentMacro = t + case tkDefault: + if defaultSeen { + return nil, &Error{pos, "multiple default token"} + } + if m != nil { + nrc.machines, m = append(nrc.machines, m), nil + } + m = new(Machine) + m.Name = "" + defaultSeen = true + case tkMachine: + if defaultSeen { + return nil, &Error{pos, errBadDefaultOrder} + } + if m != nil { + nrc.machines, m = append(nrc.machines, m), nil + } + m = new(Machine) + if t.rawvalue, m.Name, pos, err = scanValue(scanner, pos); err != nil { + return nil, &Error{pos, err.Error()} + } + t.value = m.Name + m.nametoken = t + case tkLogin: + if m == nil || m.Login != "" { + return nil, &Error{pos, "unexpected token login "} + } + if t.rawvalue, m.Login, pos, err = scanValue(scanner, pos); err != nil { + return nil, &Error{pos, err.Error()} + } + t.value = m.Login + m.logintoken = t + case tkPassword: + if m == nil || m.Password != "" { + return nil, &Error{pos, "unexpected token password"} + } + if t.rawvalue, m.Password, pos, err = scanValue(scanner, pos); err != nil { + return nil, &Error{pos, err.Error()} + } + t.value = m.Password + m.passtoken = t + case tkAccount: + if m == nil || m.Account != "" { + return nil, &Error{pos, "unexpected token account"} + } + if t.rawvalue, m.Account, pos, err = scanValue(scanner, pos); err != nil { + return nil, &Error{pos, err.Error()} + } + t.value = m.Account + m.accounttoken = t + } + + nrc.tokens = append(nrc.tokens, t) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + if m != nil { + nrc.machines, m = append(nrc.machines, m), nil + } + return &nrc, nil +} + +// ParseFile opens the file at filename and then passes its io.Reader to +// Parse(). +func ParseFile(filename string) (*Netrc, error) { + fd, err := os.Open(filename) + if err != nil { + return nil, err + } + defer fd.Close() + return Parse(fd) +} + +// Parse parses from the the Reader r as a netrc file and returns the set of +// machine information and macros defined in it. The ``default'' machine, +// which is intended to be used when no machine name matches, is identified +// by an empty machine name. There can be only one ``default'' machine. +// +// If there is a parsing error, an Error is returned. +func Parse(r io.Reader) (*Netrc, error) { + return parse(r, 1) +} + +// FindMachine parses the netrc file identified by filename and returns the +// Machine named by name. If a problem occurs parsing the file at filename, an +// error is returned. If a machine named by name exists, it is returned. If no +// Machine with name name is found and there is a ``default'' machine, the +// ``default'' machine is returned. Otherwise, nil is returned. +func FindMachine(filename, name string) (m *Machine, err error) { + n, err := ParseFile(filename) + if err != nil { + return nil, err + } + return n.FindMachine(name), nil +} diff --git a/vendor/_nuts/github.com/bgentry/go-netrc/netrc/netrc_test.go b/vendor/_nuts/github.com/bgentry/go-netrc/netrc/netrc_test.go new file mode 100644 index 00000000..70ceacf6 --- /dev/null +++ b/vendor/_nuts/github.com/bgentry/go-netrc/netrc/netrc_test.go @@ -0,0 +1,559 @@ +// Copyright © 2010 Fazlul Shahriar and +// Copyright © 2014 Blake Gentry . +// See LICENSE file for license details. + +package netrc + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + "testing" +) + +var expectedMachines = []*Machine{ + &Machine{Name: "mail.google.com", Login: "joe@gmail.com", Password: "somethingSecret", Account: "justagmail"}, + &Machine{Name: "ray", Login: "demo", Password: "mypassword", Account: ""}, + &Machine{Name: "weirdlogin", Login: "uname", Password: "pass#pass", Account: ""}, + &Machine{Name: "", Login: "anonymous", Password: "joe@example.com", Account: ""}, +} +var expectedMacros = Macros{ + "allput": "put src/*", + "allput2": " put src/*\nput src2/*", +} + +func eqMachine(a *Machine, b *Machine) bool { + return a.Name == b.Name && + a.Login == b.Login && + a.Password == b.Password && + a.Account == b.Account +} + +func testExpected(n *Netrc, t *testing.T) { + if len(expectedMachines) != len(n.machines) { + t.Errorf("expected %d machines, got %d", len(expectedMachines), len(n.machines)) + } else { + for i, e := range expectedMachines { + if !eqMachine(e, n.machines[i]) { + t.Errorf("bad machine; expected %v, got %v\n", e, n.machines[i]) + } + } + } + + if len(expectedMacros) != len(n.macros) { + t.Errorf("expected %d macros, got %d", len(expectedMacros), len(n.macros)) + } else { + for k, v := range expectedMacros { + if v != n.macros[k] { + t.Errorf("bad macro for %s; expected %q, got %q\n", k, v, n.macros[k]) + } + } + } +} + +var newTokenTests = []struct { + rawkind string + tkind tkType +}{ + {"machine", tkMachine}, + {"\n\n\tmachine", tkMachine}, + {"\n machine", tkMachine}, + {"default", tkDefault}, + {"login", tkLogin}, + {"password", tkPassword}, + {"account", tkAccount}, + {"macdef", tkMacdef}, + {"\n # comment stuff ", tkComment}, + {"\n # I am another comment", tkComment}, + {"\n\t\n ", tkWhitespace}, +} + +var newTokenInvalidTests = []string{ + " junk", + "sdfdsf", + "account#unspaced comment", +} + +func TestNewToken(t *testing.T) { + for _, tktest := range newTokenTests { + tok, err := newToken([]byte(tktest.rawkind)) + if err != nil { + t.Fatal(err) + } + if tok.kind != tktest.tkind { + t.Errorf("expected tok.kind %d, got %d", tktest.tkind, tok.kind) + } + if string(tok.rawkind) != tktest.rawkind { + t.Errorf("expected tok.rawkind %q, got %q", tktest.rawkind, string(tok.rawkind)) + } + } + + for _, tktest := range newTokenInvalidTests { + _, err := newToken([]byte(tktest)) + if err == nil { + t.Errorf("expected error with %q, got none", tktest) + } + } +} + +func TestParse(t *testing.T) { + r := netrcReader("examples/good.netrc", t) + n, err := Parse(r) + if err != nil { + t.Fatal(err) + } + testExpected(n, t) +} + +func TestParseFile(t *testing.T) { + n, err := ParseFile("examples/good.netrc") + if err != nil { + t.Fatal(err) + } + testExpected(n, t) + + _, err = ParseFile("examples/bad_default_order.netrc") + if err == nil { + t.Error("expected an error parsing bad_default_order.netrc, got none") + } else if !err.(*Error).BadDefaultOrder() { + t.Error("expected BadDefaultOrder() to be true, got false") + } + + _, err = ParseFile("examples/this_file_doesnt_exist.netrc") + if err == nil { + t.Error("expected an error loading this_file_doesnt_exist.netrc, got none") + } else if _, ok := err.(*os.PathError); !ok { + t.Errorf("expected *os.Error, got %v", err) + } +} + +func TestFindMachine(t *testing.T) { + m, err := FindMachine("examples/good.netrc", "ray") + if err != nil { + t.Fatal(err) + } + if !eqMachine(m, expectedMachines[1]) { + t.Errorf("bad machine; expected %v, got %v\n", expectedMachines[1], m) + } + if m.IsDefault() { + t.Errorf("expected m.IsDefault() to be false") + } + + m, err = FindMachine("examples/good.netrc", "non.existent") + if err != nil { + t.Fatal(err) + } + if !eqMachine(m, expectedMachines[3]) { + t.Errorf("bad machine; expected %v, got %v\n", expectedMachines[3], m) + } + if !m.IsDefault() { + t.Errorf("expected m.IsDefault() to be true") + } +} + +func TestNetrcFindMachine(t *testing.T) { + n, err := ParseFile("examples/good.netrc") + if err != nil { + t.Fatal(err) + } + + m := n.FindMachine("ray") + if !eqMachine(m, expectedMachines[1]) { + t.Errorf("bad machine; expected %v, got %v\n", expectedMachines[1], m) + } + if m.IsDefault() { + t.Errorf("expected def to be false") + } + + n = &Netrc{} + m = n.FindMachine("nonexistent") + if m != nil { + t.Errorf("expected nil, got %v", m) + } +} + +func TestMarshalText(t *testing.T) { + // load up expected netrc Marshal output + expected, err := ioutil.ReadAll(netrcReader("examples/good.netrc", t)) + if err != nil { + t.Fatal(err) + } + + n, err := ParseFile("examples/good.netrc") + if err != nil { + t.Fatal(err) + } + + result, err := n.MarshalText() + if err != nil { + t.Fatal(err) + } + if string(result) != string(expected) { + t.Errorf("expected:\n%q\ngot:\n%q", string(expected), string(result)) + } + + // make sure tokens w/ no value are not serialized + m := n.FindMachine("mail.google.com") + m.UpdatePassword("") + result, err = n.MarshalText() + if err != nil { + t.Fatal(err) + } + if strings.Contains(string(result), "\tpassword \n") { + fmt.Println(string(result)) + t.Errorf("expected zero-value password token to not be serialzed") + } +} + +var newMachineTests = []struct { + name string + login string + password string + account string +}{ + {"heroku.com", "dodging-samurai-42@heroku.com", "octocatdodgeballchampions", "2011+2013"}, + {"bgentry.io", "special@test.com", "noacct", ""}, + {"github.io", "2@test.com", "", "acctwithnopass"}, + {"someotherapi.com", "", "passonly", ""}, +} + +func TestNewMachine(t *testing.T) { + n, err := ParseFile("examples/good.netrc") + if err != nil { + t.Fatal(err) + } + testNewMachine(t, n) + n = &Netrc{} + testNewMachine(t, n) + + // make sure that tokens without a value are not serialized at all + for _, test := range newMachineTests { + n = &Netrc{} + _ = n.NewMachine(test.name, test.login, test.password, test.account) + + bodyb, _ := n.MarshalText() + body := string(bodyb) + + // ensure desired values are present when they should be + if !strings.Contains(body, "machine") { + t.Errorf("NewMachine() %s missing keyword 'machine'", test.name) + } + if !strings.Contains(body, test.name) { + t.Errorf("NewMachine() %s missing value %q", test.name, test.name) + } + if test.login != "" && !strings.Contains(body, "login "+test.login) { + t.Errorf("NewMachine() %s missing value %q", test.name, "login "+test.login) + } + if test.password != "" && !strings.Contains(body, "password "+test.password) { + t.Errorf("NewMachine() %s missing value %q", test.name, "password "+test.password) + } + if test.account != "" && !strings.Contains(body, "account "+test.account) { + t.Errorf("NewMachine() %s missing value %q", test.name, "account "+test.account) + } + + // ensure undesired values are not present when they shouldn't be + if test.login == "" && strings.Contains(body, "login") { + t.Errorf("NewMachine() %s contains unexpected value %q", test.name, "login") + } + if test.password == "" && strings.Contains(body, "password") { + t.Errorf("NewMachine() %s contains unexpected value %q", test.name, "password") + } + if test.account == "" && strings.Contains(body, "account") { + t.Errorf("NewMachine() %s contains unexpected value %q", test.name, "account") + } + } +} + +func testNewMachine(t *testing.T, n *Netrc) { + for _, test := range newMachineTests { + mcount := len(n.machines) + // sanity check + bodyb, _ := n.MarshalText() + body := string(bodyb) + for _, value := range []string{test.name, test.login, test.password, test.account} { + if value != "" && strings.Contains(body, value) { + t.Errorf("MarshalText() before NewMachine() contained unexpected %q", value) + } + } + + // test prefix for machine token + prefix := "\n" + if len(n.tokens) == 0 { + prefix = "" + } + + m := n.NewMachine(test.name, test.login, test.password, test.account) + if m == nil { + t.Fatalf("NewMachine() returned nil") + } + + if len(n.machines) != mcount+1 { + t.Errorf("n.machines count expected %d, got %d", mcount+1, len(n.machines)) + } + // check values + if m.Name != test.name { + t.Errorf("m.Name expected %q, got %q", test.name, m.Name) + } + if m.Login != test.login { + t.Errorf("m.Login expected %q, got %q", test.login, m.Login) + } + if m.Password != test.password { + t.Errorf("m.Password expected %q, got %q", test.password, m.Password) + } + if m.Account != test.account { + t.Errorf("m.Account expected %q, got %q", test.account, m.Account) + } + // check tokens + checkToken(t, "nametoken", m.nametoken, tkMachine, prefix+"machine", test.name) + checkToken(t, "logintoken", m.logintoken, tkLogin, "\n\tlogin", test.login) + checkToken(t, "passtoken", m.passtoken, tkPassword, "\n\tpassword", test.password) + checkToken(t, "accounttoken", m.accounttoken, tkAccount, "\n\taccount", test.account) + // check marshal output + bodyb, _ = n.MarshalText() + body = string(bodyb) + for _, value := range []string{test.name, test.login, test.password, test.account} { + if !strings.Contains(body, value) { + t.Errorf("MarshalText() after NewMachine() did not include %q as expected", value) + } + } + } +} + +func checkToken(t *testing.T, name string, tok *token, kind tkType, rawkind, value string) { + if tok == nil { + t.Errorf("%s not defined", name) + return + } + if tok.kind != kind { + t.Errorf("%s expected kind %d, got %d", name, kind, tok.kind) + } + if string(tok.rawkind) != rawkind { + t.Errorf("%s expected rawkind %q, got %q", name, rawkind, string(tok.rawkind)) + } + if tok.value != value { + t.Errorf("%s expected value %q, got %q", name, value, tok.value) + } + if tok.value != value { + t.Errorf("%s expected value %q, got %q", name, value, tok.value) + } +} + +func TestNewMachineGoesBeforeDefault(t *testing.T) { + n, err := ParseFile("examples/good.netrc") + if err != nil { + t.Fatal(err) + } + m := n.NewMachine("mymachine", "mylogin", "mypassword", "myaccount") + if m2 := n.machines[len(n.machines)-2]; m2 != m { + t.Errorf("expected machine %v, got %v", m, m2) + } +} + +func TestRemoveMachine(t *testing.T) { + n, err := ParseFile("examples/good.netrc") + if err != nil { + t.Fatal(err) + } + + tests := []string{"mail.google.com", "weirdlogin"} + + for _, name := range tests { + mcount := len(n.machines) + // sanity check + m := n.FindMachine(name) + if m == nil { + t.Fatalf("machine %q not found", name) + } + if m.IsDefault() { + t.Fatalf("expected machine %q, got default instead", name) + } + n.RemoveMachine(name) + + if len(n.machines) != mcount-1 { + t.Errorf("n.machines count expected %d, got %d", mcount-1, len(n.machines)) + } + + // make sure Machine is no longer returned by FindMachine() + if m2 := n.FindMachine(name); m2 != nil && !m2.IsDefault() { + t.Errorf("Machine %q not removed from Machines list", name) + } + + // make sure tokens are not present in tokens list + for _, token := range []*token{m.nametoken, m.logintoken, m.passtoken, m.accounttoken} { + if token != nil { + for _, tok2 := range n.tokens { + if tok2 == token { + t.Errorf("token not removed from tokens list: %v", token) + break + } + } + } + } + + bodyb, _ := n.MarshalText() + body := string(bodyb) + for _, value := range []string{m.Name, m.Login, m.Password, m.Account} { + if value != "" && strings.Contains(body, value) { + t.Errorf("MarshalText() after RemoveMachine() contained unexpected %q", value) + } + } + } +} + +func TestUpdateLogin(t *testing.T) { + n, err := ParseFile("examples/good.netrc") + if err != nil { + t.Fatal(err) + } + + tests := []struct { + exists bool + name string + oldlogin string + newlogin string + }{ + {true, "mail.google.com", "joe@gmail.com", "joe2@gmail.com"}, + {false, "heroku.com", "", "dodging-samurai-42@heroku.com"}, + } + + bodyb, _ := n.MarshalText() + body := string(bodyb) + for _, test := range tests { + if strings.Contains(body, test.newlogin) { + t.Errorf("MarshalText() before UpdateLogin() contained unexpected %q", test.newlogin) + } + } + + for _, test := range tests { + m := n.FindMachine(test.name) + if m.IsDefault() == test.exists { + t.Errorf("expected machine %s to not exist, but it did", test.name) + } else { + if !test.exists { + m = n.NewMachine(test.name, test.newlogin, "", "") + } + if m == nil { + t.Errorf("machine %s was nil", test.name) + continue + } + m.UpdateLogin(test.newlogin) + m := n.FindMachine(test.name) + if m.Login != test.newlogin { + t.Errorf("expected new login %q, got %q", test.newlogin, m.Login) + } + if m.logintoken.value != test.newlogin { + t.Errorf("expected m.logintoken %q, got %q", test.newlogin, m.logintoken.value) + } + } + } + + bodyb, _ = n.MarshalText() + body = string(bodyb) + for _, test := range tests { + if test.exists && strings.Contains(body, test.oldlogin) { + t.Errorf("MarshalText() after UpdateLogin() contained unexpected %q", test.oldlogin) + } + if !strings.Contains(body, test.newlogin) { + t.Errorf("MarshalText after UpdatePassword did not contain %q as expected", test.newlogin) + } + } +} + +func TestUpdatePassword(t *testing.T) { + n, err := ParseFile("examples/good.netrc") + if err != nil { + t.Fatal(err) + } + + tests := []struct { + exists bool + name string + oldpassword string + newpassword string + }{ + {true, "ray", "mypassword", "supernewpass"}, + {false, "heroku.com", "", "octocatdodgeballchampions"}, + } + + bodyb, _ := n.MarshalText() + body := string(bodyb) + for _, test := range tests { + if test.exists && !strings.Contains(body, test.oldpassword) { + t.Errorf("MarshalText() before UpdatePassword() did not include %q as expected", test.oldpassword) + } + if strings.Contains(body, test.newpassword) { + t.Errorf("MarshalText() before UpdatePassword() contained unexpected %q", test.newpassword) + } + } + + for _, test := range tests { + m := n.FindMachine(test.name) + if m.IsDefault() == test.exists { + t.Errorf("expected machine %s to not exist, but it did", test.name) + } else { + if !test.exists { + m = n.NewMachine(test.name, "", test.newpassword, "") + } + if m == nil { + t.Errorf("machine %s was nil", test.name) + continue + } + m.UpdatePassword(test.newpassword) + m = n.FindMachine(test.name) + if m.Password != test.newpassword { + t.Errorf("expected new password %q, got %q", test.newpassword, m.Password) + } + if m.passtoken.value != test.newpassword { + t.Errorf("expected m.passtoken %q, got %q", test.newpassword, m.passtoken.value) + } + } + } + + bodyb, _ = n.MarshalText() + body = string(bodyb) + for _, test := range tests { + if test.exists && strings.Contains(body, test.oldpassword) { + t.Errorf("MarshalText() after UpdatePassword() contained unexpected %q", test.oldpassword) + } + if !strings.Contains(body, test.newpassword) { + t.Errorf("MarshalText() after UpdatePassword() did not contain %q as expected", test.newpassword) + } + } +} + +func TestNewFile(t *testing.T) { + var n Netrc + + result, err := n.MarshalText() + if err != nil { + t.Fatal(err) + } + if string(result) != "" { + t.Errorf("expected empty result=\"\", got %q", string(result)) + } + + n.NewMachine("netrctest.heroku.com", "auser", "apassword", "") + + result, err = n.MarshalText() + if err != nil { + t.Fatal(err) + } + expected := `machine netrctest.heroku.com + login auser + password apassword` + + if string(result) != expected { + t.Errorf("expected result:\n%q\ngot:\n%q", expected, string(result)) + } +} + +func netrcReader(filename string, t *testing.T) io.Reader { + b, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatal(err) + } + return bytes.NewReader(b) +} From 91195f6f6f7473252fb8b7140b7de3a66e465ed9 Mon Sep 17 00:00:00 2001 From: rubyist Date: Wed, 7 Oct 2015 00:14:09 -0400 Subject: [PATCH 2/7] Add integration tests for netrc --- test/cmd/lfstest-gitserver.go | 66 +++++++++++++++++++++++------------ test/test-credentials.sh | 29 +++++++++++++++ 2 files changed, 72 insertions(+), 23 deletions(-) diff --git a/test/cmd/lfstest-gitserver.go b/test/cmd/lfstest-gitserver.go index 9bfca684..b7a55eed 100644 --- a/test/cmd/lfstest-gitserver.go +++ b/test/cmd/lfstest-gitserver.go @@ -242,6 +242,14 @@ func lfsBatchHandler(w http.ResponseWriter, r *http.Request, repo string) { return } + if repo == "netrctest" { + user, pass, err := extractAuth(r.Header.Get("Authorization")) + if err != nil || (user != "netrcuser" || pass != "netrcpass") { + w.WriteHeader(403) + return + } + } + type batchReq struct { Operation string `json:"operation"` Objects []lfsObject `json:"objects"` @@ -498,6 +506,25 @@ func newLfsStorage() *lfsStorage { } } +func extractAuth(auth string) (string, string, error) { + if strings.HasPrefix(auth, "Basic ") { + decodeBy, err := base64.StdEncoding.DecodeString(auth[6:len(auth)]) + decoded := string(decodeBy) + + if err != nil { + return "", "", err + } + + parts := strings.SplitN(decoded, ":", 2) + if len(parts) == 2 { + return parts[0], parts[1], nil + } + return "", "", nil + } + + return "", "", nil +} + func skipIfBadAuth(w http.ResponseWriter, r *http.Request) bool { auth := r.Header.Get("Authorization") if auth == "" { @@ -505,32 +532,25 @@ func skipIfBadAuth(w http.ResponseWriter, r *http.Request) bool { return true } - if strings.HasPrefix(auth, "Basic ") { - decodeBy, err := base64.StdEncoding.DecodeString(auth[6:len(auth)]) - decoded := string(decodeBy) + user, pass, err := extractAuth(auth) + if err != nil { + w.WriteHeader(403) + log.Printf("Error decoding auth: %s\n", err) + return true + } - if err != nil { - w.WriteHeader(403) - log.Printf("Error decoding auth: %s\n", err) - return true + switch user { + case "user": + if pass == "pass" { + return false } - - parts := strings.SplitN(decoded, ":", 2) - if len(parts) == 2 { - switch parts[0] { - case "user": - if parts[1] == "pass" { - return false - } - case "path": - if strings.HasPrefix(r.URL.Path, "/"+parts[1]) { - return false - } - log.Printf("auth attempt against: %q", r.URL.Path) - } + case "netrcuser": + return false + case "path": + if strings.HasPrefix(r.URL.Path, "/"+pass) { + return false } - - log.Printf("auth does not match: %q", decoded) + log.Printf("auth attempt against: %q", r.URL.Path) } w.WriteHeader(403) diff --git a/test/test-credentials.sh b/test/test-credentials.sh index 713a129d..e0801024 100755 --- a/test/test-credentials.sh +++ b/test/test-credentials.sh @@ -139,3 +139,32 @@ password=path" [ "$expected" = "$(cat cred.log)" ] ) end_test + +begin_test "credentials from netrc" +( + set -e + + printf "machine localhost\nlogin netrcuser\npassword netrcpass\n" >> "$HOME/.netrc" + echo $HOME + echo "GITSERVER $GITSERVER" + cat $HOME/.netrc + + + reponame="netrctest" + setup_remote_repo "$reponame" + + clone_repo "$reponame" repo + + # Need a remote named "localhost" or 127.0.0.1 in netrc will interfere with the other auth + git remote add "netrc" "$(echo $GITSERVER | sed s/127.0.0.1/localhost/)/netrctest" + git lfs env + + git lfs track "*.dat" + echo "push a" > a.dat + git add .gitattributes a.dat + git commit -m "add a.dat" + + git lfs push netrc master 2>&1 | tee push.log + grep "(1 of 1 files)" push.log +) +end_test From 7a86b2b5f517f02065c83529ce719706c1558d41 Mon Sep 17 00:00:00 2001 From: rubyist Date: Wed, 7 Oct 2015 00:17:06 -0400 Subject: [PATCH 3/7] Test with bad credentials in netrc --- test/test-credentials.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/test-credentials.sh b/test/test-credentials.sh index e0801024..6d1bce78 100755 --- a/test/test-credentials.sh +++ b/test/test-credentials.sh @@ -168,3 +168,32 @@ begin_test "credentials from netrc" grep "(1 of 1 files)" push.log ) end_test + +begin_test "credentials from netrc with bad password" +( + set -e + + printf "machine localhost\nlogin netrcuser\npassword badpass\n" >> "$HOME/.netrc" + echo $HOME + echo "GITSERVER $GITSERVER" + cat $HOME/.netrc + + + reponame="netrctest" + setup_remote_repo "$reponame" + + clone_repo "$reponame" repo2 + + # Need a remote named "localhost" or 127.0.0.1 in netrc will interfere with the other auth + git remote add "netrc" "$(echo $GITSERVER | sed s/127.0.0.1/localhost/)/netrctest" + git lfs env + + git lfs track "*.dat" + echo "push a" > a.dat + git add .gitattributes a.dat + git commit -m "add a.dat" + + git push netrc master 2>&1 | tee push.log + [ "0" = "$(grep -c "(1 of 1 files)" push.log)" ] +) +end_test From 69ff7b83d40b430e111a6e1a85c707faf1c4e623 Mon Sep 17 00:00:00 2001 From: risk danger olson Date: Wed, 18 Nov 2015 09:54:44 -0700 Subject: [PATCH 4/7] save merge conflict fix, doh --- lfs/credentials.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/lfs/credentials.go b/lfs/credentials.go index 62df337c..8de00280 100644 --- a/lfs/credentials.go +++ b/lfs/credentials.go @@ -12,11 +12,8 @@ import ( "path/filepath" "strings" -<<<<<<< HEAD "github.com/github/git-lfs/vendor/_nuts/github.com/bgentry/go-netrc/netrc" -======= "github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx" ->>>>>>> cred-fixes ) // getCreds gets the credentials for the given request's URL, and sets its From e7b4348ff40e00cc35fcc002a6415e2b220381ce Mon Sep 17 00:00:00 2001 From: risk danger olson Date: Wed, 18 Nov 2015 10:23:36 -0700 Subject: [PATCH 5/7] cache the parsed netrc file so it's not checked all the time --- lfs/config.go | 16 ++++++++++++++++ lfs/config_linux.go | 6 ++++++ lfs/config_netrc.go | 28 ++++++++++++++++++++++++++++ lfs/config_windows.go | 6 ++++++ lfs/credentials.go | 11 +---------- 5 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 lfs/config_linux.go create mode 100644 lfs/config_netrc.go create mode 100644 lfs/config_windows.go diff --git a/lfs/config.go b/lfs/config.go index 885f70fd..bba73475 100644 --- a/lfs/config.go +++ b/lfs/config.go @@ -12,6 +12,7 @@ import ( "github.com/github/git-lfs/git" "github.com/github/git-lfs/vendor/_nuts/github.com/ThomsonReutersEikon/go-ntlm/ntlm" + "github.com/github/git-lfs/vendor/_nuts/github.com/bgentry/go-netrc/netrc" "github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx" ) @@ -60,6 +61,7 @@ type Configuration struct { fetchIncludePaths []string fetchExcludePaths []string fetchPruneConfig *FetchPruneConfig + parsedNetrc netrcfinder } func NewConfig() *Configuration { @@ -176,6 +178,20 @@ func (c *Configuration) SetAccess(authType string) { c.SetEndpointAccess(c.Endpoint(), authType) } +func (c *Configuration) FindNetrcHost(host string) (*netrc.Machine, error) { + c.loading.Lock() + defer c.loading.Unlock() + if c.parsedNetrc == nil { + n, err := c.parseNetrc() + if err != nil { + return nil, err + } + c.parsedNetrc = n + } + + return c.parsedNetrc.FindMachine(host), nil +} + func (c *Configuration) EndpointAccess(e Endpoint) string { key := fmt.Sprintf("lfs.%s.access", e.Url) if v, ok := c.GitConfig(key); ok && len(v) > 0 { diff --git a/lfs/config_linux.go b/lfs/config_linux.go new file mode 100644 index 00000000..9b58829e --- /dev/null +++ b/lfs/config_linux.go @@ -0,0 +1,6 @@ +// +build !windows +package lfs + +func init() { + netrcBasename = ".netrc" +} diff --git a/lfs/config_netrc.go b/lfs/config_netrc.go new file mode 100644 index 00000000..e75de4ba --- /dev/null +++ b/lfs/config_netrc.go @@ -0,0 +1,28 @@ +package lfs + +// different on unix vs windows +var netrcBasename string + +type netrcfinder interface { + FindMachine(string) *netrc.Machine +} + +type noNetrc struct{} + +func (n *noNetrc) FindMachine(host string) *netrc.Machine { + return nil +} + +func (c *Configuration) parseNetrc() (netrcfinder, error) { + home := c.Getenv("HOME") + if len(home) == 0 { + return &noNetrc{}, nil + } + + nrcfilename := filepath.Join(home, netrcBasename) + if _, err := os.Stat(nrcfilename); err != nil { + return &noNetrc{}, nil + } + + return netrc.ParseFile(nrcfilename) +} diff --git a/lfs/config_windows.go b/lfs/config_windows.go new file mode 100644 index 00000000..50b0a779 --- /dev/null +++ b/lfs/config_windows.go @@ -0,0 +1,6 @@ +// +build windows +package lfs + +func init() { + netrcBasename = "_netrc" +} diff --git a/lfs/credentials.go b/lfs/credentials.go index 8de00280..f2ec2928 100644 --- a/lfs/credentials.go +++ b/lfs/credentials.go @@ -7,12 +7,9 @@ import ( "net" "net/http" "net/url" - "os" "os/exec" - "path/filepath" "strings" - "github.com/github/git-lfs/vendor/_nuts/github.com/bgentry/go-netrc/netrc" "github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx" ) @@ -99,18 +96,12 @@ func getCredURLForAPI(req *http.Request) (*url.URL, error) { } func setCredURLFromNetrc(req *http.Request) bool { - home := os.Getenv("HOME") - if len(home) == 0 { - return false - } - - nrc := filepath.Join(home, ".netrc") host, _, err := net.SplitHostPort(req.URL.Host) if err != nil { return false } - machine, err := netrc.FindMachine(nrc, host) + machine, err := Config.FindNetrcHost(host) if err != nil || machine == nil { return false } From f145021933d92e97a143f66e05ab1e7ca08cba76 Mon Sep 17 00:00:00 2001 From: risk danger olson Date: Wed, 18 Nov 2015 10:25:34 -0700 Subject: [PATCH 6/7] gofmt --- lfs/config_linux.go | 2 +- lfs/config_netrc.go | 7 +++++++ lfs/config_windows.go | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lfs/config_linux.go b/lfs/config_linux.go index 9b58829e..8f5242ba 100644 --- a/lfs/config_linux.go +++ b/lfs/config_linux.go @@ -2,5 +2,5 @@ package lfs func init() { - netrcBasename = ".netrc" + netrcBasename = ".netrc" } diff --git a/lfs/config_netrc.go b/lfs/config_netrc.go index e75de4ba..c7a4dd0f 100644 --- a/lfs/config_netrc.go +++ b/lfs/config_netrc.go @@ -1,5 +1,12 @@ package lfs +import ( + "os" + "path/filepath" + + "github.com/github/git-lfs/vendor/_nuts/github.com/bgentry/go-netrc/netrc" +) + // different on unix vs windows var netrcBasename string diff --git a/lfs/config_windows.go b/lfs/config_windows.go index 50b0a779..7a7a2cc8 100644 --- a/lfs/config_windows.go +++ b/lfs/config_windows.go @@ -2,5 +2,5 @@ package lfs func init() { - netrcBasename = "_netrc" + netrcBasename = "_netrc" } From 2a601cbd092b50354d4a5f9ea1ac04c99e7d4468 Mon Sep 17 00:00:00 2001 From: rubyist Date: Sat, 21 Nov 2015 10:14:10 -0500 Subject: [PATCH 7/7] Use a build flagged var instead of var + init() for setting netrc name Instead of a blank var in the main config with values set by init() in build flag protected files, the build flagged files should create the var. This prevents cases where no build flagged file builds leaving an empty netrc basename causing auth failures. This also renames the file config_linux.go to config_nix.go because it should build on all unix-y platforms. --- lfs/config_linux.go | 6 ------ lfs/config_netrc.go | 3 --- lfs/config_nix.go | 4 ++++ lfs/config_windows.go | 4 +--- 4 files changed, 5 insertions(+), 12 deletions(-) delete mode 100644 lfs/config_linux.go create mode 100644 lfs/config_nix.go diff --git a/lfs/config_linux.go b/lfs/config_linux.go deleted file mode 100644 index 8f5242ba..00000000 --- a/lfs/config_linux.go +++ /dev/null @@ -1,6 +0,0 @@ -// +build !windows -package lfs - -func init() { - netrcBasename = ".netrc" -} diff --git a/lfs/config_netrc.go b/lfs/config_netrc.go index c7a4dd0f..3a0a1497 100644 --- a/lfs/config_netrc.go +++ b/lfs/config_netrc.go @@ -7,9 +7,6 @@ import ( "github.com/github/git-lfs/vendor/_nuts/github.com/bgentry/go-netrc/netrc" ) -// different on unix vs windows -var netrcBasename string - type netrcfinder interface { FindMachine(string) *netrc.Machine } diff --git a/lfs/config_nix.go b/lfs/config_nix.go new file mode 100644 index 00000000..13233f05 --- /dev/null +++ b/lfs/config_nix.go @@ -0,0 +1,4 @@ +// +build !windows +package lfs + +var netrcBasename = ".netrc" diff --git a/lfs/config_windows.go b/lfs/config_windows.go index 7a7a2cc8..a9dfbe70 100644 --- a/lfs/config_windows.go +++ b/lfs/config_windows.go @@ -1,6 +1,4 @@ // +build windows package lfs -func init() { - netrcBasename = "_netrc" -} +var netrcBasename = "_netrc"