Merge pull request #4815 from bk2204/negotiate-fallback

Fall back from Negotiate to Basic
This commit is contained in:
brian m. carlson 2022-01-21 14:08:06 +00:00 committed by GitHub
commit 4a3701b555
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 13 deletions

@ -31,3 +31,12 @@ func (a *Access) Mode() AccessMode {
func (a *Access) URL() string {
return a.url
}
// AllAccessModes returns all access modes in the order they should be tried.
func AllAccessModes() []AccessMode {
return []AccessMode{
NoneAccess,
NegotiateAccess,
BasicAccess,
}
}

@ -68,9 +68,11 @@ func (c *Client) doWithAuth(remote string, access creds.Access, req *http.Reques
res, err := c.doWithCreds(req, credWrapper, access, via)
if err != nil {
if errors.IsAuthError(err) {
newAccess := access.Upgrade(getAuthAccess(res))
newMode, newModes := getAuthAccess(res, access.Mode(), c.access)
newAccess := access.Upgrade(newMode)
if newAccess.Mode() != access.Mode() {
c.Endpoints.SetAccess(newAccess)
c.access = newModes
}
if credWrapper.Creds != nil {
@ -312,20 +314,34 @@ var (
authenticateHeaders = []string{"Lfs-Authenticate", "Www-Authenticate"}
)
func getAuthAccess(res *http.Response) creds.AccessMode {
for _, headerName := range authenticateHeaders {
for _, auth := range res.Header[headerName] {
pieces := strings.SplitN(strings.ToLower(auth), " ", 2)
if len(pieces) == 0 {
continue
}
func getAuthAccess(res *http.Response, access creds.AccessMode, modes []creds.AccessMode) (creds.AccessMode, []creds.AccessMode) {
newModes := make([]creds.AccessMode, 0, len(modes))
for _, mode := range modes {
if access != mode {
newModes = append(newModes, mode)
}
}
if res != nil {
supportedModes := make(map[creds.AccessMode]struct{})
for _, headerName := range authenticateHeaders {
for _, auth := range res.Header[headerName] {
pieces := strings.SplitN(strings.ToLower(auth), " ", 2)
if len(pieces) == 0 {
continue
}
switch creds.AccessMode(pieces[0]) {
case creds.NegotiateAccess:
return creds.AccessMode(pieces[0])
switch creds.AccessMode(pieces[0]) {
case creds.BasicAccess, creds.NegotiateAccess:
supportedModes[creds.AccessMode(pieces[0])] = struct{}{}
}
}
}
for _, mode := range newModes {
if _, ok := supportedModes[mode]; ok {
return mode, newModes
}
}
}
return creds.BasicAccess
return creds.BasicAccess, newModes
}

@ -39,11 +39,23 @@ func TestAuthenticateHeaderAccess(t *testing.T) {
res := &http.Response{Header: make(http.Header)}
res.Header.Set(key, value)
t.Logf("%s: %s", key, value)
assert.Equal(t, expected, getAuthAccess(res))
result, _ := getAuthAccess(res, creds.NoneAccess, creds.AllAccessModes())
assert.Equal(t, expected, result)
}
}
}
func TestDualAccessModes(t *testing.T) {
res := &http.Response{Header: make(http.Header)}
res.Header["Www-Authenticate"] = []string{"Negotiate 123", "Basic 456"}
access, next := getAuthAccess(res, creds.NoneAccess, creds.AllAccessModes())
assert.Equal(t, creds.NegotiateAccess, access)
access, next = getAuthAccess(res, access, next)
assert.Equal(t, creds.BasicAccess, access)
access, _ = getAuthAccess(res, access, next)
assert.Equal(t, creds.BasicAccess, access)
}
func TestDoWithAuthApprove(t *testing.T) {
var called uint32

@ -17,6 +17,7 @@ type Client struct {
client *lfshttp.Client
context lfshttp.Context
access []creds.AccessMode
}
func NewClient(ctx lfshttp.Context) (*Client, error) {
@ -37,6 +38,7 @@ func NewClient(ctx lfshttp.Context) (*Client, error) {
client: httpClient,
context: ctx,
credContext: creds.NewCredentialHelperContext(gitEnv, osEnv),
access: creds.AllAccessModes(),
}
return c, nil

@ -3,6 +3,7 @@ package lfshttp
import (
"context"
"crypto/tls"
goerrors "errors"
"fmt"
"io"
"net"
@ -301,6 +302,11 @@ func (c *Client) DoWithRedirect(cli *http.Client, req *http.Request, remote stri
if err != nil {
c.traceResponse(req, tracedReq, nil)
// SPNEGO (Negotiate) errors are authentication errors.
var spnegoErr *spnego.Error
if goerrors.As(err, &spnegoErr) {
return nil, nil, errors.NewAuthError(err)
}
return nil, nil, err
}