lfsapi: return null cred helper if cred helper isn't used for a req

This commit is contained in:
rick olson 2017-10-26 16:09:37 -06:00
parent c8b16f965c
commit 28ff509488
3 changed files with 85 additions and 107 deletions

@ -21,29 +21,7 @@ var (
)
func (c *Client) DoWithAuth(remote string, req *http.Request) (*http.Response, error) {
credHelper := c.Credentials
if credHelper == nil {
var err error
credHelper, err = getCredentialHelper(c.osEnv, c.gitEnv)
if err != nil {
tracerx.Printf("error getting credential helper: %s", err)
}
if credHelper == nil {
credHelper = defaultCredentialHelper
}
}
netrcFinder := c.Netrc
if netrcFinder == nil {
netrcFinder = defaultNetrcFinder
}
ef := c.Endpoints
if ef == nil {
ef = defaultEndpointFinder
}
apiEndpoint, access, creds, credsURL, err := getCreds(credHelper, netrcFinder, ef, remote, req)
apiEndpoint, access, credHelper, credsURL, creds, err := c.getCreds(remote, req)
if err != nil {
return nil, err
}
@ -101,32 +79,48 @@ func (c *Client) doWithCreds(req *http.Request, credHelper CredentialHelper, cre
// 3. The Git Remote URL, which should be something like "https://git.com/repo.git"
// This URL is used for the Git Credential Helper. This way existing https
// Git remote credentials can be re-used for LFS.
func getCreds(credHelper CredentialHelper, netrcFinder NetrcFinder, ef EndpointFinder, remote string, req *http.Request) (Endpoint, Access, Creds, *url.URL, error) {
func (c *Client) getCreds(remote string, req *http.Request) (Endpoint, Access, CredentialHelper, *url.URL, Creds, error) {
ef := c.Endpoints
if ef == nil {
ef = defaultEndpointFinder
}
netrcFinder := c.Netrc
if netrcFinder == nil {
netrcFinder = defaultNetrcFinder
}
operation := getReqOperation(req)
apiEndpoint := ef.Endpoint(operation, remote)
access := ef.AccessFor(apiEndpoint.Url)
if access != NTLMAccess {
if requestHasAuth(req) || setAuthFromNetrc(netrcFinder, req) || access == NoneAccess {
return apiEndpoint, access, nil, nil, nil
return apiEndpoint, access, nullCreds, nil, nil, nil
}
credsURL, err := getCredURLForAPI(ef, operation, remote, apiEndpoint, req)
if err != nil {
return apiEndpoint, access, nil, nil, errors.Wrap(err, "creds")
return apiEndpoint, access, nullCreds, nil, nil, errors.Wrap(err, "creds")
}
if credsURL == nil {
return apiEndpoint, access, nil, nil, nil
return apiEndpoint, access, nullCreds, nil, nil, nil
}
creds, err := fillGitCreds(credHelper, ef, req, credsURL)
return apiEndpoint, access, creds, credsURL, err
credHelper, creds, err := c.getGitCreds(ef, req, credsURL)
if err == nil {
tracerx.Printf("Filled credentials for %s", credsURL)
setRequestAuth(req, creds["username"], creds["password"])
}
return apiEndpoint, access, credHelper, credsURL, creds, err
}
// NTLM ONLY
credsURL, err := url.Parse(apiEndpoint.Url)
if err != nil {
return apiEndpoint, access, nil, nil, errors.Wrap(err, "creds")
return apiEndpoint, access, nullCreds, nil, nil, errors.Wrap(err, "creds")
}
if netrcMachine := getAuthFromNetrc(netrcFinder, req); netrcMachine != nil {
@ -138,20 +132,16 @@ func getCreds(credHelper CredentialHelper, netrcFinder NetrcFinder, ef EndpointF
"source": "netrc",
}
return apiEndpoint, access, creds, credsURL, nil
return apiEndpoint, access, nullCreds, credsURL, creds, nil
}
creds, err := getGitCreds(credHelper, ef, req, credsURL)
return apiEndpoint, access, creds, credsURL, err
// NTLM uses creds to create the session
credHelper, creds, err := c.getGitCreds(ef, req, credsURL)
return apiEndpoint, access, credHelper, credsURL, creds, err
}
func getGitCreds(credHelper CredentialHelper, ef EndpointFinder, req *http.Request, u *url.URL) (Creds, error) {
path := strings.TrimPrefix(u.Path, "/")
input := Creds{"protocol": u.Scheme, "host": u.Host, "path": path}
if u.User != nil && u.User.Username() != "" {
input["username"] = u.User.Username()
}
func (c *Client) getGitCreds(ef EndpointFinder, req *http.Request, u *url.URL) (CredentialHelper, Creds, error) {
credHelper, input := c.getCredentialHelper(u)
creds, err := credHelper.Fill(input)
if creds == nil || len(creds) < 1 {
errmsg := fmt.Sprintf("Git credentials for %s not found", u)
@ -163,17 +153,7 @@ func getGitCreds(credHelper CredentialHelper, ef EndpointFinder, req *http.Reque
err = errors.New(errmsg)
}
return creds, err
}
func fillGitCreds(credHelper CredentialHelper, ef EndpointFinder, req *http.Request, u *url.URL) (Creds, error) {
creds, err := getGitCreds(credHelper, ef, req, u)
if err == nil {
tracerx.Printf("Filled credentials for %s", u)
setRequestAuth(req, creds["username"], creds["password"])
}
return creds, err
return credHelper, creds, err
}
func getAuthFromNetrc(netrcFinder NetrcFinder, req *http.Request) *netrc.Machine {

@ -517,8 +517,10 @@ func TestGetCreds(t *testing.T) {
},
}
credHelper := &fakeCredentialFiller{}
netrcFinder := &fakeNetrc{}
client := &Client{
Credentials: &fakeCredentialFiller{},
Netrc: &fakeNetrc{},
}
for desc, test := range tests {
t.Log(desc)
req, err := http.NewRequest(test.Method, test.Href, nil)
@ -531,8 +533,8 @@ func TestGetCreds(t *testing.T) {
req.Header.Set(key, value)
}
ef := NewEndpointFinder(NewContext(nil, nil, test.Config))
endpoint, access, creds, credsURL, err := getCreds(credHelper, netrcFinder, ef, test.Remote, req)
client.Endpoints = NewEndpointFinder(NewContext(nil, nil, test.Config))
endpoint, access, _, credsURL, creds, err := client.getCreds(test.Remote, req)
if !assert.Nil(t, err) {
continue
}

@ -8,86 +8,61 @@ import (
"strings"
"sync"
"github.com/git-lfs/git-lfs/config"
"github.com/git-lfs/git-lfs/errors"
"github.com/rubyist/tracerx"
)
// credsConfig supplies configuration options pertaining to the authorization
// process in package lfsapi.
type credsConfig struct {
// AskPass is a string containing an executable name as well as a
// program arguments.
//
// See: https://git-scm.com/docs/gitcredentials#_requesting_credentials
// for more.
AskPass string `os:"GIT_ASKPASS" git:"core.askpass" os:"SSH_ASKPASS"`
// Helper is a string defining the credential helper that Git should use.
Helper string `git:"credential.helper"`
// Cached is a boolean determining whether or not to enable the
// credential cacher.
Cached bool
// SkipPrompt is a boolean determining whether or not to prompt the user
// for a password.
SkipPrompt bool `os:"GIT_TERMINAL_PROMPT"`
}
// getCredentialHelper parses a 'credsConfig' from the git and OS environments,
// returning the appropriate CredentialHelper to authenticate requests with.
//
// It returns an error if any configuration was invalid, or otherwise
// un-useable.
func getCredentialHelper(osEnv, gitEnv config.Environment) (CredentialHelper, error) {
ccfg, err := getCredentialConfig(osEnv, gitEnv)
if err != nil {
return nil, err
func (c *Client) getCredentialHelper(u *url.URL) (CredentialHelper, Creds) {
path := strings.TrimPrefix(u.Path, "/")
input := Creds{"protocol": u.Scheme, "host": u.Host, "path": path}
if u.User != nil && u.User.Username() != "" {
input["username"] = u.User.Username()
}
if c.Credentials != nil {
return c.Credentials, input
}
askpass, ok := c.osEnv.Get("GIT_ASKPASS")
if !ok {
askpass, ok = c.gitEnv.Get("core.askpass")
}
if !ok {
askpass, ok = c.osEnv.Get("SSH_ASKPASS")
}
helper, _ := c.gitEnv.Get("credential.helper")
cached := c.gitEnv.Bool("lfs.cachecredentials", true)
skipPrompt := c.osEnv.Bool("GIT_TERMINAL_PROMPT", false)
var hs []CredentialHelper
if len(ccfg.Helper) == 0 && len(ccfg.AskPass) > 0 {
if len(helper) == 0 && len(askpass) > 0 {
hs = append(hs, &AskPassCredentialHelper{
Program: ccfg.AskPass,
Program: askpass,
})
}
var h CredentialHelper
h = &commandCredentialHelper{
SkipPrompt: ccfg.SkipPrompt,
SkipPrompt: skipPrompt,
}
if ccfg.Cached {
if cached {
h = withCredentialCache(h)
}
hs = append(hs, h)
switch len(hs) {
case 0:
return nil, nil
return defaultCredentialHelper, input
case 1:
return hs[0], nil
return hs[0], input
}
return CredentialHelpers(hs), nil
}
// getCredentialConfig parses a *credsConfig given the OS and Git
// configurations.
func getCredentialConfig(o, g config.Environment) (*credsConfig, error) {
askpass, ok := o.Get("GIT_ASKPASS")
if !ok {
askpass, ok = g.Get("core.askpass")
}
if !ok {
askpass, ok = o.Get("SSH_ASKPASS")
}
helper, _ := g.Get("credential.helper")
what := &credsConfig{
AskPass: askpass,
Helper: helper,
Cached: g.Bool("lfs.cachecredentials", true),
SkipPrompt: o.Bool("GIT_TERMINAL_PROMPT", false),
}
return what, nil
return CredentialHelpers(hs), input
}
// CredentialHelpers is a []CredentialHelper that iterates through each
@ -332,6 +307,8 @@ func (h *commandCredentialHelper) Reject(creds Creds) error {
}
func (h *commandCredentialHelper) Approve(creds Creds) error {
tracerx.Printf("creds: git credential approve (%q, %q, %q)",
creds["protocol"], creds["host"], creds["path"])
_, err := h.exec("approve", creds)
return err
}
@ -383,3 +360,22 @@ func (h *commandCredentialHelper) exec(subcommand string, input Creds) (Creds, e
return creds, nil
}
type nullCredentialHelper struct{}
var (
nullCredError = errors.New("No credential helper configured")
nullCreds = &nullCredentialHelper{}
)
func (h *nullCredentialHelper) Fill(input Creds) (Creds, error) {
return nil, nullCredError
}
func (h *nullCredentialHelper) Approve(creds Creds) error {
return nil
}
func (h *nullCredentialHelper) Reject(creds Creds) error {
return nil
}