git-lfs/lfsapi/auth.go

199 lines
4.5 KiB
Go
Raw Normal View History

2016-12-19 21:38:06 +00:00
package lfsapi
import (
"encoding/base64"
"fmt"
2016-12-19 21:53:18 +00:00
"net"
2016-12-19 21:38:06 +00:00
"net/http"
"net/url"
"os"
"strings"
"github.com/git-lfs/git-lfs/errors"
"github.com/rubyist/tracerx"
)
var (
defaultCredentialHelper = &CommandCredentialHelper{}
defaultNetrcFinder = &noFinder{}
defaultEndpointFinder = NewEndpointFinder(nil)
)
func (c *Client) getCreds(remote string, req *http.Request) (Creds, error) {
credHelper := c.Credentials
if credHelper == nil {
credHelper = defaultCredentialHelper
}
netrcFinder := c.Netrc
if netrcFinder == nil {
netrcFinder = defaultNetrcFinder
}
ef := c.Endpoints
if ef == nil {
ef = defaultEndpointFinder
}
return getCreds(credHelper, netrcFinder, ef, remote, req)
}
2016-12-19 21:53:18 +00:00
func getCreds(credHelper CredentialHelper, netrcFinder NetrcFinder, ef EndpointFinder, remote string, req *http.Request) (Creds, error) {
2016-12-19 21:38:06 +00:00
if skipCreds(ef, req) {
return nil, nil
}
operation := getReqOperation(req)
apiEndpoint := ef.Endpoint(operation, remote)
credsUrl, err := getCredURLForAPI(ef, operation, remote, apiEndpoint, req)
if err != nil {
return nil, errors.Wrap(err, "creds")
}
if credsUrl == nil {
return nil, nil
}
2016-12-19 21:53:18 +00:00
if setAuthFromNetrc(netrcFinder, req) {
return nil, nil
}
2016-12-19 21:38:06 +00:00
return fillCredentials(credHelper, ef, req, credsUrl)
}
func fillCredentials(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()
}
creds, err := credHelper.Fill(input)
if creds == nil || len(creds) < 1 {
errmsg := fmt.Sprintf("Git credentials for %s not found", u)
if err != nil {
errmsg = errmsg + ":\n" + err.Error()
} else {
errmsg = errmsg + "."
}
err = errors.New(errmsg)
}
if err != nil {
return nil, err
}
tracerx.Printf("Filled credentials for %s", u)
setRequestAuth(req, creds["username"], creds["password"])
return creds, err
}
2016-12-19 21:53:18 +00:00
func setAuthFromNetrc(netrcFinder NetrcFinder, req *http.Request) bool {
hostname := req.URL.Host
var host string
if strings.Contains(hostname, ":") {
var err error
host, _, err = net.SplitHostPort(hostname)
if err != nil {
tracerx.Printf("netrc: error parsing %q: %s", hostname, err)
return false
}
} else {
host = hostname
}
if machine := netrcFinder.FindMachine(host); machine != nil {
setRequestAuth(req, machine.Login, machine.Password)
return true
}
return false
}
2016-12-19 21:38:06 +00:00
func getCredURLForAPI(ef EndpointFinder, operation, remote string, e Endpoint, req *http.Request) (*url.URL, error) {
apiUrl, err := url.Parse(e.Url)
if err != nil {
return nil, err
}
// if the LFS request doesn't match the current LFS url, don't bother
// attempting to set the Authorization header from the LFS or Git remote URLs.
if req.URL.Scheme != apiUrl.Scheme ||
req.URL.Host != apiUrl.Host {
return req.URL, nil
}
if setRequestAuthFromUrl(ef, req, e, apiUrl) {
return nil, nil
}
credsUrl := apiUrl
if len(remote) > 0 {
if u := ef.GitRemoteURL(remote, operation == "upload"); u != "" {
gitRemoteUrl, err := url.Parse(u)
if err != nil {
return nil, err
}
if gitRemoteUrl.Scheme == apiUrl.Scheme &&
gitRemoteUrl.Host == apiUrl.Host {
if setRequestAuthFromUrl(ef, req, e, gitRemoteUrl) {
return nil, nil
}
credsUrl = gitRemoteUrl
}
}
}
return credsUrl, nil
}
func skipCreds(ef EndpointFinder, req *http.Request) bool {
if ef.AccessFor(req.URL.String()) == NTLMAccess {
return false
}
if len(req.Header.Get("Authorization")) > 0 {
return true
}
return len(req.URL.Query().Get("token")) > 0
}
func setRequestAuthFromUrl(ef EndpointFinder, req *http.Request, apiEndpoint Endpoint, u *url.URL) bool {
if ef.AccessFor(apiEndpoint.Url) == NTLMAccess || u.User == nil {
return false
}
if pass, ok := u.User.Password(); ok {
fmt.Fprintln(os.Stderr, "warning: current Git remote contains credentials")
setRequestAuth(req, u.User.Username(), pass)
return true
}
return false
}
func setRequestAuth(req *http.Request, user, pass string) {
// better not be NTLM!
if len(user) == 0 && len(pass) == 0 {
return
}
token := fmt.Sprintf("%s:%s", user, pass)
auth := "Basic " + strings.TrimSpace(base64.StdEncoding.EncodeToString([]byte(token)))
req.Header.Set("Authorization", auth)
}
func getReqOperation(req *http.Request) string {
operation := "download"
if req.Method == "POST" || req.Method == "PUT" {
operation = "upload"
}
return operation
}