Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit a6f1aa1

Browse files
committed
gps: support loading credentials from a netrc file
This enables support for private gitlab files and other places where the URL requires basic authentication. Initial proposal and implementation came from Johnny (github.com/wesgur). Fixes #2061. Fixes #1898.
1 parent 21c40aa commit a6f1aa1

File tree

3 files changed

+118
-3
lines changed

3 files changed

+118
-3
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ GOBIN := $(GOPATH)/bin
77
default: build validate test
88

99
get-deps:
10-
go get -u golang.org/x/lint/golint honnef.co/go/tools/cmd/megacheck
10+
go get -u golang.org/x/lint/golint honnef.co/go/tools/cmd/staticcheck
1111

1212
build:
1313
go fmt ./...

gps/deduce.go

+116-1
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,19 @@ import (
88
"context"
99
"fmt"
1010
"io"
11+
"io/ioutil"
1112
"net/http"
1213
"net/url"
14+
"os"
1315
"path"
16+
"path/filepath"
1417
"regexp"
18+
"runtime"
1519
"strconv"
1620
"strings"
1721
"sync"
1822

19-
"github.com/armon/go-radix"
23+
radix "github.com/armon/go-radix"
2024
"github.com/pkg/errors"
2125
)
2226

@@ -26,6 +30,8 @@ var (
2630
hgSchemes = []string{"https", "ssh", "http"}
2731
svnSchemes = []string{"https", "http", "svn", "svn+ssh"}
2832
gopkginSchemes = []string{"https", "http"}
33+
netrc []netrcLine
34+
readNetrcOnce sync.Once
2935
)
3036

3137
const gopkgUnstableSuffix = "-unstable"
@@ -848,6 +854,8 @@ func doFetchMetadata(ctx context.Context, scheme, path string) (io.ReadCloser, e
848854
return nil, errors.Wrapf(err, "unable to build HTTP request for URL %q", url)
849855
}
850856

857+
req = addAuthFromNetrc(url, req)
858+
851859
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
852860
if err != nil {
853861
return nil, errors.Wrapf(err, "failed HTTP request to URL %q", url)
@@ -859,6 +867,113 @@ func doFetchMetadata(ctx context.Context, scheme, path string) (io.ReadCloser, e
859867
}
860868
}
861869

870+
// See https://github.com/golang/go/blob/master/src/cmd/go/internal/web2/web.go
871+
// for implementation
872+
// Temporary netrc reader until https://github.com/golang/go/issues/31334 is solved
873+
type netrcLine struct {
874+
machine string
875+
login string
876+
password string
877+
}
878+
879+
func parseNetrc(data string) []netrcLine {
880+
// See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
881+
// for documentation on the .netrc format.
882+
var nrc []netrcLine
883+
var l netrcLine
884+
inMacro := false
885+
for _, line := range strings.Split(data, "\n") {
886+
if inMacro {
887+
if line == "" {
888+
inMacro = false
889+
}
890+
continue
891+
}
892+
893+
f := strings.Fields(line)
894+
i := 0
895+
for ; i < len(f)-1; i += 2 {
896+
// Reset at each "machine" token.
897+
// “The auto-login process searches the .netrc file for a machine token
898+
// that matches […]. Once a match is made, the subsequent .netrc tokens
899+
// are processed, stopping when the end of file is reached or another
900+
// machine or a default token is encountered.”
901+
switch f[i] {
902+
case "machine":
903+
l = netrcLine{machine: f[i+1]}
904+
case "login":
905+
l.login = f[i+1]
906+
case "password":
907+
l.password = f[i+1]
908+
case "macdef":
909+
// “A macro is defined with the specified name; its contents begin with
910+
// the next .netrc line and continue until a null line (consecutive
911+
// new-line characters) is encountered.”
912+
inMacro = true
913+
}
914+
if l.machine != "" && l.login != "" && l.password != "" {
915+
nrc = append(nrc, l)
916+
l = netrcLine{}
917+
}
918+
}
919+
920+
if i < len(f) && f[i] == "default" {
921+
// “There can be only one default token, and it must be after all machine tokens.”
922+
break
923+
}
924+
}
925+
926+
return nrc
927+
}
928+
929+
func netrcPath() (string, error) {
930+
if env := os.Getenv("NETRC"); env != "" {
931+
return env, nil
932+
}
933+
934+
dir := os.Getenv("HOME")
935+
936+
base := ".netrc"
937+
if runtime.GOOS == "windows" {
938+
base = "_netrc"
939+
}
940+
return filepath.Join(dir, base), nil
941+
}
942+
943+
// readNetrc parses a user's netrc file, ignoring any errors that occur.
944+
func readNetrc() {
945+
path, err := netrcPath()
946+
if err != nil {
947+
return
948+
}
949+
950+
data, err := ioutil.ReadFile(path)
951+
if err != nil {
952+
return
953+
}
954+
955+
netrc = parseNetrc(string(data))
956+
}
957+
958+
// addAuthFromNetrc uses basic authentication on go-get requests
959+
// for private repositories.
960+
func addAuthFromNetrc(rawurl string, req *http.Request) *http.Request {
961+
readNetrcOnce.Do(readNetrc)
962+
for _, m := range netrc {
963+
u, err := url.Parse(rawurl)
964+
if err != nil {
965+
continue
966+
}
967+
968+
if u.Host == m.machine {
969+
req.SetBasicAuth(m.login, m.password)
970+
break
971+
}
972+
}
973+
974+
return req
975+
}
976+
862977
// getMetadata fetches and decodes remote metadata for path.
863978
//
864979
// scheme is optional. If it's http, only http will be attempted for fetching.

hack/lint.bash

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ set -e
99
PKGS=$(go list ./... | grep -vF /vendor/)
1010
go vet $PKGS
1111
golint $PKGS
12-
megacheck -unused.exported -ignore "github.com/golang/dep/internal/test/test.go:U1000 github.com/golang/dep/gps/prune.go:U1000 github.com/golang/dep/manifest.go:U1000" $PKGS
12+
staticcheck -ignore "github.com/golang/dep/internal/test/test.go:U1000 github.com/golang/dep/gps/prune.go:U1000 github.com/golang/dep/manifest.go:U1000" $PKGS

0 commit comments

Comments
 (0)