From bb6b083667fe417af176b1822f7ceb68a9db52c8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 20 Aug 2016 18:45:38 -0400 Subject: [PATCH] functions for adding netrc info to URLs --- get.go | 2 +- get_http.go | 6 +++- netrc.go | 67 +++++++++++++++++++++++++++++++++++++++ netrc_test.go | 63 ++++++++++++++++++++++++++++++++++++ test-fixtures/netrc/basic | 3 ++ util_test.go | 24 ++++++++++++++ 6 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 netrc.go create mode 100644 netrc_test.go create mode 100644 test-fixtures/netrc/basic create mode 100644 util_test.go diff --git a/get.go b/get.go index 75d813cdb..cd9a411be 100644 --- a/get.go +++ b/get.go @@ -46,7 +46,7 @@ var Getters map[string]Getter var forcedRegexp = regexp.MustCompile(`^([A-Za-z0-9]+)::(.+)$`) func init() { - httpGetter := new(HttpGetter) + httpGetter := &HttpGetter{Netrc: true} Getters = map[string]Getter{ "file": new(FileGetter), diff --git a/get_http.go b/get_http.go index 47b3e7e42..06970c58d 100644 --- a/get_http.go +++ b/get_http.go @@ -32,7 +32,11 @@ import ( // The source URL, whether from the header or meta tag, must be a fully // formed URL. The shorthand syntax of "github.com/foo/bar" or relative // paths are not allowed. -type HttpGetter struct{} +type HttpGetter struct { + // Netrc, if true, will lookup and use auth information found + // in the user's netrc file if available. + Netrc bool +} func (g *HttpGetter) Get(dst string, u *url.URL) error { // Copy the URL so we can modify it diff --git a/netrc.go b/netrc.go new file mode 100644 index 000000000..c7f6a3fb3 --- /dev/null +++ b/netrc.go @@ -0,0 +1,67 @@ +package getter + +import ( + "fmt" + "net/url" + "os" + "runtime" + + "github.com/bgentry/go-netrc/netrc" + "github.com/mitchellh/go-homedir" +) + +// addAuthFromNetrc adds auth information to the URL from the user's +// netrc file if it can be found. This will only add the auth info +// if the URL doesn't already have auth info specified and the +// the username is blank. +func addAuthFromNetrc(u *url.URL) error { + // If the URL already has auth information, do nothing + if u.User != nil && u.User.Username() != "" { + return nil + } + + // Get the netrc file path + path := os.Getenv("NETRC") + if path == "" { + filename := ".netrc" + if runtime.GOOS == "windows" { + filename = "_netrc" + } + + var err error + path, err = homedir.Expand("~/" + filename) + if err != nil { + return err + } + } + + // If the file is not a file, then do nothing + if fi, err := os.Stat(path); err != nil { + // File doesn't exist, do nothing + if os.IsNotExist(err) { + return nil + } + + // Some other error! + return err + } else if fi.IsDir() { + // File is directory, ignore + return nil + } + + // Load up the netrc file + net, err := netrc.ParseFile(path) + if err != nil { + return fmt.Errorf("Error parsing netrc file at %q: %s", path, err) + } + + machine := net.FindMachine(u.Host) + if machine == nil { + // Machine not found, no problem + return nil + } + + // Set the user info + u.User = url.UserPassword(machine.Login, machine.Password) + return nil +} diff --git a/netrc_test.go b/netrc_test.go new file mode 100644 index 000000000..e222c9633 --- /dev/null +++ b/netrc_test.go @@ -0,0 +1,63 @@ +package getter + +import ( + "net/url" + "testing" +) + +func TestAddAuthFromNetrc(t *testing.T) { + defer tempEnv(t, "NETRC", "./test-fixtures/netrc/basic")() + + u, err := url.Parse("http://example.com") + if err != nil { + t.Fatalf("err: %s", err) + } + + if err := addAuthFromNetrc(u); err != nil { + t.Fatalf("err: %s", err) + } + + expected := "http://foo:bar@example.com" + actual := u.String() + if expected != actual { + t.Fatalf("Mismatch: %q != %q", actual, expected) + } +} + +func TestAddAuthFromNetrc_hasAuth(t *testing.T) { + defer tempEnv(t, "NETRC", "./test-fixtures/netrc/basic")() + + u, err := url.Parse("http://username:password@example.com") + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := u.String() + if err := addAuthFromNetrc(u); err != nil { + t.Fatalf("err: %s", err) + } + + actual := u.String() + if expected != actual { + t.Fatalf("Mismatch: %q != %q", actual, expected) + } +} + +func TestAddAuthFromNetrc_hasUsername(t *testing.T) { + defer tempEnv(t, "NETRC", "./test-fixtures/netrc/basic")() + + u, err := url.Parse("http://username@example.com") + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := u.String() + if err := addAuthFromNetrc(u); err != nil { + t.Fatalf("err: %s", err) + } + + actual := u.String() + if expected != actual { + t.Fatalf("Mismatch: %q != %q", actual, expected) + } +} diff --git a/test-fixtures/netrc/basic b/test-fixtures/netrc/basic new file mode 100644 index 000000000..574dd49ac --- /dev/null +++ b/test-fixtures/netrc/basic @@ -0,0 +1,3 @@ +machine example.com +login foo +password bar diff --git a/util_test.go b/util_test.go new file mode 100644 index 000000000..b99be8c12 --- /dev/null +++ b/util_test.go @@ -0,0 +1,24 @@ +package getter + +import ( + "os" + "testing" +) + +// tempEnv sets the env var temporarily and returns a function that should +// be deferred to clean it up. +func tempEnv(t *testing.T, k, v string) func() { + old := os.Getenv(k) + + // Set env + if err := os.Setenv(k, v); err != nil { + t.Fatalf("err: %s", err) + } + + // Easy cleanup + return func() { + if err := os.Setenv(k, old); err != nil { + t.Fatalf("err: %s", err) + } + } +}